10a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarpackage android.support.v7.widget; 20a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 30a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.graphics.Rect; 40a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.support.annotation.Nullable; 50a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.util.Log; 60a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.view.View; 70a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.view.ViewGroup; 80a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 90a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.lang.reflect.Field; 100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.ArrayList; 110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.Arrays; 120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.HashSet; 130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.LinkedHashMap; 140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.List; 150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.Map; 160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.concurrent.CountDownLatch; 170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.concurrent.TimeUnit; 180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_END; 210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_START; 220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL; 230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS; 240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_NONE; 250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL; 260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.assertEquals; 270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.assertFalse; 280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.assertNotNull; 290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.assertTrue; 300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3166826566020afc8d11f183cf3fe443ac0a022384Yigit Boyarimport static java.util.concurrent.TimeUnit.SECONDS; 3266826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar 3366826566020afc8d11f183cf3fe443ac0a022384Yigit Boyarimport org.hamcrest.CoreMatchers; 3466826566020afc8d11f183cf3fe443ac0a022384Yigit Boyarimport org.hamcrest.MatcherAssert; 3566826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar 360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarpublic class BaseStaggeredGridLayoutManagerTest extends BaseRecyclerViewInstrumentationTest { 370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected static final boolean DEBUG = false; 390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected static final int AVG_ITEM_PER_VIEW = 3; 400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected static final String TAG = "StaggeredGridLayoutManagerTest"; 410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar volatile WrappedLayoutManager mLayoutManager; 420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar GridTestAdapter mAdapter; 430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected static List<Config> createBaseVariations() { 450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar List<Config> variations = new ArrayList<>(); 460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int orientation : new int[]{VERTICAL, HORIZONTAL}) { 470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (boolean reverseLayout : new boolean[]{false, true}) { 480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int spanCount : new int[]{1, 3}) { 490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int gapStrategy : new int[]{GAP_HANDLING_NONE, 500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS}) { 514143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar for (boolean wrap : new boolean[]{true, false}) { 524143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar variations.add(new Config(orientation, reverseLayout, spanCount, 534143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar gapStrategy).wrap(wrap)); 544143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 554143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return variations; 610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected static List<Config> addConfigVariation(List<Config> base, String fieldName, 640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Object... variations) 650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException { 660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar List<Config> newConfigs = new ArrayList<Config>(); 670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Field field = Config.class.getDeclaredField(fieldName); 680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Config config : base) { 690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Object variation : variations) { 700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Config newConfig = (Config) config.clone(); 710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar field.set(newConfig, variation); 720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar newConfigs.add(newConfig); 730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return newConfigs; 760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void setupByConfig(Config config) throws Throwable { 790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setupByConfig(config, new GridTestAdapter(config.mItemCount, config.mOrientation)); 800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void setupByConfig(Config config, GridTestAdapter adapter) throws Throwable { 830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mAdapter = adapter; 840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView = new RecyclerView(getActivity()); 850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.setAdapter(mAdapter); 860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.setHasFixedSize(true); 870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager = new WrappedLayoutManager(config.mSpanCount, 880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar config.mOrientation); 890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.setGapStrategy(config.mGapStrategy); 900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.setReverseLayout(config.mReverseLayout); 910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.setLayoutManager(mLayoutManager); 920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { 930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar RecyclerView.State state) { 960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StaggeredGridLayoutManager.LayoutParams 980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams(); 990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull("view should have layout params assigned", lp); 1000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull("when item offsets are requested, view should have a valid span", 1010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.mSpan); 1020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable t) { 1030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar postExceptionToInstrumentation(t); 1040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 1070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StaggeredGridLayoutManager.LayoutParams getLp(View view) { 1100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams(); 1110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void waitFirstLayout() throws Throwable { 1140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(1); 1150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setRecyclerView(mRecyclerView); 1168cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar mLayoutManager.waitForLayout(3); 1170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar getInstrumentation().waitForIdleSync(); 1180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar /** 1210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * enqueues an empty runnable to main thread so that we can be assured it did run 1220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * 1230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * @param count Number of times to run 1240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar */ 1250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected void waitForMainThread(int count) throws Throwable { 1260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final AtomicInteger i = new AtomicInteger(count); 1270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar while (i.get() > 0) { 1280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar runTestOnUiThread(new Runnable() { 1290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 1300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 1310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar i.decrementAndGet(); 1320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 1340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void assertRectSetsNotEqual(String message, Map<Item, Rect> before, 1380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Map<Item, Rect> after) { 1390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Throwable throwable = null; 1400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 1410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertRectSetsEqual("NOT " + message, before, after); 1420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable t) { 1430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar throwable = t; 1440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull(message + " two layout should be different", throwable); 1460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) { 1490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertRectSetsEqual(message, before, after, true); 1500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after, 1530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar boolean strictItemEquality) { 1540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StringBuilder log = new StringBuilder(); 1550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 1560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar log.append("checking rectangle equality.\n"); 1570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar log.append("total space:" + mLayoutManager.mPrimaryOrientation.getTotalSpace()); 1580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar log.append("before:"); 1590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 1600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar log.append("\n").append(entry.getKey().mAdapterIndex).append(":") 1610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .append(entry.getValue()); 1620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar log.append("\nafter:"); 1640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Map.Entry<Item, Rect> entry : after.entrySet()) { 1650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar log.append("\n").append(entry.getKey().mAdapterIndex).append(":") 1660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .append(entry.getValue()); 1670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar message += "\n\n" + log.toString(); 1690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(message + ": item counts should be equal", before.size() 1710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar , after.size()); 1720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 1730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final Item beforeItem = entry.getKey(); 1740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Rect afterRect = null; 1750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (strictItemEquality) { 1760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar afterRect = after.get(beforeItem); 1770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull(message + ": Same item should be visible after simple re-layout", 1780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar afterRect); 1790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 1800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Map.Entry<Item, Rect> afterEntry : after.entrySet()) { 1810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final Item afterItem = afterEntry.getKey(); 1820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (afterItem.mAdapterIndex == beforeItem.mAdapterIndex) { 1830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar afterRect = afterEntry.getValue(); 1840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar break; 1850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull(message + ": Item with same adapter index should be visible " + 1880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar "after simple re-layout", 1890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar afterRect); 1900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(message + ": Item should be laid out at the same coordinates", 1920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar entry.getValue(), 1930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar afterRect); 1940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected void assertViewPositions(Config config) { 1980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ArrayList<ArrayList<View>> viewsBySpan = mLayoutManager.collectChildrenBySpan(); 1990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar OrientationHelper orientationHelper = OrientationHelper 2000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .createOrientationHelper(mLayoutManager, config.mOrientation); 2010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (ArrayList<View> span : viewsBySpan) { 2020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // validate all children's order. first child should have min start mPosition 2030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int count = span.size(); 2040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0, j = 1; j < count; i++, j++) { 2050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View prev = span.get(i); 2060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View next = span.get(j); 2070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue(config + " prev item should be above next item", 2080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getDecoratedEnd(prev) <= orientationHelper 2090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getDecoratedStart(next) 2100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ); 2110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected TargetTuple findInvisibleTarget(Config config) { 2170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int minPosition = Integer.MAX_VALUE, maxPosition = Integer.MIN_VALUE; 2180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < mLayoutManager.getChildCount(); i++) { 2190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View child = mLayoutManager.getChildAt(i); 2200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int position = mRecyclerView.getChildLayoutPosition(child); 2210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (position < minPosition) { 2220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar minPosition = position; 2230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (position > maxPosition) { 2250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar maxPosition = position; 2260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int tailTarget = maxPosition + (mAdapter.getItemCount() - maxPosition) / 2; 2290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int headTarget = minPosition / 2; 2300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int target; 2310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // where will the child come from ? 2320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int itemLayoutDirection; 2330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (Math.abs(tailTarget - maxPosition) > Math.abs(headTarget - minPosition)) { 2340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar target = tailTarget; 2350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar itemLayoutDirection = config.mReverseLayout ? LAYOUT_START : LAYOUT_END; 2360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 2370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar target = headTarget; 2380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar itemLayoutDirection = config.mReverseLayout ? LAYOUT_END : LAYOUT_START; 2390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 2410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Log.d(TAG, 2420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar config + " target:" + target + " min:" + minPosition + ", max:" + maxPosition); 2430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return new TargetTuple(target, itemLayoutDirection); 2450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected void scrollToPositionWithOffset(final int position, final int offset) 2480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar throws Throwable { 2490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar runTestOnUiThread(new Runnable() { 2500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 2510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 2520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.scrollToPositionWithOffset(position, offset); 2530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 2550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar static class OnLayoutListener { 2580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void before(RecyclerView.Recycler recycler, RecyclerView.State state) { 2600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state) { 2630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar static class VisibleChildren { 2670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int[] firstVisiblePositions; 2690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int[] firstFullyVisiblePositions; 2710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int[] lastVisiblePositions; 2730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int[] lastFullyVisiblePositions; 2750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View findFirstPartialVisibleClosestToStart; 2770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View findFirstPartialVisibleClosestToEnd; 2780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar VisibleChildren(int spanCount) { 2800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar firstFullyVisiblePositions = new int[spanCount]; 2810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar firstVisiblePositions = new int[spanCount]; 2820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lastVisiblePositions = new int[spanCount]; 2830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lastFullyVisiblePositions = new int[spanCount]; 2840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < spanCount; i++) { 2850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar firstFullyVisiblePositions[i] = RecyclerView.NO_POSITION; 2860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar firstVisiblePositions[i] = RecyclerView.NO_POSITION; 2870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lastVisiblePositions[i] = RecyclerView.NO_POSITION; 2880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lastFullyVisiblePositions[i] = RecyclerView.NO_POSITION; 2890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 2930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public boolean equals(Object o) { 2940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (this == o) { 2950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return true; 2960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (o == null || getClass() != o.getClass()) { 2980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 2990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar VisibleChildren that = (VisibleChildren) o; 3020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (!Arrays.equals(firstFullyVisiblePositions, that.firstFullyVisiblePositions)) { 3040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 3050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (findFirstPartialVisibleClosestToStart 3070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar != null ? !findFirstPartialVisibleClosestToStart 3080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .equals(that.findFirstPartialVisibleClosestToStart) 3090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar : that.findFirstPartialVisibleClosestToStart != null) { 3100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 3110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (!Arrays.equals(firstVisiblePositions, that.firstVisiblePositions)) { 3130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 3140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (!Arrays.equals(lastFullyVisiblePositions, that.lastFullyVisiblePositions)) { 3160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 3170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (findFirstPartialVisibleClosestToEnd != null ? !findFirstPartialVisibleClosestToEnd 3190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .equals(that.findFirstPartialVisibleClosestToEnd) 3200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar : that.findFirstPartialVisibleClosestToEnd 3210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar != null) { 3220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 3230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (!Arrays.equals(lastVisiblePositions, that.lastVisiblePositions)) { 3250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 3260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return true; 3290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 3320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public int hashCode() { 3330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int result = Arrays.hashCode(firstVisiblePositions); 3340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar result = 31 * result + Arrays.hashCode(firstFullyVisiblePositions); 3350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar result = 31 * result + Arrays.hashCode(lastVisiblePositions); 3360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar result = 31 * result + Arrays.hashCode(lastFullyVisiblePositions); 3370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar result = 31 * result + (findFirstPartialVisibleClosestToStart != null 3380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ? findFirstPartialVisibleClosestToStart 3390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .hashCode() : 0); 3400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar result = 31 * result + (findFirstPartialVisibleClosestToEnd != null 3410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ? findFirstPartialVisibleClosestToEnd 3420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .hashCode() 3430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar : 0); 3440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return result; 3450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 3480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public String toString() { 3490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return "VisibleChildren{" + 3500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar "firstVisiblePositions=" + Arrays.toString(firstVisiblePositions) + 3510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ", firstFullyVisiblePositions=" + Arrays.toString(firstFullyVisiblePositions) + 3520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ", lastVisiblePositions=" + Arrays.toString(lastVisiblePositions) + 3530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ", lastFullyVisiblePositions=" + Arrays.toString(lastFullyVisiblePositions) + 3540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ", findFirstPartialVisibleClosestToStart=" + 3550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar viewToString(findFirstPartialVisibleClosestToStart) + 3560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ", findFirstPartialVisibleClosestToEnd=" + 3570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar viewToString(findFirstPartialVisibleClosestToEnd) + 3580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar '}'; 3590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar private String viewToString(View view) { 3620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (view == null) { 3630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return null; 3640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ViewGroup.LayoutParams lp = view.getLayoutParams(); 3660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (lp instanceof RecyclerView.LayoutParams == false) { 3670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return System.identityHashCode(view) + "(?)"; 3680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar RecyclerView.LayoutParams rvlp = (RecyclerView.LayoutParams) lp; 3700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return System.identityHashCode(view) + "(" + rvlp.getViewAdapterPosition() + ")"; 3710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar abstract static class OnBindCallback { 3750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar abstract void onBoundItem(TestViewHolder vh, int position); 3770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar boolean assignRandomSize() { 3790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return true; 3800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void onCreatedViewHolder(TestViewHolder vh) { 3830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar static class Config implements Cloneable { 3870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar static final int DEFAULT_ITEM_COUNT = 300; 3890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mOrientation = OrientationHelper.VERTICAL; 3910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar boolean mReverseLayout = false; 3930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mSpanCount = 3; 3950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mGapStrategy = GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS; 3970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mItemCount = DEFAULT_ITEM_COUNT; 3990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar boolean mWrap = false; 4014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 4020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Config(int orientation, boolean reverseLayout, int spanCount, int gapStrategy) { 4030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOrientation = orientation; 4040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mReverseLayout = reverseLayout; 4050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSpanCount = spanCount; 4060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mGapStrategy = gapStrategy; 4070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public Config() { 4100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Config orientation(int orientation) { 4140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOrientation = orientation; 4150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return this; 4160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Config reverseLayout(boolean reverseLayout) { 4190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mReverseLayout = reverseLayout; 4200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return this; 4210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Config spanCount(int spanCount) { 4240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSpanCount = spanCount; 4250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return this; 4260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Config gapStrategy(int gapStrategy) { 4290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mGapStrategy = gapStrategy; 4300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return this; 4310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public Config itemCount(int itemCount) { 4340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mItemCount = itemCount; 4350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return this; 4360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4384143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar public Config wrap(boolean wrap) { 4394143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mWrap = wrap; 4404143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return this; 4414143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 4424143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 4430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 4440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public String toString() { 4450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return "[CONFIG:" + 4460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar " span:" + mSpanCount + "," + 4470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar " orientation:" + (mOrientation == HORIZONTAL ? "horz," : "vert,") + 4480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar " reverse:" + (mReverseLayout ? "T" : "F") + 4490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar " itemCount:" + mItemCount + 4504143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar " wrapContent:" + mWrap + 4510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar " gap strategy: " + gapStrategyName(mGapStrategy); 4520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected static String gapStrategyName(int gapStrategy) { 4550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar switch (gapStrategy) { 4560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar case GAP_HANDLING_NONE: 4570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return "none"; 4580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar case GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS: 4590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return "move spans"; 4600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return "gap strategy: unknown"; 4620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 4650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public Object clone() throws CloneNotSupportedException { 4660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return super.clone(); 4670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar class WrappedLayoutManager extends StaggeredGridLayoutManager { 4710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar CountDownLatch layoutLatch; 4730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar OnLayoutListener mOnLayoutListener; 4740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // gradle does not yet let us customize manifest for tests which is necessary to test RTL. 4750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // until bug is fixed, we'll fake it. 4760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // public issue id: 57819 4770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Boolean mFakeRTL; 4780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 4800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar boolean isLayoutRTL() { 4810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return mFakeRTL == null ? super.isLayoutRTL() : mFakeRTL; 4820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void expectLayouts(int count) { 4850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutLatch = new CountDownLatch(count); 4860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 48866826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar public void waitForLayout(int seconds) throws Throwable { 48966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar layoutLatch.await(seconds * (DEBUG ? 100 : 1), SECONDS); 490bc61003e6a10872c23f0de155456cb2fbeef31a7Yigit Boyar checkForMainThreadException(); 49166826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar MatcherAssert.assertThat("all layouts should complete on time", 49266826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar layoutLatch.getCount(), CoreMatchers.is(0L)); 49366826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar // use a runnable to ensure RV layout is finished 49466826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar getInstrumentation().runOnMainSync(new Runnable() { 49566826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar @Override 49666826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar public void run() { 49766826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar } 49866826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar }); 4990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void assertNoLayout(String msg, long timeout) throws Throwable { 5020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 5030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertFalse(msg, layoutLatch.getCount() == 0); 5040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 5070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 5080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar String before; 5090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 5100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar before = layoutToString("before"); 5110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 5120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar before = "enable DEBUG"; 5130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 5150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mOnLayoutListener != null) { 5160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOnLayoutListener.before(recycler, state); 5170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super.onLayoutChildren(recycler, state); 5190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mOnLayoutListener != null) { 5200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOnLayoutListener.after(recycler, state); 5210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar validateChildren(before); 5230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable t) { 5240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar postExceptionToInstrumentation(t); 5250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutLatch.countDown(); 5280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 5310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state) { 5320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 5330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int result = super.scrollBy(dt, recycler, state); 5340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar validateChildren(); 5350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return result; 5360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable t) { 5370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar postExceptionToInstrumentation(t); 5380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return 0; 5410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public WrappedLayoutManager(int spanCount, int orientation) { 5440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super(spanCount, orientation); 5450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ArrayList<ArrayList<View>> collectChildrenBySpan() { 5480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ArrayList<ArrayList<View>> viewsBySpan = new ArrayList<ArrayList<View>>(); 5490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < getSpanCount(); i++) { 5500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar viewsBySpan.add(new ArrayList<View>()); 5510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < getChildCount(); i++) { 5530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View view = getChildAt(i); 5540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar LayoutParams lp 5550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar = (LayoutParams) view 5560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getLayoutParams(); 5570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar viewsBySpan.get(lp.mSpan.mIndex).add(view); 5580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return viewsBySpan; 5600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Nullable 5630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 5640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public View onFocusSearchFailed(View focused, int direction, RecyclerView.Recycler recycler, 5650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar RecyclerView.State state) { 5660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View result = null; 5670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 5680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar result = super.onFocusSearchFailed(focused, direction, recycler, state); 5690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar validateChildren(); 5700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable t) { 5710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar postExceptionToInstrumentation(t); 5720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return result; 5740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Rect getViewBounds(View view) { 5770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (getOrientation() == HORIZONTAL) { 5780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return new Rect( 5790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedStart(view), 5800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSecondaryOrientation.getDecoratedStart(view), 5810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedEnd(view), 5820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSecondaryOrientation.getDecoratedEnd(view)); 5830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 5840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return new Rect( 5850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSecondaryOrientation.getDecoratedStart(view), 5860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedStart(view), 5870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSecondaryOrientation.getDecoratedEnd(view), 5880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedEnd(view)); 5890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public String getBoundsLog() { 5930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StringBuilder sb = new StringBuilder(); 5940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar sb.append("view bounds:[start:").append(mPrimaryOrientation.getStartAfterPadding()) 5950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .append(",").append(" end").append(mPrimaryOrientation.getEndAfterPadding()); 5960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar sb.append("\nchildren bounds\n"); 5970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int childCount = getChildCount(); 5980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < childCount; i++) { 5990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View child = getChildAt(i); 6000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child)) 6010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .append("[").append("start:").append( 6020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedStart(child)).append(", end:") 6030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .append(mPrimaryOrientation.getDecoratedEnd(child)).append("]\n"); 6040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return sb.toString(); 6060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public VisibleChildren traverseAndFindVisibleChildren() { 6090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int childCount = getChildCount(); 6100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final VisibleChildren visibleChildren = new VisibleChildren(getSpanCount()); 6110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int start = mPrimaryOrientation.getStartAfterPadding(); 6120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int end = mPrimaryOrientation.getEndAfterPadding(); 6130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < childCount; i++) { 6140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View child = getChildAt(i); 6150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int childStart = mPrimaryOrientation.getDecoratedStart(child); 6160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int childEnd = mPrimaryOrientation.getDecoratedEnd(child); 6170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final boolean fullyVisible = childStart >= start && childEnd <= end; 6180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final boolean hidden = childEnd <= start || childStart >= end; 6190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (hidden) { 6200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar continue; 6210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int position = getPosition(child); 6230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int span = getLp(child).getSpanIndex(); 6240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (fullyVisible) { 6250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (position < visibleChildren.firstFullyVisiblePositions[span] || 6260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.firstFullyVisiblePositions[span] 6270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar == RecyclerView.NO_POSITION) { 6280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.firstFullyVisiblePositions[span] = position; 6290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (position > visibleChildren.lastFullyVisiblePositions[span]) { 6320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.lastFullyVisiblePositions[span] = position; 6330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (position < visibleChildren.firstVisiblePositions[span] || 6370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.firstVisiblePositions[span] == RecyclerView.NO_POSITION) { 6380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.firstVisiblePositions[span] = position; 6390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (position > visibleChildren.lastVisiblePositions[span]) { 6420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.lastVisiblePositions[span] = position; 6430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (visibleChildren.findFirstPartialVisibleClosestToStart == null) { 6450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.findFirstPartialVisibleClosestToStart = child; 6460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.findFirstPartialVisibleClosestToEnd = child; 6480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return visibleChildren; 6500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Map<Item, Rect> collectChildCoordinates() throws Throwable { 6530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>(); 6540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar runTestOnUiThread(new Runnable() { 6550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 6560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 6570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int childCount = getChildCount(); 6580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < childCount; i++) { 6590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View child = getChildAt(i); 6600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // do it if and only if child is visible 6610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (child.getRight() < 0 || child.getBottom() < 0 || 6620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar child.getLeft() >= getWidth() || child.getTop() >= getHeight()) { 6630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // invisible children may be drawn in cases like scrolling so we should 6640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // ignore them 6650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar continue; 6660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar LayoutParams lp = (LayoutParams) child 6680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getLayoutParams(); 6690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar TestViewHolder vh = (TestViewHolder) lp.mViewHolder; 6700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar items.put(vh.mBoundItem, getViewBounds(child)); 6710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 6740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return items; 6750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void setFakeRtl(Boolean fakeRtl) { 6790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mFakeRTL = fakeRtl; 6800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 6810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar requestLayoutOnUIThread(mRecyclerView); 6820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable throwable) { 6830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar postExceptionToInstrumentation(throwable); 6840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar String layoutToString(String hint) { 6880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StringBuilder sb = new StringBuilder(); 6890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar sb.append("LAYOUT POSITIONS AND INDICES ").append(hint).append("\n"); 6900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < getChildCount(); i++) { 6910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final View view = getChildAt(i); 6920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final LayoutParams layoutParams = (LayoutParams) view.getLayoutParams(); 6930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar sb.append(String.format("index: %d pos: %d top: %d bottom: %d span: %d isFull:%s", 6940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar i, getPosition(view), 6950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedStart(view), 6960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedEnd(view), 6970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutParams.getSpanIndex(), layoutParams.isFullSpan())).append("\n"); 6980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return sb.toString(); 7000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected void validateChildren() { 7030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar validateChildren(null); 7040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar private void validateChildren(String msg) { 7070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (getChildCount() == 0 || mRecyclerView.mState.isPreLayout()) { 7080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return; 7090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int dir = mShouldReverseLayout ? -1 : 1; 7110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int i = 0; 7120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int pos = -1; 7130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar while (i < getChildCount()) { 7140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); 7150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (lp.isItemRemoved()) { 7160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar i++; 7170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar continue; 7180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar pos = getPosition(getChildAt(i)); 7200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar break; 7210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (pos == -1) { 7230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return; 7240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar while (++i < getChildCount()) { 7260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); 7270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (lp.isItemRemoved()) { 7280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar continue; 7290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar pos += dir; 7310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (getPosition(getChildAt(i)) != pos) { 7320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar throw new RuntimeException("INVALID POSITION FOR CHILD " + i + "\n" + 7330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutToString("ERROR") + "\n msg:" + msg); 7340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar class GridTestAdapter extends TestAdapter { 7400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mOrientation; 7420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mRecyclerViewWidth; 7430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mRecyclerViewHeight; 7440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Integer mSizeReference = null; 7450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // original ids of items that should be full span 7470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar HashSet<Integer> mFullSpanItems = new HashSet<Integer>(); 7480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected boolean mViewsHaveEqualSize = false; // size in the scrollable direction 7500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected OnBindCallback mOnBindCallback; 7520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar GridTestAdapter(int count, int orientation) { 7540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super(count); 7550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOrientation = orientation; 7560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 7590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public TestViewHolder onCreateViewHolder(ViewGroup parent, 7600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int viewType) { 7610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerViewWidth = parent.getWidth(); 7620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerViewHeight = parent.getHeight(); 7630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar TestViewHolder vh = super.onCreateViewHolder(parent, viewType); 7640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mOnBindCallback != null) { 7650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOnBindCallback.onCreatedViewHolder(vh); 7660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return vh; 7680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 7710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void offsetOriginalIndices(int start, int offset) { 7720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mFullSpanItems.size() > 0) { 7730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar HashSet<Integer> old = mFullSpanItems; 7740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mFullSpanItems = new HashSet<Integer>(); 7750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Integer i : old) { 7760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (i < start) { 7770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mFullSpanItems.add(i); 7780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else if (offset > 0 || (start + Math.abs(offset)) <= i) { 7790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mFullSpanItems.add(i + offset); 7800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else if (DEBUG) { 7810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Log.d(TAG, "removed full span item " + i); 7820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super.offsetOriginalIndices(start, offset); 7860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 7890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected void moveInUIThread(int from, int to) { 7900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar boolean setAsFullSpanAgain = mFullSpanItems.contains(from); 7910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super.moveInUIThread(from, to); 7920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (setAsFullSpanAgain) { 7930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mFullSpanItems.add(to); 7940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 7980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 7990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int position) { 8000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mSizeReference == null) { 8010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSizeReference = mOrientation == OrientationHelper.HORIZONTAL ? mRecyclerViewWidth 8020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar / AVG_ITEM_PER_VIEW : mRecyclerViewHeight / AVG_ITEM_PER_VIEW; 8030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super.onBindViewHolder(holder, position); 8050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Item item = mItems.get(position); 8060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) holder.itemView 8070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getLayoutParams(); 8080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (lp instanceof StaggeredGridLayoutManager.LayoutParams) { 8094143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ((StaggeredGridLayoutManager.LayoutParams) lp) 8104143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar .setFullSpan(mFullSpanItems.contains(item.mAdapterIndex)); 8110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 8124143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar StaggeredGridLayoutManager.LayoutParams slp 8134143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar = (StaggeredGridLayoutManager.LayoutParams) mLayoutManager 8144143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar .generateDefaultLayoutParams(); 8150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar holder.itemView.setLayoutParams(slp); 8160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar slp.setFullSpan(mFullSpanItems.contains(item.mAdapterIndex)); 8170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp = slp; 8180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 8200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mOnBindCallback == null || mOnBindCallback.assignRandomSize()) { 8210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int minSize = mViewsHaveEqualSize ? mSizeReference : 8220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSizeReference + 20 * (item.mId % 10); 8230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mOrientation == OrientationHelper.HORIZONTAL) { 8240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar holder.itemView.setMinimumWidth(minSize); 8250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 8260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar holder.itemView.setMinimumHeight(minSize); 8270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.topMargin = 3; 8290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.leftMargin = 5; 8300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.rightMargin = 7; 8310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.bottomMargin = 9; 8320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 8340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mOnBindCallback != null) { 8350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOnBindCallback.onBoundItem(holder, position); 8360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar} 840