LinearLayoutManagerTest.java revision 8edcb0bdeaba6931f9d8154f0c81f57da7ddab2a
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; 238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.util.Log; 24d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.view.View; 25d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.lang.reflect.Field; 27d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList; 288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.LinkedHashMap; 29d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List; 308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.Map; 318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.UUID; 32d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.CountDownLatch; 33d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.TimeUnit; 34d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/** 368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}. 378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p> 388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation 398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling 408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing. 418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */ 42d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpublic class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest { 43d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final boolean DEBUG = false; 458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final String TAG = "LinearLayoutManagerTest"; 478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 48d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar WrappedLinearLayoutManager mLayoutManager; 49d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 50d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar TestAdapter mTestAdapter; 51d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 52d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final List<Config> mBaseVariations = new ArrayList<Config>(); 53d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 54d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 55d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar protected void setUp() throws Exception { 56d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.setUp(); 57d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int orientation : new int[]{LinearLayoutManager.VERTICAL, 58d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar LinearLayoutManager.HORIZONTAL}) { 59d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean reverseLayout : new boolean[]{false, true}) { 60d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean stackFromBottom : new boolean[]{false, true}) { 61d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom)); 62d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 63d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 64d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 65d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 66d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar protected List<Config> addConfigVariation(List<Config> base, String fieldName, 688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Object... variations) 698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException { 708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar List<Config> newConfigs = new ArrayList<Config>(); 718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Field field = Config.class.getDeclaredField(fieldName); 728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Config config : base) { 738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Object variation : variations) { 748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Config newConfig = (Config) config.clone(); 758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar field.set(newConfig, variation); 768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar newConfigs.add(newConfig); 778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return newConfigs; 808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable { 838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView = new RecyclerView(getActivity()); 848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setHasFixedSize(true); 85d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mTestAdapter = new TestAdapter(config.mItemCount); 868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setAdapter(mTestAdapter); 87d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation, 88d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config.mReverseLayout); 89d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setLayoutManager(mLayoutManager); 918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForFirstLayout) { 928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private void waitForFirstLayout() throws Throwable { 97d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(mRecyclerView); 99d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 100d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 101d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 102d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 103d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void testGetFirstLastChildrenTest() throws Throwable { 104d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (Config config : mBaseVariations) { 105d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar getFirstLastChildrenTest(config); 106d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 107d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 108d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 109d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void getFirstLastChildrenTest(final Config config) throws Throwable { 1108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, true); 111d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Runnable viewInBoundsTest = new Runnable() { 112d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 113d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 114d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren(); 115d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final String boundsLog = mLayoutManager.getBoundsLog(); 116d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nfirst visible child should match traversal result\n" 117d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstVisiblePosition, 118d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstVisibleItemPosition() 119d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 120d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 121d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nfirst fully visible child should match traversal result\n" 122d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstFullyVisiblePosition, 123d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstCompletelyVisibleItemPosition() 124d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 125d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 126d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nlast visible child should match traversal result\n" 127d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastVisiblePosition, 128d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastVisibleItemPosition() 129d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 130d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 131d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nlast fully visible child should match traversal result\n" 132d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastFullyVisiblePosition, 133d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 134d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 135d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 136d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 137d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 138d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching 139d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // case 140d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount(); 141d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 142d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 143d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 144d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.smoothScrollToPosition(scrollPosition); 145d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 146d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 147d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar while (mLayoutManager.isSmoothScrolling() || 148d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 149d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 150d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Thread.sleep(200); 151d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 152d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // delete all items 153d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(2); 154d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount()); 155d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 156d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // test empty case 157d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 158d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // set a new adapter with huge items to test full bounds check 159d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 160d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace(); 161d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final TestAdapter newAdapter = new TestAdapter(100) { 162d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 163d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 164d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int position) { 165d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.onBindViewHolder(holder, position); 166d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (config.mOrientation == LinearLayoutManager.HORIZONTAL) { 167d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumWidth(totalSpace + 5); 168d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } else { 169d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumHeight(totalSpace + 5); 170d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 171d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 172d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 173d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 174d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 175d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 176d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.setAdapter(newAdapter); 177d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 178d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 179d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 180d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 181d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 182d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void testSavedState() throws Throwable { 1848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{ 1858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 1868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 1878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 1888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // do nothing 1898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 1928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 1938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "doing nothing"; 1948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 1968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 1978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 1988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 1998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 2008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPosition(mTestAdapter.getItemCount() * 3 / 4); 2018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 2028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position"; 2078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 2108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 2128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 2138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3, 2148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 50); 2158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 2168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with positive offset"; 2218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 2248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 2268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 2278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3, 2288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar -50); 2298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 2308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with negative offset"; 2358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 2388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{ 2408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 2418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Doing nothing"; 2448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 2478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 2498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // update config as well so that restore assertions will work 2508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation = 1 - config.mOrientation; 2518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setOrientation(config.mOrientation); 2528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 2568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 2578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing orientation"; 2628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 2658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 2678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd = !config.mStackFromEnd; 2688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 2698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 2738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; //stack from end should not move items on change 2748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing stack from end"; 2798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 2828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 2848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout = !config.mReverseLayout; 2858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setReverseLayout(config.mReverseLayout); 2868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 2908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 2918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing reverse layout"; 2968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 2998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean[] waitForLayoutOptions = new boolean[]{false, true}; 3008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Config config : addConfigVariation(mBaseVariations, "mItemCount", 0, 300)) { 3018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) { 3028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (boolean waitForLayout : waitForLayoutOptions) { 3038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) { 3048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedStateTest((Config) config.clone(), waitForLayout, postLayoutRunnable, 3058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreRunnable); 3068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 3078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void savedStateTest(Config config, boolean waitForLayout, 3158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation) 3168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 3178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (DEBUG) { 3188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " + 3198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config + " post layout action " + postLayoutOperation.describe() + 3208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "post restore action " + postRestoreOperation.describe()); 3218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, false); 3238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 3248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 3258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postLayoutOperation.run(); 3268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 3288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcelable savedState = mRecyclerView.onSaveInstanceState(); 3298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // we append a suffix to the parcelable to test out of bounds 3308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar String parcelSuffix = UUID.randomUUID().toString(); 3318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcel parcel = Parcel.obtain(); 3328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState.writeToParcel(parcel, 0); 3338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.writeString(parcelSuffix); 3348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 3358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // reset for reading 3368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.setDataPosition(0); 3378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // re-create 3388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 3398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 3408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView restored = new RecyclerView(getActivity()); 3428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // this config should be no op. 3438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), 3448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1 - config.mOrientation, !config.mReverseLayout); 3458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(!config.mStackFromEnd); 3468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setLayoutManager(mLayoutManager); 3478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // use the same adapter for Rect matching 3488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setAdapter(mTestAdapter); 3498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.onRestoreInstanceState(savedState); 3508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreOperation.onAfterRestore(config); 3518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals("Parcel reading should not go out of bounds", parcelSuffix, 3528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.readString()); 3538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 3548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(restored); 3558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 3568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // calculate prefix here instead of above to include post restore changes 3578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() + 3588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "\npostRestore:" + postRestoreOperation.describe() + "\n"; 3598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, reverse layout should be preserved", 3608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout, mLayoutManager.getReverseLayout()); 3618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, orientation should be preserved", 3628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation, mLayoutManager.getOrientation()); 3638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, stack from end should be preserved", 3648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd, mLayoutManager.getStackFromEnd()); 3658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 3668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (postRestoreOperation.shouldLayoutMatch(config)) { 3678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual( 3688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix + ": on restore, previous view positions should be preserved", 3698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 3708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 3718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsNotEqual( 3728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix 3738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar + ": on restore with changes, previous view positions should NOT be preserved", 3748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 3758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPositionWithOffset(final int position, final int offset) throws Throwable { 3808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 3818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 3838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.scrollToPositionWithOffset(position, offset); 3848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 3868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsNotEqual(String message, Map<Item, Rect> before, 3898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> after) { 3908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Throwable throwable = null; 3918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar try { 3928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual("NOT " + message, before, after); 3938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } catch (Throwable t) { 3948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throwable = t; 3958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + "\ntwo layout should be different", throwable); 3978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) { 4008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (DEBUG) { 4018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "checking rectangle equality."); 4028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "before:"); 4038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 4048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, entry.getKey().originalIndex + ":" + entry.getValue()); 4058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "after:"); 4078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Map.Entry<Item, Rect> entry : after.entrySet()) { 4088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, entry.getKey().originalIndex + ":" + entry.getValue()); 4098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nitem counts should be equal", before.size() 4128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar , after.size()); 4138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 4148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect afterRect = after.get(entry.getKey()); 4158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + ":\nSame item should be visible after simple re-layout", 4168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar afterRect); 4178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nItem should be laid out at the same coordinates", 4188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar entry.getValue(), afterRect); 4198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 422d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar static class VisibleChildren { 423d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 424d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstVisiblePosition = RecyclerView.NO_POSITION; 425d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 426d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstFullyVisiblePosition = RecyclerView.NO_POSITION; 427d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 428d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastVisiblePosition = RecyclerView.NO_POSITION; 429d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 430d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastFullyVisiblePosition = RecyclerView.NO_POSITION; 431d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 432d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 433d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 434d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "VisibleChildren{" + 435d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "firstVisiblePosition=" + firstVisiblePosition + 436d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", firstFullyVisiblePosition=" + firstFullyVisiblePosition + 437d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastVisiblePosition=" + lastVisiblePosition + 438d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastFullyVisiblePosition=" + lastFullyVisiblePosition + 439d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 440d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 441d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 442d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 4438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostLayoutRunnable { 4448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract void run() throws Throwable; 4468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 4488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostRestoreRunnable { 4518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 4538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 4568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 4588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; 4598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar class WrappedLinearLayoutManager extends LinearLayoutManager { 463d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 464d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar CountDownLatch layoutLatch; 465d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 4668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar OrientationHelper mSecondaryOrientation; 4678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 468d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 469d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super(context, orientation, reverseLayout); 470d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 471d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 472d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void expectLayouts(int count) { 473d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch = new CountDownLatch(count); 474d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 475d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 476d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void waitForLayout(long timeout) throws InterruptedException { 477d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar waitForLayout(timeout, TimeUnit.SECONDS); 478d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 479d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 4808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void setOrientation(int orientation) { 4828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar super.setOrientation(orientation); 4838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation = null; 4848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void ensureRenderState() { 4888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar super.ensureRenderState(); 4898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mSecondaryOrientation == null) { 4908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation = getOrientation() == HORIZONTAL 4918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar ? createVerticalOrientationHelper() 4928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar : createHorizontalOrientationHelper(); 4938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 496d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException { 497d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch.await(timeout, timeUnit); 498d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals("all expected layouts should be executed at the expected time", 499d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 0, layoutLatch.getCount()); 500d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 501d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 502d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String getBoundsLog() { 503d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar StringBuilder sb = new StringBuilder(); 504d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding()) 505d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding()); 506d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("\nchildren bounds\n"); 507d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childCount = getChildCount(); 508d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 509d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 510d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child)) 511d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append("[").append("start:").append( 512d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientationHelper.getDecoratedStart(child)).append(", end:") 513d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n"); 514d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 515d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return sb.toString(); 516d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 517d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 518d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public VisibleChildren traverseAndFindVisibleChildren() { 519d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int childCount = getChildCount(); 520d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final VisibleChildren visibleChildren = new VisibleChildren(); 521d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int start = mOrientationHelper.getStartAfterPadding(); 522d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int end = mOrientationHelper.getEndAfterPadding(); 523d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 524d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 525d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childStart = mOrientationHelper.getDecoratedStart(child); 526d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childEnd = mOrientationHelper.getDecoratedEnd(child); 527d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean fullyVisible = childStart >= start && childEnd <= end; 528d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean hidden = childEnd <= start || childStart >= end; 529d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (hidden) { 530d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar continue; 531d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 532d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int position = getPosition(child); 533d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (fullyVisible) { 534d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstFullyVisiblePosition || 535d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) { 536d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition = position; 537d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 538d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 539d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastFullyVisiblePosition) { 540d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastFullyVisiblePosition = position; 541d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 542d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 543d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 544d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstVisiblePosition || 545d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) { 546d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition = position; 547d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 548d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 549d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastVisiblePosition) { 550d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastVisiblePosition = position; 551d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 552d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 553d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 554d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return visibleChildren; 555d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 556d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 5578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect getViewBounds(View view) { 5588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (getOrientation() == HORIZONTAL) { 5598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 5608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 5618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 5628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view), 5638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view)); 5648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 5658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 5668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 5678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 5688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view), 5698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view)); 5708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> collectChildCoordinates() throws Throwable { 5758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>(); 5768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 5778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 5788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 5798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final int childCount = getChildCount(); 5808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (int i = 0; i < childCount; i++) { 5818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar View child = getChildAt(i); 5828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child 5838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar .getLayoutParams(); 5848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar TestViewHolder vh = (TestViewHolder) lp.mViewHolder; 5858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar items.put(vh.mBindedItem, getViewBounds(child)); 5868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 5898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return items; 5908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 592d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 593d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 594d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.onLayoutChildren(recycler, state); 595d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch.countDown(); 596d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 597d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 598d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 5998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar static class Config implements Cloneable { 600d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 601d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private static final int DEFAULT_ITEM_COUNT = 300; 602d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 603d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private boolean mStackFromEnd; 604d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 605d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mOrientation = LinearLayoutManager.VERTICAL; 606d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 607d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar boolean mReverseLayout = false; 608d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 609d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mItemCount = DEFAULT_ITEM_COUNT; 610d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 611d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config(int orientation, boolean reverseLayout, boolean stackFromEnd) { 612d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 613d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 614d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromEnd; 615d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 616d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 617d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config() { 618d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 619d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 620d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 621d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config orientation(int orientation) { 622d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 623d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 624d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 625d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 626d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config stackFromBottom(boolean stackFromBottom) { 627d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromBottom; 628d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 629d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 630d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 631d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config reverseLayout(boolean reverseLayout) { 632d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 633d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 634d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 635d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 636d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config itemCount(int itemCount) { 637d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mItemCount = itemCount; 638d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 639d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 640d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 6418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // required by convention 6428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 6438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public Object clone() throws CloneNotSupportedException { 6448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return super.clone(); 6458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 647d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 648d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 649d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "Config{" + 650d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "mStackFromEnd=" + mStackFromEnd + 651d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mOrientation=" + mOrientation + 652d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mReverseLayout=" + mReverseLayout + 653d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mItemCount=" + mItemCount + 654d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 655d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 656d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 657d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar} 658