BaseLinearLayoutManagerTest.java revision 66826566020afc8d11f183cf3fe443ac0a022384
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 18999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport android.content.Context; 19999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport android.graphics.Rect; 20999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport android.util.Log; 21999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport android.view.View; 224143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport android.view.ViewGroup; 23999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 24999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.lang.reflect.Field; 25999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.ArrayList; 26999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.LinkedHashMap; 27999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.List; 28999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.Map; 29999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.concurrent.CountDownLatch; 30999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.concurrent.TimeUnit; 31999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 32999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL; 33999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL; 344143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport static android.view.ViewGroup.LayoutParams.FILL_PARENT; 354143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.*; 37999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 3866826566020afc8d11f183cf3fe443ac0a022384Yigit Boyarimport static java.util.concurrent.TimeUnit.SECONDS; 3966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar 4066826566020afc8d11f183cf3fe443ac0a022384Yigit Boyarimport org.hamcrest.CoreMatchers; 4166826566020afc8d11f183cf3fe443ac0a022384Yigit Boyarimport org.hamcrest.MatcherAssert; 4266826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar 43999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarpublic class BaseLinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest { 44999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 45999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar protected static final boolean DEBUG = false; 46999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar protected static final String TAG = "LinearLayoutManagerTest"; 47999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 48999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar protected static List<Config> createBaseVariations() { 49999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar List<Config> variations = new ArrayList<>(); 50999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (int orientation : new int[]{VERTICAL, HORIZONTAL}) { 51999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (boolean reverseLayout : new boolean[]{false, true}) { 52999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (boolean stackFromBottom : new boolean[]{false, true}) { 534143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar for (boolean wrap : new boolean[]{false, true}) { 544143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar variations.add( 554143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar new Config(orientation, reverseLayout, stackFromBottom).wrap(wrap)); 564143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 574143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 58999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 59999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 60999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 61999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return variations; 62999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 63999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 64999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar WrappedLinearLayoutManager mLayoutManager; 65999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar TestAdapter mTestAdapter; 66999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 67999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar protected static List<Config> addConfigVariation(List<Config> base, String fieldName, 68999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Object... variations) 69999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException { 70999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar List<Config> newConfigs = new ArrayList<Config>(); 71999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Field field = Config.class.getDeclaredField(fieldName); 72999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (Config config : base) { 73999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (Object variation : variations) { 74999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config newConfig = (Config) config.clone(); 75999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar field.set(newConfig, variation); 76999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar newConfigs.add(newConfig); 77999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 78999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 79999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return newConfigs; 80999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 81999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 82999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable { 83999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView = inflateWrappedRV(); 84999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 85999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView.setHasFixedSize(true); 86999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount) 87999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar : config.mTestAdapter; 88999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView.setAdapter(mTestAdapter); 89999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation, 90999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar config.mReverseLayout); 91999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 92999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 93999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView.setLayoutManager(mLayoutManager); 944143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (config.mWrap) { 954143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mRecyclerView.setLayoutParams( 964143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar new ViewGroup.LayoutParams( 974143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar config.mOrientation == HORIZONTAL ? WRAP_CONTENT : FILL_PARENT, 984143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar config.mOrientation == VERTICAL ? WRAP_CONTENT : FILL_PARENT 994143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ) 1004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ); 1014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 102999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (waitForFirstLayout) { 103999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar waitForFirstLayout(); 104999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 105999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 106999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 107999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void scrollToPositionWithPredictive(final int scrollPosition, final int scrollOffset) 108999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar throws Throwable { 109999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar setupByConfig(new Config(VERTICAL, false, false), true); 110999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 111999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.mOnLayoutListener = new OnLayoutListener() { 112999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 113999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state) { 114999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (state.isPreLayout()) { 115999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals("pending scroll position should still be pending", 116999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar scrollPosition, mLayoutManager.mPendingScrollPosition); 117999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) { 118999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals("pending scroll position offset should still be pending", 119999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar scrollOffset, mLayoutManager.mPendingScrollPositionOffset); 120999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 121999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } else { 122999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar RecyclerView.ViewHolder vh = 123999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView.findViewHolderForLayoutPosition(scrollPosition); 124999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertNotNull("scroll to position should work", vh); 125999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) { 126999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals("scroll offset should be applied properly", 127999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.getPaddingTop() + scrollOffset + 128999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ((RecyclerView.LayoutParams) vh.itemView 129999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .getLayoutParams()).topMargin, 130999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.getDecoratedTop(vh.itemView)); 131999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 132999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 133999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 134999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }; 135999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.expectLayouts(2); 136999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar runTestOnUiThread(new Runnable() { 137999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 138999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void run() { 139999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar try { 140999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mTestAdapter.addAndNotify(0, 1); 141999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (scrollOffset == LinearLayoutManager.INVALID_OFFSET) { 142999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.scrollToPosition(scrollPosition); 143999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } else { 144999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.scrollToPositionWithOffset(scrollPosition, 145999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar scrollOffset); 146999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 147999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 148999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } catch (Throwable throwable) { 149999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar throwable.printStackTrace(); 150999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 151999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 152999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 153999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }); 154999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.waitForLayout(2); 155999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar checkForMainThreadException(); 156999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 157999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 158999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar protected void waitForFirstLayout() throws Throwable { 159999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.expectLayouts(1); 160999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar setRecyclerView(mRecyclerView); 161999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.waitForLayout(2); 162999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 163999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 164999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar void scrollToPositionWithOffset(final int position, final int offset) throws Throwable { 165999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar runTestOnUiThread(new Runnable() { 166999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 167999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void run() { 168999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.scrollToPositionWithOffset(position, offset); 169999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 170999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }); 171999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 172999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 173999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void assertRectSetsNotEqual(String message, Map<Item, Rect> before, 174999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Map<Item, Rect> after, boolean strictItemEquality) { 175999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Throwable throwable = null; 176999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar try { 177999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertRectSetsEqual("NOT " + message, before, after, strictItemEquality); 178999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } catch (Throwable t) { 179999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar throwable = t; 180999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 181999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertNotNull(message + "\ntwo layout should be different", throwable); 182999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 183999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 184999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) { 185999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertRectSetsEqual(message, before, after, true); 186999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 187999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 188999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after, 189999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar boolean strictItemEquality) { 190999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar StringBuilder sb = new StringBuilder(); 191999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append("checking rectangle equality.\n"); 192999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append("before:\n"); 193999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 194999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 195999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 196999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append("after:\n"); 197999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (Map.Entry<Item, Rect> entry : after.entrySet()) { 198999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 199999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 200999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar message = message + "\n" + sb.toString(); 201999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals(message + ":\nitem counts should be equal", before.size() 202999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar , after.size()); 203999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 204999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final Item beforeItem = entry.getKey(); 205999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Rect afterRect = null; 206999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (strictItemEquality) { 207999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar afterRect = after.get(beforeItem); 208999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertNotNull(message + ":\nSame item should be visible after simple re-layout", 209999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar afterRect); 210999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } else { 211999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (Map.Entry<Item, Rect> afterEntry : after.entrySet()) { 212999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final Item afterItem = afterEntry.getKey(); 213999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (afterItem.mAdapterIndex == beforeItem.mAdapterIndex) { 214999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar afterRect = afterEntry.getValue(); 215999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar break; 216999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 217999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 218999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertNotNull(message + ":\nItem with same adapter index should be visible " + 219999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar "after simple re-layout", 220999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar afterRect); 221999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 222999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals(message + ":\nItem should be laid out at the same coordinates", 223999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar entry.getValue(), afterRect); 224999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 225999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 226999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 227999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar static class VisibleChildren { 228999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 229999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int firstVisiblePosition = RecyclerView.NO_POSITION; 230999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 231999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int firstFullyVisiblePosition = RecyclerView.NO_POSITION; 232999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 233999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int lastVisiblePosition = RecyclerView.NO_POSITION; 234999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 235999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int lastFullyVisiblePosition = RecyclerView.NO_POSITION; 236999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 237999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 238999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public String toString() { 239999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return "VisibleChildren{" + 240999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar "firstVisiblePosition=" + firstVisiblePosition + 241999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", firstFullyVisiblePosition=" + firstFullyVisiblePosition + 242999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", lastVisiblePosition=" + lastVisiblePosition + 243999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", lastFullyVisiblePosition=" + lastFullyVisiblePosition + 244999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar '}'; 245999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 246999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 247999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 248999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar static class OnLayoutListener { 249999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 250999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar void before(RecyclerView.Recycler recycler, RecyclerView.State state) { 251999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 252999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 253999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state) { 254999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 255999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 256999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 257999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar static class Config implements Cloneable { 258999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 259999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar static final int DEFAULT_ITEM_COUNT = 100; 260999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 261999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar boolean mStackFromEnd; 262999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 263999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int mOrientation = VERTICAL; 264999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 265999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar boolean mReverseLayout = false; 266999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 267999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar boolean mRecycleChildrenOnDetach = false; 268999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 269999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int mItemCount = DEFAULT_ITEM_COUNT; 270999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 2714143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar boolean mWrap = false; 2724143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 273999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar TestAdapter mTestAdapter; 274999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 275999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config(int orientation, boolean reverseLayout, boolean stackFromEnd) { 276999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientation = orientation; 277999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mReverseLayout = reverseLayout; 278999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mStackFromEnd = stackFromEnd; 279999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 280999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 281999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public Config() { 282999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 283999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 284999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 285999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config adapter(TestAdapter adapter) { 286999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mTestAdapter = adapter; 287999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return this; 288999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 289999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 290999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) { 291999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecycleChildrenOnDetach = recycleChildrenOnDetach; 292999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return this; 293999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 294999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 295999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config orientation(int orientation) { 296999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientation = orientation; 297999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return this; 298999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 299999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 300999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config stackFromBottom(boolean stackFromBottom) { 301999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mStackFromEnd = stackFromBottom; 302999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return this; 303999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 304999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 305999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config reverseLayout(boolean reverseLayout) { 306999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mReverseLayout = reverseLayout; 307999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return this; 308999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 309999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 310999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public Config itemCount(int itemCount) { 311999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mItemCount = itemCount; 312999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return this; 313999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 314999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 315999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // required by convention 316999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 317999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public Object clone() throws CloneNotSupportedException { 318999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return super.clone(); 319999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 320999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 321999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 322999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public String toString() { 323999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return "Config{" + 324999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar "mStackFromEnd=" + mStackFromEnd + 325999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", mOrientation=" + mOrientation + 326999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", mReverseLayout=" + mReverseLayout + 327999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach + 328999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ", mItemCount=" + mItemCount + 3294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ", wrap=" + mWrap + 330999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar '}'; 331999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 3324143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 3334143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar public Config wrap(boolean wrap) { 3344143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mWrap = wrap; 3354143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return this; 3364143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 337999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 338999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 339999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar class WrappedLinearLayoutManager extends LinearLayoutManager { 340999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 341999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar CountDownLatch layoutLatch; 342999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 343999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar OrientationHelper mSecondaryOrientation; 344999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 345999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar OnLayoutListener mOnLayoutListener; 346999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 347999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 348999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super(context, orientation, reverseLayout); 349999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 350999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 351999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void expectLayouts(int count) { 352999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar layoutLatch = new CountDownLatch(count); 353999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 354999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 35566826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar public void waitForLayout(int seconds) throws Throwable { 35666826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar layoutLatch.await(seconds * (DEBUG ? 100 : 1), SECONDS); 35766826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar checkForMainThreadException(); 35866826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar MatcherAssert.assertThat("all layouts should complete on time", 35966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar layoutLatch.getCount(), CoreMatchers.is(0L)); 36066826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar // use a runnable to ensure RV layout is finished 36166826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar getInstrumentation().runOnMainSync(new Runnable() { 36266826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar @Override 36366826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar public void run() { 36466826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar } 36566826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar }); 366999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 367999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 368999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 369999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void setOrientation(int orientation) { 370999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super.setOrientation(orientation); 371999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mSecondaryOrientation = null; 372999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 373999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 374999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 375999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) { 376999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (DEBUG) { 377999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child)); 378999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 379999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super.removeAndRecycleView(child, recycler); 380999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 381999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 382999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 383999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) { 384999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (DEBUG) { 385999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Log.d(TAG, 386999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index))); 387999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 388999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super.removeAndRecycleViewAt(index, recycler); 389999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 390999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 391999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 392999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar void ensureLayoutState() { 393999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super.ensureLayoutState(); 394999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (mSecondaryOrientation == null) { 395999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mSecondaryOrientation = OrientationHelper.createOrientationHelper(this, 396999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 1 - getOrientation()); 397999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 398999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 399999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 400999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 401999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar LayoutState createLayoutState() { 402999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return new LayoutState() { 403999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 404999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar View next(RecyclerView.Recycler recycler) { 405999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final boolean hadMore = hasMore(mRecyclerView.mState); 406999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int position = mCurrentPosition; 407999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar View next = super.next(recycler); 408999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals("if has more, should return a view", hadMore, next != null); 409999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals("position of the returned view must match current position", 410999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar position, RecyclerView.getChildViewHolderInt(next).getLayoutPosition()); 411999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return next; 412999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 413999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }; 414999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 415999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 416999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public String getBoundsLog() { 417999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar StringBuilder sb = new StringBuilder(); 418999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding()) 419999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding()); 420999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append("\nchildren bounds\n"); 421999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int childCount = getChildCount(); 422999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (int i = 0; i < childCount; i++) { 423999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar View child = getChildAt(i); 424999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child)) 425999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .append("[").append("start:").append( 426999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientationHelper.getDecoratedStart(child)).append(", end:") 427999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n"); 428999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 429999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return sb.toString(); 430999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 431999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 432999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void waitForAnimationsToEnd(int timeoutInSeconds) throws InterruptedException { 433999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator(); 434999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (itemAnimator == null) { 435999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return; 436999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 437999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final CountDownLatch latch = new CountDownLatch(1); 438999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final boolean running = itemAnimator.isRunning( 439999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 440999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 441999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void onAnimationsFinished() { 442999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar latch.countDown(); 443999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 444999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 445999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ); 446999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (running) { 447999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar latch.await(timeoutInSeconds, TimeUnit.SECONDS); 448999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 449999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 450999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 451999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public VisibleChildren traverseAndFindVisibleChildren() { 452999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int childCount = getChildCount(); 453999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final VisibleChildren visibleChildren = new VisibleChildren(); 454999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int start = mOrientationHelper.getStartAfterPadding(); 455999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int end = mOrientationHelper.getEndAfterPadding(); 456999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (int i = 0; i < childCount; i++) { 457999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar View child = getChildAt(i); 458999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int childStart = mOrientationHelper.getDecoratedStart(child); 459999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int childEnd = mOrientationHelper.getDecoratedEnd(child); 460999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final boolean fullyVisible = childStart >= start && childEnd <= end; 461999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final boolean hidden = childEnd <= start || childStart >= end; 462999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (hidden) { 463999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar continue; 464999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 465999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int position = getPosition(child); 466999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (fullyVisible) { 467999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (position < visibleChildren.firstFullyVisiblePosition || 468999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) { 469999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar visibleChildren.firstFullyVisiblePosition = position; 470999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 471999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 472999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (position > visibleChildren.lastFullyVisiblePosition) { 473999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar visibleChildren.lastFullyVisiblePosition = position; 474999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 475999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 476999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 477999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (position < visibleChildren.firstVisiblePosition || 478999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) { 479999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar visibleChildren.firstVisiblePosition = position; 480999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 481999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 482999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (position > visibleChildren.lastVisiblePosition) { 483999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar visibleChildren.lastVisiblePosition = position; 484999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 485999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 486999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 487999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return visibleChildren; 488999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 489999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 490999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Rect getViewBounds(View view) { 491999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (getOrientation() == HORIZONTAL) { 492999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return new Rect( 493999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientationHelper.getDecoratedStart(view), 494999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mSecondaryOrientation.getDecoratedStart(view), 495999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientationHelper.getDecoratedEnd(view), 496999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mSecondaryOrientation.getDecoratedEnd(view)); 497999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } else { 498999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return new Rect( 499999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mSecondaryOrientation.getDecoratedStart(view), 500999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientationHelper.getDecoratedStart(view), 501999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mSecondaryOrientation.getDecoratedEnd(view), 502999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOrientationHelper.getDecoratedEnd(view)); 503999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 504999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 505999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 506999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 507999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Map<Item, Rect> collectChildCoordinates() throws Throwable { 508999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>(); 509999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar runTestOnUiThread(new Runnable() { 510999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 511999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void run() { 512999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int childCount = getChildCount(); 513999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Rect layoutBounds = new Rect(0, 0, 514999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.getWidth(), mLayoutManager.getHeight()); 515999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (int i = 0; i < childCount; i++) { 516999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar View child = getChildAt(i); 517999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child 518999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .getLayoutParams(); 519999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar TestViewHolder vh = (TestViewHolder) lp.mViewHolder; 520999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Rect childBounds = getViewBounds(child); 521999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (new Rect(childBounds).intersect(layoutBounds)) { 522999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar items.put(vh.mBoundItem, childBounds); 523999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 524999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 525999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 526999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }); 527999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return items; 528999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 529999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 530999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 531999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 532999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar try { 533999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (mOnLayoutListener != null) { 534999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOnLayoutListener.before(recycler, state); 535999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 536999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super.onLayoutChildren(recycler, state); 537999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (mOnLayoutListener != null) { 538999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mOnLayoutListener.after(recycler, state); 539999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 540999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } catch (Throwable t) { 541999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar postExceptionToInstrumentation(t); 542999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 543999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar layoutLatch.countDown(); 544999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 545999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 546999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 547999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 548999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar} 549