10a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarpackage android.support.v7.widget;
20a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport static android.support.v7.widget.LayoutState.LAYOUT_END;
4210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport static android.support.v7.widget.LayoutState.LAYOUT_START;
5210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;
6210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS;
7210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_NONE;
8210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL;
9210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikas
10210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport static org.junit.Assert.assertEquals;
11210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport static org.junit.Assert.assertFalse;
12210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport static org.junit.Assert.assertNotNull;
13210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport static org.junit.Assert.assertTrue;
14210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikas
15210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport static java.util.concurrent.TimeUnit.SECONDS;
16210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikas
179c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiriimport android.graphics.Color;
180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.graphics.Rect;
199c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiriimport android.graphics.drawable.ColorDrawable;
209c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiriimport android.graphics.drawable.StateListDrawable;
210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.support.annotation.Nullable;
220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.util.Log;
239c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiriimport android.util.StateSet;
240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.view.View;
250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.view.ViewGroup;
260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
27210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport org.hamcrest.CoreMatchers;
28210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikasimport org.hamcrest.MatcherAssert;
29210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikas
300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.lang.reflect.Field;
310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.ArrayList;
320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.Arrays;
330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.HashSet;
340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.LinkedHashMap;
350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.List;
360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.Map;
370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.concurrent.CountDownLatch;
380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.concurrent.TimeUnit;
390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.concurrent.atomic.AtomicInteger;
400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
41febbd2db61780fb43c70b036a598b63a3465c7c4Yigit Boyarabstract class BaseStaggeredGridLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    protected static final boolean DEBUG = false;
440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    protected static final int AVG_ITEM_PER_VIEW = 3;
45febbd2db61780fb43c70b036a598b63a3465c7c4Yigit Boyar    protected static final String TAG = "SGLM_TEST";
460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    volatile WrappedLayoutManager mLayoutManager;
470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    GridTestAdapter mAdapter;
480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    protected static List<Config> createBaseVariations() {
500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        List<Config> variations = new ArrayList<>();
510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (boolean reverseLayout : new boolean[]{false, true}) {
530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                for (int spanCount : new int[]{1, 3}) {
540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    for (int gapStrategy : new int[]{GAP_HANDLING_NONE,
550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS}) {
564143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                        for (boolean wrap : new boolean[]{true, false}) {
574143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                            variations.add(new Config(orientation, reverseLayout, spanCount,
584143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                                    gapStrategy).wrap(wrap));
594143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                        }
604143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        return variations;
660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    protected static List<Config> addConfigVariation(List<Config> base, String fieldName,
690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Object... variations)
700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException {
710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        List<Config> newConfigs = new ArrayList<Config>();
720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Field field = Config.class.getDeclaredField(fieldName);
730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        for (Config config : base) {
740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (Object variation : variations) {
750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                Config newConfig = (Config) config.clone();
760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                field.set(newConfig, variation);
770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                newConfigs.add(newConfig);
780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        return newConfigs;
810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    void setupByConfig(Config config) throws Throwable {
840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        setupByConfig(config, new GridTestAdapter(config.mItemCount, config.mOrientation));
850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    void setupByConfig(Config config, GridTestAdapter adapter) throws Throwable {
880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mAdapter = adapter;
891ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        mRecyclerView = new WrappedRecyclerView(getActivity());
900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mRecyclerView.setAdapter(mAdapter);
910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mRecyclerView.setHasFixedSize(true);
92a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska        mLayoutManager = new WrappedLayoutManager(config.mSpanCount, config.mOrientation);
930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mLayoutManager.setGapStrategy(config.mGapStrategy);
940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mLayoutManager.setReverseLayout(config.mReverseLayout);
950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mRecyclerView.setLayoutManager(mLayoutManager);
960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    RecyclerView.State state) {
1000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                try {
1010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    StaggeredGridLayoutManager.LayoutParams
1020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
1030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    assertNotNull("view should have layout params assigned", lp);
1040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    assertNotNull("when item offsets are requested, view should have a valid span",
1050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            lp.mSpan);
1060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                } catch (Throwable t) {
1070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    postExceptionToInstrumentation(t);
1080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
1090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
1100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        });
1110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    StaggeredGridLayoutManager.LayoutParams getLp(View view) {
1140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        return (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
1150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    void waitFirstLayout() throws Throwable {
1180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mLayoutManager.expectLayouts(1);
1190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        setRecyclerView(mRecyclerView);
1208cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar        mLayoutManager.waitForLayout(3);
1210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        getInstrumentation().waitForIdleSync();
1220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    /**
1250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar     * enqueues an empty runnable to main thread so that we can be assured it did run
1260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar     *
1270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar     * @param count Number of times to run
1280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar     */
1290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    protected void waitForMainThread(int count) throws Throwable {
1300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final AtomicInteger i = new AtomicInteger(count);
1310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        while (i.get() > 0) {
13242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas            mActivityRule.runOnUiThread(new Runnable() {
1330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                @Override
1340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                public void run() {
1350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    i.decrementAndGet();
1360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
1370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            });
1380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
1390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void assertRectSetsNotEqual(String message, Map<Item, Rect> before,
1420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Map<Item, Rect> after) {
1430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Throwable throwable = null;
1440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        try {
1450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertRectSetsEqual("NOT " + message, before, after);
1460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        } catch (Throwable t) {
1470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            throwable = t;
1480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
1490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertNotNull(message + " two layout should be different", throwable);
1500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) {
1530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertRectSetsEqual(message, before, after, true);
1540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after,
1570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            boolean strictItemEquality) {
1580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        StringBuilder log = new StringBuilder();
1590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (DEBUG) {
1600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            log.append("checking rectangle equality.\n");
1610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            log.append("total space:" + mLayoutManager.mPrimaryOrientation.getTotalSpace());
1620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            log.append("before:");
1630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (Map.Entry<Item, Rect> entry : before.entrySet()) {
1640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                log.append("\n").append(entry.getKey().mAdapterIndex).append(":")
1650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .append(entry.getValue());
1660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
1670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            log.append("\nafter:");
1680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (Map.Entry<Item, Rect> entry : after.entrySet()) {
1690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                log.append("\n").append(entry.getKey().mAdapterIndex).append(":")
1700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .append(entry.getValue());
1710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
1720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            message += "\n\n" + log.toString();
1730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
1740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertEquals(message + ": item counts should be equal", before.size()
1750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                , after.size());
1760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
1770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final Item beforeItem = entry.getKey();
1780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Rect afterRect = null;
1790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (strictItemEquality) {
1800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                afterRect = after.get(beforeItem);
1810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertNotNull(message + ": Same item should be visible after simple re-layout",
1820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        afterRect);
1830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
1840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                for (Map.Entry<Item, Rect> afterEntry : after.entrySet()) {
1850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    final Item afterItem = afterEntry.getKey();
1860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    if (afterItem.mAdapterIndex == beforeItem.mAdapterIndex) {
1870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        afterRect = afterEntry.getValue();
1880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        break;
1890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
1900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
1910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertNotNull(message + ": Item with same adapter index should be visible " +
1920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                "after simple re-layout",
1930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        afterRect);
1940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
1950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertEquals(message + ": Item should be laid out at the same coordinates",
1960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    entry.getValue(),
1970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    afterRect);
1980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
1990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    protected void assertViewPositions(Config config) {
2020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        ArrayList<ArrayList<View>> viewsBySpan = mLayoutManager.collectChildrenBySpan();
2030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        OrientationHelper orientationHelper = OrientationHelper
2040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                .createOrientationHelper(mLayoutManager, config.mOrientation);
2050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        for (ArrayList<View> span : viewsBySpan) {
2060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            // validate all children's order. first child should have min start mPosition
2070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int count = span.size();
2080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0, j = 1; j < count; i++, j++) {
2090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                View prev = span.get(i);
2100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                View next = span.get(j);
2110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertTrue(config + " prev item should be above next item",
2120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        orientationHelper.getDecoratedEnd(prev) <= orientationHelper
2130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                .getDecoratedStart(next)
2140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                );
2150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    protected TargetTuple findInvisibleTarget(Config config) {
2210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int minPosition = Integer.MAX_VALUE, maxPosition = Integer.MIN_VALUE;
2220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
2230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            View child = mLayoutManager.getChildAt(i);
2240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            int position = mRecyclerView.getChildLayoutPosition(child);
2250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (position < minPosition) {
2260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                minPosition = position;
2270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (position > maxPosition) {
2290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                maxPosition = position;
2300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int tailTarget = maxPosition + (mAdapter.getItemCount() - maxPosition) / 2;
2330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int headTarget = minPosition / 2;
2340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int target;
2350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // where will the child come from ?
2360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int itemLayoutDirection;
2370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (Math.abs(tailTarget - maxPosition) > Math.abs(headTarget - minPosition)) {
2380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            target = tailTarget;
2390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            itemLayoutDirection = config.mReverseLayout ? LAYOUT_START : LAYOUT_END;
2400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        } else {
2410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            target = headTarget;
2420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            itemLayoutDirection = config.mReverseLayout ? LAYOUT_END : LAYOUT_START;
2430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (DEBUG) {
2450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Log.d(TAG,
2460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    config + " target:" + target + " min:" + minPosition + ", max:" + maxPosition);
2470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        return new TargetTuple(target, itemLayoutDirection);
2490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    protected void scrollToPositionWithOffset(final int position, final int offset)
2520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            throws Throwable {
25342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
2550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void run() {
2560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mLayoutManager.scrollToPositionWithOffset(position, offset);
2570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        });
2590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    static class OnLayoutListener {
2620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        void before(RecyclerView.Recycler recycler, RecyclerView.State state) {
2640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        void after(RecyclerView.Recycler recycler, RecyclerView.State state) {
2670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    static class VisibleChildren {
2710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int[] firstVisiblePositions;
2730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int[] firstFullyVisiblePositions;
2750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int[] lastVisiblePositions;
2770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int[] lastFullyVisiblePositions;
2790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        View findFirstPartialVisibleClosestToStart;
2810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        View findFirstPartialVisibleClosestToEnd;
2820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        VisibleChildren(int spanCount) {
2840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            firstFullyVisiblePositions = new int[spanCount];
2850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            firstVisiblePositions = new int[spanCount];
2860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            lastVisiblePositions = new int[spanCount];
2870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            lastFullyVisiblePositions = new int[spanCount];
2880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0; i < spanCount; i++) {
2890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                firstFullyVisiblePositions[i] = RecyclerView.NO_POSITION;
2900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                firstVisiblePositions[i] = RecyclerView.NO_POSITION;
2910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lastVisiblePositions[i] = RecyclerView.NO_POSITION;
2920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lastFullyVisiblePositions[i] = RecyclerView.NO_POSITION;
2930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
2970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public boolean equals(Object o) {
2980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (this == o) {
2990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return true;
3000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (o == null || getClass() != o.getClass()) {
3020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            VisibleChildren that = (VisibleChildren) o;
3060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (!Arrays.equals(firstFullyVisiblePositions, that.firstFullyVisiblePositions)) {
3080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (findFirstPartialVisibleClosestToStart
3110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    != null ? !findFirstPartialVisibleClosestToStart
3120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    .equals(that.findFirstPartialVisibleClosestToStart)
3130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    : that.findFirstPartialVisibleClosestToStart != null) {
3140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (!Arrays.equals(firstVisiblePositions, that.firstVisiblePositions)) {
3170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (!Arrays.equals(lastFullyVisiblePositions, that.lastFullyVisiblePositions)) {
3200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (findFirstPartialVisibleClosestToEnd != null ? !findFirstPartialVisibleClosestToEnd
3230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    .equals(that.findFirstPartialVisibleClosestToEnd)
3240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    : that.findFirstPartialVisibleClosestToEnd
3250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            != null) {
3260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (!Arrays.equals(lastVisiblePositions, that.lastVisiblePositions)) {
3290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return true;
3330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
3360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public int hashCode() {
3370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            int result = Arrays.hashCode(firstVisiblePositions);
3380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            result = 31 * result + Arrays.hashCode(firstFullyVisiblePositions);
3390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            result = 31 * result + Arrays.hashCode(lastVisiblePositions);
3400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            result = 31 * result + Arrays.hashCode(lastFullyVisiblePositions);
3410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            result = 31 * result + (findFirstPartialVisibleClosestToStart != null
3420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ? findFirstPartialVisibleClosestToStart
3430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    .hashCode() : 0);
3440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            result = 31 * result + (findFirstPartialVisibleClosestToEnd != null
3450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ? findFirstPartialVisibleClosestToEnd
3460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    .hashCode()
3470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    : 0);
3480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return result;
3490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
3520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public String toString() {
3530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return "VisibleChildren{" +
3540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    "firstVisiblePositions=" + Arrays.toString(firstVisiblePositions) +
3550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ", firstFullyVisiblePositions=" + Arrays.toString(firstFullyVisiblePositions) +
3560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ", lastVisiblePositions=" + Arrays.toString(lastVisiblePositions) +
3570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ", lastFullyVisiblePositions=" + Arrays.toString(lastFullyVisiblePositions) +
3580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ", findFirstPartialVisibleClosestToStart=" +
3590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    viewToString(findFirstPartialVisibleClosestToStart) +
3600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ", findFirstPartialVisibleClosestToEnd=" +
3610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    viewToString(findFirstPartialVisibleClosestToEnd) +
3620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    '}';
3630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        private String viewToString(View view) {
3660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (view == null) {
3670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return null;
3680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            ViewGroup.LayoutParams lp = view.getLayoutParams();
3700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (lp instanceof RecyclerView.LayoutParams == false) {
3710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return System.identityHashCode(view) + "(?)";
3720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            RecyclerView.LayoutParams rvlp = (RecyclerView.LayoutParams) lp;
3740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return System.identityHashCode(view) + "(" + rvlp.getViewAdapterPosition() + ")";
3750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
3770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    abstract static class OnBindCallback {
3790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        abstract void onBoundItem(TestViewHolder vh, int position);
3810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        boolean assignRandomSize() {
3830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return true;
3840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        void onCreatedViewHolder(TestViewHolder vh) {
3870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
3890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    static class Config implements Cloneable {
3910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        static final int DEFAULT_ITEM_COUNT = 300;
3930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mOrientation = OrientationHelper.VERTICAL;
3950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        boolean mReverseLayout = false;
3970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mSpanCount = 3;
3990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mGapStrategy = GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS;
4010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mItemCount = DEFAULT_ITEM_COUNT;
4030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        boolean mWrap = false;
4054143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
4060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Config(int orientation, boolean reverseLayout, int spanCount, int gapStrategy) {
4070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mOrientation = orientation;
4080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mReverseLayout = reverseLayout;
4090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mSpanCount = spanCount;
4100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mGapStrategy = gapStrategy;
4110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public Config() {
4140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Config orientation(int orientation) {
4180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mOrientation = orientation;
4190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return this;
4200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Config reverseLayout(boolean reverseLayout) {
4230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mReverseLayout = reverseLayout;
4240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return this;
4250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Config spanCount(int spanCount) {
4280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mSpanCount = spanCount;
4290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return this;
4300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Config gapStrategy(int gapStrategy) {
4330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mGapStrategy = gapStrategy;
4340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return this;
4350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public Config itemCount(int itemCount) {
4380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mItemCount = itemCount;
4390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return this;
4400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4424143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        public Config wrap(boolean wrap) {
4434143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            mWrap = wrap;
4444143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            return this;
4454143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        }
4464143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
4470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
4480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public String toString() {
449210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikas            return "[CONFIG:"
450210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikas                    + "span:" + mSpanCount
451210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikas                    + ",orientation:" + (mOrientation == HORIZONTAL ? "horz," : "vert,")
452210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikas                    + ",reverse:" + (mReverseLayout ? "T" : "F")
453210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikas                    + ",itemCount:" + mItemCount
454210acb1081c292500390aaeb7a3eba249933dd9fAurimas Liutikas                    + ",wrapContent:" + mWrap
45531f688b7ce44ea1855b2f2d13680977611987583Aurimas Liutikas                    + ",gap_strategy:" + gapStrategyName(mGapStrategy);
4560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        protected static String gapStrategyName(int gapStrategy) {
4590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            switch (gapStrategy) {
4600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                case GAP_HANDLING_NONE:
4610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    return "none";
4620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                case GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS:
46331f688b7ce44ea1855b2f2d13680977611987583Aurimas Liutikas                    return "move_spans";
4640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
46531f688b7ce44ea1855b2f2d13680977611987583Aurimas Liutikas            return "gap_strategy:unknown";
4660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
4690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public Object clone() throws CloneNotSupportedException {
4700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return super.clone();
4710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
4730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    class WrappedLayoutManager extends StaggeredGridLayoutManager {
4750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        CountDownLatch layoutLatch;
4771ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        CountDownLatch prefetchLatch;
4780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        OnLayoutListener mOnLayoutListener;
4790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // gradle does not yet let us customize manifest for tests which is necessary to test RTL.
4800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // until bug is fixed, we'll fake it.
4810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // public issue id: 57819
4820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Boolean mFakeRTL;
483a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska        CountDownLatch mSnapLatch;
4840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
4860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        boolean isLayoutRTL() {
4870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return mFakeRTL == null ? super.isLayoutRTL() : mFakeRTL;
4880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public void expectLayouts(int count) {
4910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            layoutLatch = new CountDownLatch(count);
4920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
49466826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        public void waitForLayout(int seconds) throws Throwable {
49566826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar            layoutLatch.await(seconds * (DEBUG ? 100 : 1), SECONDS);
496bc61003e6a10872c23f0de155456cb2fbeef31a7Yigit Boyar            checkForMainThreadException();
49766826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar            MatcherAssert.assertThat("all layouts should complete on time",
49866826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar                    layoutLatch.getCount(), CoreMatchers.is(0L));
49966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar            // use a runnable to ensure RV layout is finished
50066826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar            getInstrumentation().runOnMainSync(new Runnable() {
50166826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar                @Override
50266826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar                public void run() {
50366826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar                }
50466826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar            });
5050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
5060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5071ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        public void expectPrefetch(int count) {
5081ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            prefetchLatch = new CountDownLatch(count);
5091ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        }
5101ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik
5111ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        public void waitForPrefetch(int seconds) throws Throwable {
5121ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            prefetchLatch.await(seconds * (DEBUG ? 100 : 1), SECONDS);
5131ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            checkForMainThreadException();
5141ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            MatcherAssert.assertThat("all prefetches should complete on time",
5151ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik                    prefetchLatch.getCount(), CoreMatchers.is(0L));
5161ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            // use a runnable to ensure RV layout is finished
5171ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            getInstrumentation().runOnMainSync(new Runnable() {
5181ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik                @Override
5191ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik                public void run() {
5201ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik                }
5211ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            });
5221ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        }
5231ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik
524c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        public void expectIdleState(int count) {
525a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska            mSnapLatch = new CountDownLatch(count);
526c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
527c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                @Override
528c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
529c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                    super.onScrollStateChanged(recyclerView, newState);
530c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
531a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska                        mSnapLatch.countDown();
532a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska                        if (mSnapLatch.getCount() == 0L) {
533c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                            mRecyclerView.removeOnScrollListener(this);
534c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                        }
535c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                    }
536c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                }
537c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            });
538c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
539c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
540c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        public void waitForSnap(int seconds) throws Throwable {
541a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska            mSnapLatch.await(seconds * (DEBUG ? 100 : 1), SECONDS);
542c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            checkForMainThreadException();
543c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            MatcherAssert.assertThat("all scrolling should complete on time",
544a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska                    mSnapLatch.getCount(), CoreMatchers.is(0L));
545c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            // use a runnable to ensure RV layout is finished
546c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            getInstrumentation().runOnMainSync(new Runnable() {
547c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                @Override
548c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                public void run() {
549c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                }
550c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            });
551c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
552c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
5530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public void assertNoLayout(String msg, long timeout) throws Throwable {
5540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            layoutLatch.await(timeout, TimeUnit.SECONDS);
5550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertFalse(msg, layoutLatch.getCount() == 0);
5560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
5570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
5590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
5600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            String before;
5610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (DEBUG) {
5620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                before = layoutToString("before");
5630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
5640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                before = "enable DEBUG";
5650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
5660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            try {
5670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (mOnLayoutListener != null) {
5680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    mOnLayoutListener.before(recycler, state);
5690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
5700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                super.onLayoutChildren(recycler, state);
5710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (mOnLayoutListener != null) {
5720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    mOnLayoutListener.after(recycler, state);
5730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
5740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                validateChildren(before);
5750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } catch (Throwable t) {
5760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                postExceptionToInstrumentation(t);
5770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
5780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            layoutLatch.countDown();
5800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
5810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
5830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state) {
5840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            try {
5850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                int result = super.scrollBy(dt, recycler, state);
5860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                validateChildren();
5870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return result;
5880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } catch (Throwable t) {
5890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                postExceptionToInstrumentation(t);
5900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
5910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return 0;
5930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
5940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
595c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View findFirstVisibleItemClosestToCenter() {
596c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            final int boundsStart = mPrimaryOrientation.getStartAfterPadding();
597c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            final int boundsEnd = mPrimaryOrientation.getEndAfterPadding();
598c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            final int boundsCenter = (boundsStart + boundsEnd) / 2;
599c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            final Rect childBounds = new Rect();
600c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            int minDist = Integer.MAX_VALUE;
601c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            View closestChild = null;
602c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            for (int i = getChildCount() - 1; i >= 0; i--) {
603c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                final View child = getChildAt(i);
604c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                childBounds.setEmpty();
605c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                getDecoratedBoundsWithMargins(child, childBounds);
606c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                int childCenter = canScrollHorizontally()
607c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                        ? childBounds.centerX() : childBounds.centerY();
608c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                int dist = Math.abs(boundsCenter - childCenter);
609c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                if (dist < minDist) {
610c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                    minDist = dist;
611c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                    closestChild = child;
612c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                }
613c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            }
614c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            return closestChild;
615c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
616c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
6170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public WrappedLayoutManager(int spanCount, int orientation) {
6180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            super(spanCount, orientation);
6190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        ArrayList<ArrayList<View>> collectChildrenBySpan() {
6220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            ArrayList<ArrayList<View>> viewsBySpan = new ArrayList<ArrayList<View>>();
6230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0; i < getSpanCount(); i++) {
6240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                viewsBySpan.add(new ArrayList<View>());
6250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0; i < getChildCount(); i++) {
6270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                View view = getChildAt(i);
6280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                LayoutParams lp
6290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        = (LayoutParams) view
6300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .getLayoutParams();
6310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                viewsBySpan.get(lp.mSpan.mIndex).add(view);
6320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return viewsBySpan;
6340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Nullable
6370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
6380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public View onFocusSearchFailed(View focused, int direction, RecyclerView.Recycler recycler,
6390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                RecyclerView.State state) {
6400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            View result = null;
6410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            try {
6420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                result = super.onFocusSearchFailed(focused, direction, recycler, state);
6430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                validateChildren();
6440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } catch (Throwable t) {
6450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                postExceptionToInstrumentation(t);
6460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return result;
6480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Rect getViewBounds(View view) {
6510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (getOrientation() == HORIZONTAL) {
6520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return new Rect(
6530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedStart(view),
6540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mSecondaryOrientation.getDecoratedStart(view),
6550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedEnd(view),
6560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mSecondaryOrientation.getDecoratedEnd(view));
6570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
6580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return new Rect(
6590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mSecondaryOrientation.getDecoratedStart(view),
6600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedStart(view),
6610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mSecondaryOrientation.getDecoratedEnd(view),
6620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedEnd(view));
6630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public String getBoundsLog() {
6670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            StringBuilder sb = new StringBuilder();
6680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            sb.append("view bounds:[start:").append(mPrimaryOrientation.getStartAfterPadding())
6690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    .append(",").append(" end").append(mPrimaryOrientation.getEndAfterPadding());
6700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            sb.append("\nchildren bounds\n");
6710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int childCount = getChildCount();
6720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0; i < childCount; i++) {
6730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                View child = getChildAt(i);
6740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child))
6750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .append("[").append("start:").append(
6760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedStart(child)).append(", end:")
6770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .append(mPrimaryOrientation.getDecoratedEnd(child)).append("]\n");
6780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return sb.toString();
6800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public VisibleChildren traverseAndFindVisibleChildren() {
6830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            int childCount = getChildCount();
6840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final VisibleChildren visibleChildren = new VisibleChildren(getSpanCount());
6850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int start = mPrimaryOrientation.getStartAfterPadding();
6860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int end = mPrimaryOrientation.getEndAfterPadding();
6870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0; i < childCount; i++) {
6880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                View child = getChildAt(i);
6890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int childStart = mPrimaryOrientation.getDecoratedStart(child);
6900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int childEnd = mPrimaryOrientation.getDecoratedEnd(child);
6910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final boolean fullyVisible = childStart >= start && childEnd <= end;
6920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final boolean hidden = childEnd <= start || childStart >= end;
6930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (hidden) {
6940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    continue;
6950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
6960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int position = getPosition(child);
6970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int span = getLp(child).getSpanIndex();
6980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (fullyVisible) {
6990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    if (position < visibleChildren.firstFullyVisiblePositions[span] ||
7000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            visibleChildren.firstFullyVisiblePositions[span]
7010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                    == RecyclerView.NO_POSITION) {
7020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        visibleChildren.firstFullyVisiblePositions[span] = position;
7030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
7040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    if (position > visibleChildren.lastFullyVisiblePositions[span]) {
7060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        visibleChildren.lastFullyVisiblePositions[span] = position;
7070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
7080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (position < visibleChildren.firstVisiblePositions[span] ||
7110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        visibleChildren.firstVisiblePositions[span] == RecyclerView.NO_POSITION) {
7120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    visibleChildren.firstVisiblePositions[span] = position;
7130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (position > visibleChildren.lastVisiblePositions[span]) {
7160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    visibleChildren.lastVisiblePositions[span] = position;
7170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (visibleChildren.findFirstPartialVisibleClosestToStart == null) {
7190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    visibleChildren.findFirstPartialVisibleClosestToStart = child;
7200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                visibleChildren.findFirstPartialVisibleClosestToEnd = child;
7220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return visibleChildren;
7240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
7250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Map<Item, Rect> collectChildCoordinates() throws Throwable {
7270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>();
72842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas            mActivityRule.runOnUiThread(new Runnable() {
7290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                @Override
7300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                public void run() {
731febbd2db61780fb43c70b036a598b63a3465c7c4Yigit Boyar                    final int start = mPrimaryOrientation.getStartAfterPadding();
732febbd2db61780fb43c70b036a598b63a3465c7c4Yigit Boyar                    final int end = mPrimaryOrientation.getEndAfterPadding();
7330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    final int childCount = getChildCount();
7340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    for (int i = 0; i < childCount; i++) {
7350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        View child = getChildAt(i);
736febbd2db61780fb43c70b036a598b63a3465c7c4Yigit Boyar                        // ignore child if it fits the recycling constraints
737febbd2db61780fb43c70b036a598b63a3465c7c4Yigit Boyar                        if (mPrimaryOrientation.getDecoratedStart(child) >= end
738febbd2db61780fb43c70b036a598b63a3465c7c4Yigit Boyar                                || mPrimaryOrientation.getDecoratedEnd(child) < start) {
7390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            continue;
7400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        }
741febbd2db61780fb43c70b036a598b63a3465c7c4Yigit Boyar                        LayoutParams lp = (LayoutParams) child.getLayoutParams();
7420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        TestViewHolder vh = (TestViewHolder) lp.mViewHolder;
7430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        items.put(vh.mBoundItem, getViewBounds(child));
7440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
7450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            });
7470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return items;
7480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
7490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public void setFakeRtl(Boolean fakeRtl) {
7520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mFakeRTL = fakeRtl;
7530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            try {
7540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                requestLayoutOnUIThread(mRecyclerView);
7550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } catch (Throwable throwable) {
7560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                postExceptionToInstrumentation(throwable);
7570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
7590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        String layoutToString(String hint) {
7610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            StringBuilder sb = new StringBuilder();
7620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            sb.append("LAYOUT POSITIONS AND INDICES ").append(hint).append("\n");
7630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0; i < getChildCount(); i++) {
7640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final View view = getChildAt(i);
7650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
7660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                sb.append(String.format("index: %d pos: %d top: %d bottom: %d span: %d isFull:%s",
7670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        i, getPosition(view),
7680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedStart(view),
7690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedEnd(view),
7700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        layoutParams.getSpanIndex(), layoutParams.isFullSpan())).append("\n");
7710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return sb.toString();
7730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
7740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        protected void validateChildren() {
7760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            validateChildren(null);
7770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
7780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        private void validateChildren(String msg) {
7800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (getChildCount() == 0 || mRecyclerView.mState.isPreLayout()) {
7810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return;
7820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int dir = mShouldReverseLayout ? -1 : 1;
7840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            int i = 0;
7850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            int pos = -1;
7860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            while (i < getChildCount()) {
7870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
7880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (lp.isItemRemoved()) {
7890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    i++;
7900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    continue;
7910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                pos = getPosition(getChildAt(i));
7930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                break;
7940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (pos == -1) {
7960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return;
7970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            while (++i < getChildCount()) {
7990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
8000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (lp.isItemRemoved()) {
8010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    continue;
8020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
8030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                pos += dir;
8040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (getPosition(getChildAt(i)) != pos) {
8050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    throw new RuntimeException("INVALID POSITION FOR CHILD " + i + "\n" +
8060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            layoutToString("ERROR") + "\n msg:" + msg);
8070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
8080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
8090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
8101ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik
8111ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        @Override
8121e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
8133104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik                LayoutPrefetchRegistry layoutPrefetchRegistry) {
8141ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            if (prefetchLatch != null) prefetchLatch.countDown();
8153104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik            super.collectAdjacentPrefetchPositions(dx, dy, state, layoutPrefetchRegistry);
8161ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        }
8170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
8180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    class GridTestAdapter extends TestAdapter {
8200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mOrientation;
8220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mRecyclerViewWidth;
8230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mRecyclerViewHeight;
8240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Integer mSizeReference = null;
8250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // original ids of items that should be full span
8270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        HashSet<Integer> mFullSpanItems = new HashSet<Integer>();
8280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        protected boolean mViewsHaveEqualSize = false; // size in the scrollable direction
8300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        protected OnBindCallback mOnBindCallback;
8320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        GridTestAdapter(int count, int orientation) {
8340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            super(count);
8350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mOrientation = orientation;
8360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
8370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
8390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public TestViewHolder onCreateViewHolder(ViewGroup parent,
8400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                int viewType) {
8410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mRecyclerViewWidth = parent.getWidth();
8420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mRecyclerViewHeight = parent.getHeight();
8430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            TestViewHolder vh = super.onCreateViewHolder(parent, viewType);
8440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (mOnBindCallback != null) {
8450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mOnBindCallback.onCreatedViewHolder(vh);
8460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
8470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return vh;
8480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
8490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
8510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public void offsetOriginalIndices(int start, int offset) {
8520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (mFullSpanItems.size() > 0) {
8530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                HashSet<Integer> old = mFullSpanItems;
8540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mFullSpanItems = new HashSet<Integer>();
8550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                for (Integer i : old) {
8560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    if (i < start) {
8570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mFullSpanItems.add(i);
8580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    } else if (offset > 0 || (start + Math.abs(offset)) <= i) {
8590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mFullSpanItems.add(i + offset);
8600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    } else if (DEBUG) {
8610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        Log.d(TAG, "removed full span item " + i);
8620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
8630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
8640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
8650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            super.offsetOriginalIndices(start, offset);
8660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
8670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
8690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        protected void moveInUIThread(int from, int to) {
8700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            boolean setAsFullSpanAgain = mFullSpanItems.contains(from);
8710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            super.moveInUIThread(from, to);
8720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (setAsFullSpanAgain) {
8730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mFullSpanItems.add(to);
8740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
8750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
8760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
8780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public void onBindViewHolder(TestViewHolder holder,
8790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                int position) {
8800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (mSizeReference == null) {
8810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mSizeReference = mOrientation == OrientationHelper.HORIZONTAL ? mRecyclerViewWidth
8820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        / AVG_ITEM_PER_VIEW : mRecyclerViewHeight / AVG_ITEM_PER_VIEW;
8830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
8840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            super.onBindViewHolder(holder, position);
885a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska
8860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Item item = mItems.get(position);
8870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) holder.itemView
8880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    .getLayoutParams();
8890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (lp instanceof StaggeredGridLayoutManager.LayoutParams) {
8904143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                ((StaggeredGridLayoutManager.LayoutParams) lp)
8914143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                        .setFullSpan(mFullSpanItems.contains(item.mAdapterIndex));
8920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
8934143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                StaggeredGridLayoutManager.LayoutParams slp
894a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska                    = (StaggeredGridLayoutManager.LayoutParams) mLayoutManager
895a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska                    .generateDefaultLayoutParams();
8960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                holder.itemView.setLayoutParams(slp);
8970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                slp.setFullSpan(mFullSpanItems.contains(item.mAdapterIndex));
8980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lp = slp;
8990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
9000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
9010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (mOnBindCallback == null || mOnBindCallback.assignRandomSize()) {
9020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int minSize = mViewsHaveEqualSize ? mSizeReference :
9030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mSizeReference + 20 * (item.mId % 10);
9040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (mOrientation == OrientationHelper.HORIZONTAL) {
9050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    holder.itemView.setMinimumWidth(minSize);
9060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                } else {
9070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    holder.itemView.setMinimumHeight(minSize);
9080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
9090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lp.topMargin = 3;
9100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lp.leftMargin = 5;
9110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lp.rightMargin = 7;
9120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lp.bottomMargin = 9;
9130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
9149c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri            // Good to have colors for debugging
9159c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri            StateListDrawable stl = new StateListDrawable();
9169c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri            stl.addState(new int[]{android.R.attr.state_focused},
9179c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    new ColorDrawable(Color.RED));
9189c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri            stl.addState(StateSet.WILD_CARD, new ColorDrawable(Color.BLUE));
9199c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri            //noinspection deprecation using this for kitkat tests
9209c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri            holder.itemView.setBackgroundDrawable(stl);
9210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (mOnBindCallback != null) {
9220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mOnBindCallback.onBoundItem(holder, position);
9230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
9240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
9250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
9260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar}
927