BaseLinearLayoutManagerTest.java revision ec4b5429886bfe93707a85823cf40de107bd9dc6
1999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar/* 2999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * Copyright (C) 2015 The Android Open Source Project 3999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * 4999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * you may not use this file except in compliance with the License. 6999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * You may obtain a copy of the License at 7999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * 8999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * 10999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * Unless required by applicable law or agreed to in writing, software 11999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * See the License for the specific language governing permissions and 14999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * limitations under the License. 15999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar */ 16999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarpackage android.support.v7.widget; 17999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 18e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL; 19e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static android.support.v7.widget.LinearLayoutManager.VERTICAL; 20e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 21e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 22e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas 23e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static org.junit.Assert.assertEquals; 24e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static org.junit.Assert.assertNotNull; 25e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas 26e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static java.util.concurrent.TimeUnit.SECONDS; 27e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas 28999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport android.content.Context; 29999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport android.graphics.Rect; 30c587f7dba5a337169e854e235da59f595255d6ccAga Madurskaimport android.support.v4.util.Pair; 31999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport android.util.Log; 32999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport android.view.View; 334143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport android.view.ViewGroup; 34999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 35e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport org.hamcrest.CoreMatchers; 36e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport org.hamcrest.MatcherAssert; 37e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas 38999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.lang.reflect.Field; 39999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.ArrayList; 40999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.LinkedHashMap; 41999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.List; 42999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.Map; 43999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.concurrent.CountDownLatch; 44999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.concurrent.TimeUnit; 45999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 46999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarpublic class BaseLinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest { 47999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 48999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar protected static final boolean DEBUG = false; 49999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar protected static final String TAG = "LinearLayoutManagerTest"; 50999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 51999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar protected static List<Config> createBaseVariations() { 52999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar List<Config> variations = new ArrayList<>(); 53999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (int orientation : new int[]{VERTICAL, HORIZONTAL}) { 54999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (boolean reverseLayout : new boolean[]{false, true}) { 55999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (boolean stackFromBottom : new boolean[]{false, true}) { 564143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar for (boolean wrap : new boolean[]{false, true}) { 574143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar variations.add( 584143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar new Config(orientation, reverseLayout, stackFromBottom).wrap(wrap)); 594143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 604143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 61999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 62999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 63999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 64999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return variations; 65999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 66999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 67999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar WrappedLinearLayoutManager mLayoutManager; 68999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar TestAdapter mTestAdapter; 69999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 70999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar protected static List<Config> addConfigVariation(List<Config> base, String fieldName, 71999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Object... variations) 72999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException { 73999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar List<Config> newConfigs = new ArrayList<Config>(); 74999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Field field = Config.class.getDeclaredField(fieldName); 75999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (Config config : base) { 76999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (Object variation : variations) { 77999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config newConfig = (Config) config.clone(); 78999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar field.set(newConfig, variation); 79999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar newConfigs.add(newConfig); 80999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 81999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 82999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return newConfigs; 83999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 84999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 85999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable { 86999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView = inflateWrappedRV(); 87999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 88999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView.setHasFixedSize(true); 89999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount) 90999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar : config.mTestAdapter; 91999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView.setAdapter(mTestAdapter); 92999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation, 93999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar config.mReverseLayout); 94999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 95999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 96999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView.setLayoutManager(mLayoutManager); 974143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (config.mWrap) { 984143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mRecyclerView.setLayoutParams( 994143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar new ViewGroup.LayoutParams( 100e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas config.mOrientation == HORIZONTAL ? WRAP_CONTENT : MATCH_PARENT, 101e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas config.mOrientation == VERTICAL ? WRAP_CONTENT : MATCH_PARENT 1024143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ) 1034143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ); 1044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 105999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (waitForFirstLayout) { 106999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar waitForFirstLayout(); 107999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 108999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 109999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 110999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void scrollToPositionWithPredictive(final int scrollPosition, final int scrollOffset) 111999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar throws Throwable { 112999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar setupByConfig(new Config(VERTICAL, false, false), true); 113999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 114999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.mOnLayoutListener = new OnLayoutListener() { 115999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 116999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state) { 117999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (state.isPreLayout()) { 118999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals("pending scroll position should still be pending", 119999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar scrollPosition, mLayoutManager.mPendingScrollPosition); 120999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) { 121999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals("pending scroll position offset should still be pending", 122999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar scrollOffset, mLayoutManager.mPendingScrollPositionOffset); 123999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 124999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } else { 125999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar RecyclerView.ViewHolder vh = 126999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView.findViewHolderForLayoutPosition(scrollPosition); 127999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertNotNull("scroll to position should work", vh); 128999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) { 129999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals("scroll offset should be applied properly", 130999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.getPaddingTop() + scrollOffset + 131999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ((RecyclerView.LayoutParams) vh.itemView 132999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .getLayoutParams()).topMargin, 133999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.getDecoratedTop(vh.itemView)); 134999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 135999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 136999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 137999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }; 138999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.expectLayouts(2); 139999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar runTestOnUiThread(new Runnable() { 140999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 141999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void run() { 142999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar try { 143999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mTestAdapter.addAndNotify(0, 1); 144999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (scrollOffset == LinearLayoutManager.INVALID_OFFSET) { 145999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.scrollToPosition(scrollPosition); 146999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } else { 147999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.scrollToPositionWithOffset(scrollPosition, 148999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar scrollOffset); 149999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 150999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 151999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } catch (Throwable throwable) { 152999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar throwable.printStackTrace(); 153999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 154999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 155999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 156999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }); 157999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.waitForLayout(2); 158999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar checkForMainThreadException(); 159999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 160999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 161999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar protected void waitForFirstLayout() throws Throwable { 162999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.expectLayouts(1); 163999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar setRecyclerView(mRecyclerView); 164999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.waitForLayout(2); 165999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 166999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 167999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar void scrollToPositionWithOffset(final int position, final int offset) throws Throwable { 168999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar runTestOnUiThread(new Runnable() { 169999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 170999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void run() { 171999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.scrollToPositionWithOffset(position, offset); 172999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 173999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }); 174999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 175999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 176999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void assertRectSetsNotEqual(String message, Map<Item, Rect> before, 177999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Map<Item, Rect> after, boolean strictItemEquality) { 178999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Throwable throwable = null; 179999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar try { 180999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertRectSetsEqual("NOT " + message, before, after, strictItemEquality); 181999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } catch (Throwable t) { 182999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar throwable = t; 183999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 184999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertNotNull(message + "\ntwo layout should be different", throwable); 185999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 186999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 187999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) { 188999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertRectSetsEqual(message, before, after, true); 189999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 190999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 191999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after, 192999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar boolean strictItemEquality) { 193999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar StringBuilder sb = new StringBuilder(); 194999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append("checking rectangle equality.\n"); 195999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append("before:\n"); 196999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 197999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 198999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 199999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append("after:\n"); 200999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (Map.Entry<Item, Rect> entry : after.entrySet()) { 201999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 202999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 203999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar message = message + "\n" + sb.toString(); 204999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals(message + ":\nitem counts should be equal", before.size() 205999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar , after.size()); 206999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 207999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final Item beforeItem = entry.getKey(); 208999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Rect afterRect = null; 209999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (strictItemEquality) { 210999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar afterRect = after.get(beforeItem); 211999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertNotNull(message + ":\nSame item should be visible after simple re-layout", 212999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar afterRect); 213999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } else { 214999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (Map.Entry<Item, Rect> afterEntry : after.entrySet()) { 215999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final Item afterItem = afterEntry.getKey(); 216999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (afterItem.mAdapterIndex == beforeItem.mAdapterIndex) { 217999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar afterRect = afterEntry.getValue(); 218999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar break; 219999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 220999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 221999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertNotNull(message + ":\nItem with same adapter index should be visible " + 222999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar "after simple re-layout", 223999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar afterRect); 224999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 225999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals(message + ":\nItem should be laid out at the same coordinates", 226999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar entry.getValue(), afterRect); 227999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 228999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 229999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 230999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar static class VisibleChildren { 231999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 232999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int firstVisiblePosition = RecyclerView.NO_POSITION; 233999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 234999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int firstFullyVisiblePosition = RecyclerView.NO_POSITION; 235999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 236999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int lastVisiblePosition = RecyclerView.NO_POSITION; 237999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 238999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int lastFullyVisiblePosition = RecyclerView.NO_POSITION; 239999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 240999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 241999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public String toString() { 242999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return "VisibleChildren{" + 243999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar "firstVisiblePosition=" + firstVisiblePosition + 244999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", firstFullyVisiblePosition=" + firstFullyVisiblePosition + 245999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", lastVisiblePosition=" + lastVisiblePosition + 246999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", lastFullyVisiblePosition=" + lastFullyVisiblePosition + 247999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar '}'; 248999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 249999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 250999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 251999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar static class OnLayoutListener { 252999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 253999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar void before(RecyclerView.Recycler recycler, RecyclerView.State state) { 254999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 255999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 256999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state) { 257999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 258999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 259999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 260999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar static class Config implements Cloneable { 261999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 262c587f7dba5a337169e854e235da59f595255d6ccAga Madurska static final int DEFAULT_ITEM_COUNT = 250; 263999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 264999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar boolean mStackFromEnd; 265999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 266999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int mOrientation = VERTICAL; 267999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 268999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar boolean mReverseLayout = false; 269999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 270999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar boolean mRecycleChildrenOnDetach = false; 271999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 272999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int mItemCount = DEFAULT_ITEM_COUNT; 273999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 2744143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar boolean mWrap = false; 2754143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 276999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar TestAdapter mTestAdapter; 277999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 278999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config(int orientation, boolean reverseLayout, boolean stackFromEnd) { 279999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientation = orientation; 280999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mReverseLayout = reverseLayout; 281999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mStackFromEnd = stackFromEnd; 282999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 283999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 284999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public Config() { 285999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 286999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 287999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 288999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config adapter(TestAdapter adapter) { 289999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mTestAdapter = adapter; 290999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return this; 291999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 292999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 293999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) { 294999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecycleChildrenOnDetach = recycleChildrenOnDetach; 295999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return this; 296999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 297999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 298999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config orientation(int orientation) { 299999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientation = orientation; 300999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return this; 301999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 302999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 303999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config stackFromBottom(boolean stackFromBottom) { 304999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mStackFromEnd = stackFromBottom; 305999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return this; 306999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 307999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 308999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config reverseLayout(boolean reverseLayout) { 309999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mReverseLayout = reverseLayout; 310999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return this; 311999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 312999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 313999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public Config itemCount(int itemCount) { 314999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mItemCount = itemCount; 315999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return this; 316999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 317999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 318999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // required by convention 319999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 320999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public Object clone() throws CloneNotSupportedException { 321999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return super.clone(); 322999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 323999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 324999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 325999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public String toString() { 326999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return "Config{" + 327999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar "mStackFromEnd=" + mStackFromEnd + 328999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", mOrientation=" + mOrientation + 329999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", mReverseLayout=" + mReverseLayout + 330999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach + 331999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", mItemCount=" + mItemCount + 3324143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ", wrap=" + mWrap + 333999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar '}'; 334999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 3354143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 3364143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar public Config wrap(boolean wrap) { 3374143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mWrap = wrap; 3384143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return this; 3394143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 340999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 341999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 342999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar class WrappedLinearLayoutManager extends LinearLayoutManager { 343999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 344999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar CountDownLatch layoutLatch; 345999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 346c587f7dba5a337169e854e235da59f595255d6ccAga Madurska CountDownLatch snapLatch; 347c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 348ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik CountDownLatch prefetchLatch; 349ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik 350999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar OrientationHelper mSecondaryOrientation; 351999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 352999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar OnLayoutListener mOnLayoutListener; 353999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 354999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 355999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super(context, orientation, reverseLayout); 356999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 357999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 358999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void expectLayouts(int count) { 359999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar layoutLatch = new CountDownLatch(count); 360999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 361999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 36266826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar public void waitForLayout(int seconds) throws Throwable { 36366826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar layoutLatch.await(seconds * (DEBUG ? 100 : 1), SECONDS); 36466826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar checkForMainThreadException(); 36566826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar MatcherAssert.assertThat("all layouts should complete on time", 36666826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar layoutLatch.getCount(), CoreMatchers.is(0L)); 36766826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar // use a runnable to ensure RV layout is finished 36866826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar getInstrumentation().runOnMainSync(new Runnable() { 36966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar @Override 37066826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar public void run() { 37166826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar } 37266826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar }); 373999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 374999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 375ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik public void expectPrefetch(int count) { 376ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik prefetchLatch = new CountDownLatch(count); 377ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik } 378ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik 379ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik public void waitForPrefetch(int seconds) throws Throwable { 380ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik prefetchLatch.await(seconds * (DEBUG ? 100 : 1), SECONDS); 381ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik checkForMainThreadException(); 382ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik MatcherAssert.assertThat("all prefetches should complete on time", 383ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik prefetchLatch.getCount(), CoreMatchers.is(0L)); 384ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik // use a runnable to ensure RV layout is finished 385ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik getInstrumentation().runOnMainSync(new Runnable() { 386ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik @Override 387ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik public void run() { 388ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik } 389ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik }); 390ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik } 391ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik 392c587f7dba5a337169e854e235da59f595255d6ccAga Madurska public void expectIdleState(int count) { 393c587f7dba5a337169e854e235da59f595255d6ccAga Madurska snapLatch = new CountDownLatch(count); 394c587f7dba5a337169e854e235da59f595255d6ccAga Madurska mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { 395c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @Override 396c587f7dba5a337169e854e235da59f595255d6ccAga Madurska public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 397c587f7dba5a337169e854e235da59f595255d6ccAga Madurska super.onScrollStateChanged(recyclerView, newState); 398c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (newState == RecyclerView.SCROLL_STATE_IDLE) { 399c587f7dba5a337169e854e235da59f595255d6ccAga Madurska snapLatch.countDown(); 400c587f7dba5a337169e854e235da59f595255d6ccAga Madurska if (snapLatch.getCount() == 0L) { 401c587f7dba5a337169e854e235da59f595255d6ccAga Madurska mRecyclerView.removeOnScrollListener(this); 402c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 403c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 404c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 405c587f7dba5a337169e854e235da59f595255d6ccAga Madurska }); 406c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 407c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 408c587f7dba5a337169e854e235da59f595255d6ccAga Madurska public void waitForSnap(int seconds) throws Throwable { 409c587f7dba5a337169e854e235da59f595255d6ccAga Madurska snapLatch.await(seconds * (DEBUG ? 100 : 1), SECONDS); 410c587f7dba5a337169e854e235da59f595255d6ccAga Madurska checkForMainThreadException(); 411c587f7dba5a337169e854e235da59f595255d6ccAga Madurska MatcherAssert.assertThat("all scrolling should complete on time", 412c587f7dba5a337169e854e235da59f595255d6ccAga Madurska snapLatch.getCount(), CoreMatchers.is(0L)); 413c587f7dba5a337169e854e235da59f595255d6ccAga Madurska // use a runnable to ensure RV layout is finished 414c587f7dba5a337169e854e235da59f595255d6ccAga Madurska getInstrumentation().runOnMainSync(new Runnable() { 415c587f7dba5a337169e854e235da59f595255d6ccAga Madurska @Override 416c587f7dba5a337169e854e235da59f595255d6ccAga Madurska public void run() { 417c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 418c587f7dba5a337169e854e235da59f595255d6ccAga Madurska }); 419c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 420c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 421999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 422999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void setOrientation(int orientation) { 423999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super.setOrientation(orientation); 424999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mSecondaryOrientation = null; 425999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 426999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 427999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 428999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) { 429999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (DEBUG) { 430999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child)); 431999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 432999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super.removeAndRecycleView(child, recycler); 433999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 434999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 435999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 436999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) { 437999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (DEBUG) { 438999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Log.d(TAG, 439999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index))); 440999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 441999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super.removeAndRecycleViewAt(index, recycler); 442999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 443999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 444999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 445999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar void ensureLayoutState() { 446999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super.ensureLayoutState(); 447999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (mSecondaryOrientation == null) { 448999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mSecondaryOrientation = OrientationHelper.createOrientationHelper(this, 449999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 1 - getOrientation()); 450999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 451999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 452999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 453999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 454999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar LayoutState createLayoutState() { 455999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return new LayoutState() { 456999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 457999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar View next(RecyclerView.Recycler recycler) { 458999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final boolean hadMore = hasMore(mRecyclerView.mState); 459999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int position = mCurrentPosition; 460999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar View next = super.next(recycler); 461999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals("if has more, should return a view", hadMore, next != null); 462999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals("position of the returned view must match current position", 463999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar position, RecyclerView.getChildViewHolderInt(next).getLayoutPosition()); 464999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return next; 465999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 466999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }; 467999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 468999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 469999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public String getBoundsLog() { 470999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar StringBuilder sb = new StringBuilder(); 471999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding()) 472999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding()); 473999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append("\nchildren bounds\n"); 474999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int childCount = getChildCount(); 475999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (int i = 0; i < childCount; i++) { 476999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar View child = getChildAt(i); 477999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child)) 478999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .append("[").append("start:").append( 479999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientationHelper.getDecoratedStart(child)).append(", end:") 480999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n"); 481999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 482999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return sb.toString(); 483999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 484999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 485999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void waitForAnimationsToEnd(int timeoutInSeconds) throws InterruptedException { 486999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator(); 487999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (itemAnimator == null) { 488999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return; 489999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 490999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final CountDownLatch latch = new CountDownLatch(1); 491999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final boolean running = itemAnimator.isRunning( 492999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 493999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 494999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void onAnimationsFinished() { 495999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar latch.countDown(); 496999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 497999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 498999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ); 499999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (running) { 500999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar latch.await(timeoutInSeconds, TimeUnit.SECONDS); 501999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 502999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 503999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 504999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public VisibleChildren traverseAndFindVisibleChildren() { 505999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int childCount = getChildCount(); 506999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final VisibleChildren visibleChildren = new VisibleChildren(); 507999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int start = mOrientationHelper.getStartAfterPadding(); 508999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int end = mOrientationHelper.getEndAfterPadding(); 509999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (int i = 0; i < childCount; i++) { 510999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar View child = getChildAt(i); 511999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int childStart = mOrientationHelper.getDecoratedStart(child); 512999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int childEnd = mOrientationHelper.getDecoratedEnd(child); 513999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final boolean fullyVisible = childStart >= start && childEnd <= end; 514999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final boolean hidden = childEnd <= start || childStart >= end; 515999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (hidden) { 516999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar continue; 517999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 518999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int position = getPosition(child); 519999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (fullyVisible) { 520999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (position < visibleChildren.firstFullyVisiblePosition || 521999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) { 522999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar visibleChildren.firstFullyVisiblePosition = position; 523999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 524999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 525999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (position > visibleChildren.lastFullyVisiblePosition) { 526999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar visibleChildren.lastFullyVisiblePosition = position; 527999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 528999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 529999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 530999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (position < visibleChildren.firstVisiblePosition || 531999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) { 532999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar visibleChildren.firstVisiblePosition = position; 533999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 534999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 535999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (position > visibleChildren.lastVisiblePosition) { 536999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar visibleChildren.lastVisiblePosition = position; 537999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 538999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 539999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 540999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return visibleChildren; 541999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 542999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 543999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Rect getViewBounds(View view) { 544999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (getOrientation() == HORIZONTAL) { 545999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return new Rect( 546999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientationHelper.getDecoratedStart(view), 547999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mSecondaryOrientation.getDecoratedStart(view), 548999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientationHelper.getDecoratedEnd(view), 549999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mSecondaryOrientation.getDecoratedEnd(view)); 550999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } else { 551999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return new Rect( 552999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mSecondaryOrientation.getDecoratedStart(view), 553999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientationHelper.getDecoratedStart(view), 554999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mSecondaryOrientation.getDecoratedEnd(view), 555999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientationHelper.getDecoratedEnd(view)); 556999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 557999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 558999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 559999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 560999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Map<Item, Rect> collectChildCoordinates() throws Throwable { 561999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>(); 562999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar runTestOnUiThread(new Runnable() { 563999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 564999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void run() { 565999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int childCount = getChildCount(); 566999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Rect layoutBounds = new Rect(0, 0, 567999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.getWidth(), mLayoutManager.getHeight()); 568999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (int i = 0; i < childCount; i++) { 569999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar View child = getChildAt(i); 570999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child 571999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .getLayoutParams(); 572999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar TestViewHolder vh = (TestViewHolder) lp.mViewHolder; 573999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Rect childBounds = getViewBounds(child); 574999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (new Rect(childBounds).intersect(layoutBounds)) { 575999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar items.put(vh.mBoundItem, childBounds); 576999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 577999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 578999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 579999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }); 580999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return items; 581999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 582999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 583999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 584999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 585999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar try { 586999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (mOnLayoutListener != null) { 587999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOnLayoutListener.before(recycler, state); 588999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 589999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super.onLayoutChildren(recycler, state); 590999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (mOnLayoutListener != null) { 591999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOnLayoutListener.after(recycler, state); 592999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 593999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } catch (Throwable t) { 594999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar postExceptionToInstrumentation(t); 595999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 596999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar layoutLatch.countDown(); 597999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 598ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik 599ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik @Override 600ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik int gatherPrefetchIndices(int dx, int dy, RecyclerView.State state, int[] outIndices) { 601ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik if (prefetchLatch != null) prefetchLatch.countDown(); 602ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik return super.gatherPrefetchIndices(dx, dy, state, outIndices); 603ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik } 604999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 605999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar} 606