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;
841ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        mRecyclerView = new WrappedRecyclerView(getActivity());
850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mRecyclerView.setAdapter(mAdapter);
860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mRecyclerView.setHasFixedSize(true);
87a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska        mLayoutManager = new WrappedLayoutManager(config.mSpanCount, config.mOrientation);
880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mLayoutManager.setGapStrategy(config.mGapStrategy);
890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mLayoutManager.setReverseLayout(config.mReverseLayout);
900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mRecyclerView.setLayoutManager(mLayoutManager);
910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    RecyclerView.State state) {
950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                try {
960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    StaggeredGridLayoutManager.LayoutParams
970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    assertNotNull("view should have layout params assigned", lp);
990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    assertNotNull("when item offsets are requested, view should have a valid span",
1000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            lp.mSpan);
1010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                } catch (Throwable t) {
1020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    postExceptionToInstrumentation(t);
1030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
1040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
1050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        });
1060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    StaggeredGridLayoutManager.LayoutParams getLp(View view) {
1090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        return (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
1100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    void waitFirstLayout() throws Throwable {
1130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mLayoutManager.expectLayouts(1);
1140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        setRecyclerView(mRecyclerView);
1158cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar        mLayoutManager.waitForLayout(3);
1160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        getInstrumentation().waitForIdleSync();
1170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    /**
1200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar     * enqueues an empty runnable to main thread so that we can be assured it did run
1210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar     *
1220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar     * @param count Number of times to run
1230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar     */
1240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    protected void waitForMainThread(int count) throws Throwable {
1250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final AtomicInteger i = new AtomicInteger(count);
1260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        while (i.get() > 0) {
1270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            runTestOnUiThread(new Runnable() {
1280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                @Override
1290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                public void run() {
1300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    i.decrementAndGet();
1310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
1320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            });
1330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
1340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void assertRectSetsNotEqual(String message, Map<Item, Rect> before,
1370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Map<Item, Rect> after) {
1380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Throwable throwable = null;
1390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        try {
1400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertRectSetsEqual("NOT " + message, before, after);
1410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        } catch (Throwable t) {
1420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            throwable = t;
1430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
1440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertNotNull(message + " two layout should be different", throwable);
1450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) {
1480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertRectSetsEqual(message, before, after, true);
1490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after,
1520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            boolean strictItemEquality) {
1530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        StringBuilder log = new StringBuilder();
1540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (DEBUG) {
1550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            log.append("checking rectangle equality.\n");
1560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            log.append("total space:" + mLayoutManager.mPrimaryOrientation.getTotalSpace());
1570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            log.append("before:");
1580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (Map.Entry<Item, Rect> entry : before.entrySet()) {
1590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                log.append("\n").append(entry.getKey().mAdapterIndex).append(":")
1600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .append(entry.getValue());
1610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
1620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            log.append("\nafter:");
1630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (Map.Entry<Item, Rect> entry : after.entrySet()) {
1640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                log.append("\n").append(entry.getKey().mAdapterIndex).append(":")
1650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .append(entry.getValue());
1660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
1670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            message += "\n\n" + log.toString();
1680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
1690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertEquals(message + ": item counts should be equal", before.size()
1700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                , after.size());
1710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
1720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final Item beforeItem = entry.getKey();
1730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Rect afterRect = null;
1740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (strictItemEquality) {
1750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                afterRect = after.get(beforeItem);
1760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertNotNull(message + ": Same item should be visible after simple re-layout",
1770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        afterRect);
1780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
1790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                for (Map.Entry<Item, Rect> afterEntry : after.entrySet()) {
1800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    final Item afterItem = afterEntry.getKey();
1810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    if (afterItem.mAdapterIndex == beforeItem.mAdapterIndex) {
1820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        afterRect = afterEntry.getValue();
1830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        break;
1840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
1850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
1860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertNotNull(message + ": Item with same adapter index should be visible " +
1870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                "after simple re-layout",
1880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        afterRect);
1890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
1900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertEquals(message + ": Item should be laid out at the same coordinates",
1910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    entry.getValue(),
1920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    afterRect);
1930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
1940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    protected void assertViewPositions(Config config) {
1970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        ArrayList<ArrayList<View>> viewsBySpan = mLayoutManager.collectChildrenBySpan();
1980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        OrientationHelper orientationHelper = OrientationHelper
1990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                .createOrientationHelper(mLayoutManager, config.mOrientation);
2000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        for (ArrayList<View> span : viewsBySpan) {
2010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            // validate all children's order. first child should have min start mPosition
2020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int count = span.size();
2030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0, j = 1; j < count; i++, j++) {
2040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                View prev = span.get(i);
2050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                View next = span.get(j);
2060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertTrue(config + " prev item should be above next item",
2070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        orientationHelper.getDecoratedEnd(prev) <= orientationHelper
2080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                .getDecoratedStart(next)
2090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                );
2100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    protected TargetTuple findInvisibleTarget(Config config) {
2160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int minPosition = Integer.MAX_VALUE, maxPosition = Integer.MIN_VALUE;
2170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
2180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            View child = mLayoutManager.getChildAt(i);
2190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            int position = mRecyclerView.getChildLayoutPosition(child);
2200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (position < minPosition) {
2210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                minPosition = position;
2220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (position > maxPosition) {
2240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                maxPosition = position;
2250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int tailTarget = maxPosition + (mAdapter.getItemCount() - maxPosition) / 2;
2280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int headTarget = minPosition / 2;
2290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int target;
2300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // where will the child come from ?
2310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int itemLayoutDirection;
2320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (Math.abs(tailTarget - maxPosition) > Math.abs(headTarget - minPosition)) {
2330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            target = tailTarget;
2340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            itemLayoutDirection = config.mReverseLayout ? LAYOUT_START : LAYOUT_END;
2350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        } else {
2360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            target = headTarget;
2370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            itemLayoutDirection = config.mReverseLayout ? LAYOUT_END : LAYOUT_START;
2380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (DEBUG) {
2400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Log.d(TAG,
2410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    config + " target:" + target + " min:" + minPosition + ", max:" + maxPosition);
2420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        return new TargetTuple(target, itemLayoutDirection);
2440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    protected void scrollToPositionWithOffset(final int position, final int offset)
2470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            throws Throwable {
2480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        runTestOnUiThread(new Runnable() {
2490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
2500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void run() {
2510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mLayoutManager.scrollToPositionWithOffset(position, offset);
2520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        });
2540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    static class OnLayoutListener {
2570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        void before(RecyclerView.Recycler recycler, RecyclerView.State state) {
2590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        void after(RecyclerView.Recycler recycler, RecyclerView.State state) {
2620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    static class VisibleChildren {
2660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int[] firstVisiblePositions;
2680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int[] firstFullyVisiblePositions;
2700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int[] lastVisiblePositions;
2720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int[] lastFullyVisiblePositions;
2740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        View findFirstPartialVisibleClosestToStart;
2760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        View findFirstPartialVisibleClosestToEnd;
2770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        VisibleChildren(int spanCount) {
2790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            firstFullyVisiblePositions = new int[spanCount];
2800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            firstVisiblePositions = new int[spanCount];
2810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            lastVisiblePositions = new int[spanCount];
2820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            lastFullyVisiblePositions = new int[spanCount];
2830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0; i < spanCount; i++) {
2840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                firstFullyVisiblePositions[i] = RecyclerView.NO_POSITION;
2850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                firstVisiblePositions[i] = RecyclerView.NO_POSITION;
2860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lastVisiblePositions[i] = RecyclerView.NO_POSITION;
2870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lastFullyVisiblePositions[i] = RecyclerView.NO_POSITION;
2880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
2920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public boolean equals(Object o) {
2930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (this == o) {
2940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return true;
2950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (o == null || getClass() != o.getClass()) {
2970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
2980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            VisibleChildren that = (VisibleChildren) o;
3010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (!Arrays.equals(firstFullyVisiblePositions, that.firstFullyVisiblePositions)) {
3030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (findFirstPartialVisibleClosestToStart
3060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    != null ? !findFirstPartialVisibleClosestToStart
3070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    .equals(that.findFirstPartialVisibleClosestToStart)
3080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    : that.findFirstPartialVisibleClosestToStart != null) {
3090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (!Arrays.equals(firstVisiblePositions, that.firstVisiblePositions)) {
3120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (!Arrays.equals(lastFullyVisiblePositions, that.lastFullyVisiblePositions)) {
3150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (findFirstPartialVisibleClosestToEnd != null ? !findFirstPartialVisibleClosestToEnd
3180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    .equals(that.findFirstPartialVisibleClosestToEnd)
3190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    : that.findFirstPartialVisibleClosestToEnd
3200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            != null) {
3210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (!Arrays.equals(lastVisiblePositions, that.lastVisiblePositions)) {
3240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
3250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return true;
3280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
3310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public int hashCode() {
3320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            int result = Arrays.hashCode(firstVisiblePositions);
3330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            result = 31 * result + Arrays.hashCode(firstFullyVisiblePositions);
3340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            result = 31 * result + Arrays.hashCode(lastVisiblePositions);
3350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            result = 31 * result + Arrays.hashCode(lastFullyVisiblePositions);
3360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            result = 31 * result + (findFirstPartialVisibleClosestToStart != null
3370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ? findFirstPartialVisibleClosestToStart
3380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    .hashCode() : 0);
3390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            result = 31 * result + (findFirstPartialVisibleClosestToEnd != null
3400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ? findFirstPartialVisibleClosestToEnd
3410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    .hashCode()
3420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    : 0);
3430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return result;
3440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
3470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public String toString() {
3480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return "VisibleChildren{" +
3490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    "firstVisiblePositions=" + Arrays.toString(firstVisiblePositions) +
3500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ", firstFullyVisiblePositions=" + Arrays.toString(firstFullyVisiblePositions) +
3510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ", lastVisiblePositions=" + Arrays.toString(lastVisiblePositions) +
3520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ", lastFullyVisiblePositions=" + Arrays.toString(lastFullyVisiblePositions) +
3530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ", findFirstPartialVisibleClosestToStart=" +
3540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    viewToString(findFirstPartialVisibleClosestToStart) +
3550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    ", findFirstPartialVisibleClosestToEnd=" +
3560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    viewToString(findFirstPartialVisibleClosestToEnd) +
3570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    '}';
3580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        private String viewToString(View view) {
3610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (view == null) {
3620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return null;
3630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            ViewGroup.LayoutParams lp = view.getLayoutParams();
3650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (lp instanceof RecyclerView.LayoutParams == false) {
3660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return System.identityHashCode(view) + "(?)";
3670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            RecyclerView.LayoutParams rvlp = (RecyclerView.LayoutParams) lp;
3690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return System.identityHashCode(view) + "(" + rvlp.getViewAdapterPosition() + ")";
3700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
3720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    abstract static class OnBindCallback {
3740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        abstract void onBoundItem(TestViewHolder vh, int position);
3760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        boolean assignRandomSize() {
3780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return true;
3790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        void onCreatedViewHolder(TestViewHolder vh) {
3820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
3840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    static class Config implements Cloneable {
3860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        static final int DEFAULT_ITEM_COUNT = 300;
3880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mOrientation = OrientationHelper.VERTICAL;
3900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        boolean mReverseLayout = false;
3920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mSpanCount = 3;
3940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mGapStrategy = GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS;
3960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mItemCount = DEFAULT_ITEM_COUNT;
3980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3994143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        boolean mWrap = false;
4004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
4010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Config(int orientation, boolean reverseLayout, int spanCount, int gapStrategy) {
4020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mOrientation = orientation;
4030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mReverseLayout = reverseLayout;
4040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mSpanCount = spanCount;
4050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mGapStrategy = gapStrategy;
4060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public Config() {
4090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Config orientation(int orientation) {
4130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mOrientation = orientation;
4140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return this;
4150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Config reverseLayout(boolean reverseLayout) {
4180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mReverseLayout = reverseLayout;
4190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return this;
4200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Config spanCount(int spanCount) {
4230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mSpanCount = spanCount;
4240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return this;
4250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Config gapStrategy(int gapStrategy) {
4280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mGapStrategy = gapStrategy;
4290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return this;
4300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public Config itemCount(int itemCount) {
4330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mItemCount = itemCount;
4340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return this;
4350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4374143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        public Config wrap(boolean wrap) {
4384143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            mWrap = wrap;
4394143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            return this;
4404143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        }
4414143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
4420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
4430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public String toString() {
4440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return "[CONFIG:" +
4450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    " span:" + mSpanCount + "," +
4460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    " orientation:" + (mOrientation == HORIZONTAL ? "horz," : "vert,") +
4470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    " reverse:" + (mReverseLayout ? "T" : "F") +
4480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    " itemCount:" + mItemCount +
4494143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    " wrapContent:" + mWrap +
4500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    " gap strategy: " + gapStrategyName(mGapStrategy);
4510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        protected static String gapStrategyName(int gapStrategy) {
4540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            switch (gapStrategy) {
4550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                case GAP_HANDLING_NONE:
4560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    return "none";
4570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                case GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS:
4580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    return "move spans";
4590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
4600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return "gap strategy: unknown";
4610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
4640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public Object clone() throws CloneNotSupportedException {
4650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return super.clone();
4660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
4680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    class WrappedLayoutManager extends StaggeredGridLayoutManager {
4700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        CountDownLatch layoutLatch;
4721ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        CountDownLatch prefetchLatch;
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;
478a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska        CountDownLatch mSnapLatch;
4790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
4810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        boolean isLayoutRTL() {
4820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return mFakeRTL == null ? super.isLayoutRTL() : mFakeRTL;
4830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public void expectLayouts(int count) {
4860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            layoutLatch = new CountDownLatch(count);
4870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
48966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        public void waitForLayout(int seconds) throws Throwable {
49066826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar            layoutLatch.await(seconds * (DEBUG ? 100 : 1), SECONDS);
491bc61003e6a10872c23f0de155456cb2fbeef31a7Yigit Boyar            checkForMainThreadException();
49266826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar            MatcherAssert.assertThat("all layouts should complete on time",
49366826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar                    layoutLatch.getCount(), CoreMatchers.is(0L));
49466826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar            // use a runnable to ensure RV layout is finished
49566826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar            getInstrumentation().runOnMainSync(new Runnable() {
49666826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar                @Override
49766826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar                public void run() {
49866826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar                }
49966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar            });
5000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
5010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5021ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        public void expectPrefetch(int count) {
5031ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            prefetchLatch = new CountDownLatch(count);
5041ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        }
5051ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik
5061ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        public void waitForPrefetch(int seconds) throws Throwable {
5071ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            prefetchLatch.await(seconds * (DEBUG ? 100 : 1), SECONDS);
5081ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            checkForMainThreadException();
5091ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            MatcherAssert.assertThat("all prefetches should complete on time",
5101ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik                    prefetchLatch.getCount(), CoreMatchers.is(0L));
5111ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            // use a runnable to ensure RV layout is finished
5121ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            getInstrumentation().runOnMainSync(new Runnable() {
5131ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik                @Override
5141ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik                public void run() {
5151ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik                }
5161ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            });
5171ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        }
5181ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik
519c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        public void expectIdleState(int count) {
520a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska            mSnapLatch = new CountDownLatch(count);
521c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
522c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                @Override
523c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
524c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                    super.onScrollStateChanged(recyclerView, newState);
525c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
526a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska                        mSnapLatch.countDown();
527a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska                        if (mSnapLatch.getCount() == 0L) {
528c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                            mRecyclerView.removeOnScrollListener(this);
529c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                        }
530c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                    }
531c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                }
532c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            });
533c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
534c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
535c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        public void waitForSnap(int seconds) throws Throwable {
536a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska            mSnapLatch.await(seconds * (DEBUG ? 100 : 1), SECONDS);
537c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            checkForMainThreadException();
538c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            MatcherAssert.assertThat("all scrolling should complete on time",
539a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska                    mSnapLatch.getCount(), CoreMatchers.is(0L));
540c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            // use a runnable to ensure RV layout is finished
541c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            getInstrumentation().runOnMainSync(new Runnable() {
542c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                @Override
543c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                public void run() {
544c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                }
545c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            });
546c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
547c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
5480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public void assertNoLayout(String msg, long timeout) throws Throwable {
5490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            layoutLatch.await(timeout, TimeUnit.SECONDS);
5500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertFalse(msg, layoutLatch.getCount() == 0);
5510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
5520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
5540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
5550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            String before;
5560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (DEBUG) {
5570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                before = layoutToString("before");
5580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
5590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                before = "enable DEBUG";
5600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
5610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            try {
5620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (mOnLayoutListener != null) {
5630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    mOnLayoutListener.before(recycler, state);
5640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
5650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                super.onLayoutChildren(recycler, state);
5660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (mOnLayoutListener != null) {
5670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    mOnLayoutListener.after(recycler, state);
5680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
5690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                validateChildren(before);
5700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } catch (Throwable t) {
5710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                postExceptionToInstrumentation(t);
5720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
5730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            layoutLatch.countDown();
5750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
5760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
5780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state) {
5790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            try {
5800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                int result = super.scrollBy(dt, recycler, state);
5810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                validateChildren();
5820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return result;
5830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } catch (Throwable t) {
5840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                postExceptionToInstrumentation(t);
5850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
5860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return 0;
5880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
5890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
590c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        View findFirstVisibleItemClosestToCenter() {
591c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            final int boundsStart = mPrimaryOrientation.getStartAfterPadding();
592c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            final int boundsEnd = mPrimaryOrientation.getEndAfterPadding();
593c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            final int boundsCenter = (boundsStart + boundsEnd) / 2;
594c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            final Rect childBounds = new Rect();
595c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            int minDist = Integer.MAX_VALUE;
596c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            View closestChild = null;
597c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            for (int i = getChildCount() - 1; i >= 0; i--) {
598c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                final View child = getChildAt(i);
599c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                childBounds.setEmpty();
600c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                getDecoratedBoundsWithMargins(child, childBounds);
601c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                int childCenter = canScrollHorizontally()
602c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                        ? childBounds.centerX() : childBounds.centerY();
603c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                int dist = Math.abs(boundsCenter - childCenter);
604c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                if (dist < minDist) {
605c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                    minDist = dist;
606c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                    closestChild = child;
607c587f7dba5a337169e854e235da59f595255d6ccAga Madurska                }
608c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            }
609c587f7dba5a337169e854e235da59f595255d6ccAga Madurska            return closestChild;
610c587f7dba5a337169e854e235da59f595255d6ccAga Madurska        }
611c587f7dba5a337169e854e235da59f595255d6ccAga Madurska
6120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public WrappedLayoutManager(int spanCount, int orientation) {
6130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            super(spanCount, orientation);
6140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        ArrayList<ArrayList<View>> collectChildrenBySpan() {
6170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            ArrayList<ArrayList<View>> viewsBySpan = new ArrayList<ArrayList<View>>();
6180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0; i < getSpanCount(); i++) {
6190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                viewsBySpan.add(new ArrayList<View>());
6200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0; i < getChildCount(); i++) {
6220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                View view = getChildAt(i);
6230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                LayoutParams lp
6240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        = (LayoutParams) view
6250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .getLayoutParams();
6260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                viewsBySpan.get(lp.mSpan.mIndex).add(view);
6270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return viewsBySpan;
6290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Nullable
6320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
6330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public View onFocusSearchFailed(View focused, int direction, RecyclerView.Recycler recycler,
6340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                RecyclerView.State state) {
6350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            View result = null;
6360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            try {
6370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                result = super.onFocusSearchFailed(focused, direction, recycler, state);
6380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                validateChildren();
6390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } catch (Throwable t) {
6400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                postExceptionToInstrumentation(t);
6410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return result;
6430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Rect getViewBounds(View view) {
6460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (getOrientation() == HORIZONTAL) {
6470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return new Rect(
6480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedStart(view),
6490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mSecondaryOrientation.getDecoratedStart(view),
6500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedEnd(view),
6510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mSecondaryOrientation.getDecoratedEnd(view));
6520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
6530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return new Rect(
6540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mSecondaryOrientation.getDecoratedStart(view),
6550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedStart(view),
6560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mSecondaryOrientation.getDecoratedEnd(view),
6570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedEnd(view));
6580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public String getBoundsLog() {
6620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            StringBuilder sb = new StringBuilder();
6630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            sb.append("view bounds:[start:").append(mPrimaryOrientation.getStartAfterPadding())
6640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    .append(",").append(" end").append(mPrimaryOrientation.getEndAfterPadding());
6650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            sb.append("\nchildren bounds\n");
6660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int childCount = getChildCount();
6670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0; i < childCount; i++) {
6680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                View child = getChildAt(i);
6690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child))
6700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .append("[").append("start:").append(
6710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedStart(child)).append(", end:")
6720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .append(mPrimaryOrientation.getDecoratedEnd(child)).append("]\n");
6730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return sb.toString();
6750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public VisibleChildren traverseAndFindVisibleChildren() {
6780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            int childCount = getChildCount();
6790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final VisibleChildren visibleChildren = new VisibleChildren(getSpanCount());
6800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int start = mPrimaryOrientation.getStartAfterPadding();
6810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int end = mPrimaryOrientation.getEndAfterPadding();
6820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0; i < childCount; i++) {
6830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                View child = getChildAt(i);
6840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int childStart = mPrimaryOrientation.getDecoratedStart(child);
6850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int childEnd = mPrimaryOrientation.getDecoratedEnd(child);
6860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final boolean fullyVisible = childStart >= start && childEnd <= end;
6870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final boolean hidden = childEnd <= start || childStart >= end;
6880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (hidden) {
6890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    continue;
6900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
6910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int position = getPosition(child);
6920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int span = getLp(child).getSpanIndex();
6930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (fullyVisible) {
6940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    if (position < visibleChildren.firstFullyVisiblePositions[span] ||
6950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            visibleChildren.firstFullyVisiblePositions[span]
6960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                    == RecyclerView.NO_POSITION) {
6970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        visibleChildren.firstFullyVisiblePositions[span] = position;
6980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
6990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    if (position > visibleChildren.lastFullyVisiblePositions[span]) {
7010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        visibleChildren.lastFullyVisiblePositions[span] = position;
7020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
7030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (position < visibleChildren.firstVisiblePositions[span] ||
7060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        visibleChildren.firstVisiblePositions[span] == RecyclerView.NO_POSITION) {
7070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    visibleChildren.firstVisiblePositions[span] = position;
7080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (position > visibleChildren.lastVisiblePositions[span]) {
7110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    visibleChildren.lastVisiblePositions[span] = position;
7120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (visibleChildren.findFirstPartialVisibleClosestToStart == null) {
7140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    visibleChildren.findFirstPartialVisibleClosestToStart = child;
7150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                visibleChildren.findFirstPartialVisibleClosestToEnd = child;
7170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return visibleChildren;
7190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
7200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Map<Item, Rect> collectChildCoordinates() throws Throwable {
7220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>();
7230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            runTestOnUiThread(new Runnable() {
7240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                @Override
7250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                public void run() {
7260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    final int childCount = getChildCount();
7270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    for (int i = 0; i < childCount; i++) {
7280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        View child = getChildAt(i);
7290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        // do it if and only if child is visible
7300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        if (child.getRight() < 0 || child.getBottom() < 0 ||
7310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                child.getLeft() >= getWidth() || child.getTop() >= getHeight()) {
7320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            // invisible children may be drawn in cases like scrolling so we should
7330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            // ignore them
7340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            continue;
7350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        }
7360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        LayoutParams lp = (LayoutParams) child
7370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                .getLayoutParams();
7380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        TestViewHolder vh = (TestViewHolder) lp.mViewHolder;
7390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        items.put(vh.mBoundItem, getViewBounds(child));
7400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
7410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            });
7430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return items;
7440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
7450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public void setFakeRtl(Boolean fakeRtl) {
7480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mFakeRTL = fakeRtl;
7490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            try {
7500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                requestLayoutOnUIThread(mRecyclerView);
7510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } catch (Throwable throwable) {
7520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                postExceptionToInstrumentation(throwable);
7530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
7550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        String layoutToString(String hint) {
7570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            StringBuilder sb = new StringBuilder();
7580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            sb.append("LAYOUT POSITIONS AND INDICES ").append(hint).append("\n");
7590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (int i = 0; i < getChildCount(); i++) {
7600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final View view = getChildAt(i);
7610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
7620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                sb.append(String.format("index: %d pos: %d top: %d bottom: %d span: %d isFull:%s",
7630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        i, getPosition(view),
7640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedStart(view),
7650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mPrimaryOrientation.getDecoratedEnd(view),
7660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        layoutParams.getSpanIndex(), layoutParams.isFullSpan())).append("\n");
7670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return sb.toString();
7690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
7700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        protected void validateChildren() {
7720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            validateChildren(null);
7730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
7740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        private void validateChildren(String msg) {
7760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (getChildCount() == 0 || mRecyclerView.mState.isPreLayout()) {
7770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return;
7780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int dir = mShouldReverseLayout ? -1 : 1;
7800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            int i = 0;
7810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            int pos = -1;
7820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            while (i < getChildCount()) {
7830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
7840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (lp.isItemRemoved()) {
7850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    i++;
7860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    continue;
7870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                pos = getPosition(getChildAt(i));
7890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                break;
7900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (pos == -1) {
7920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return;
7930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            while (++i < getChildCount()) {
7950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
7960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (lp.isItemRemoved()) {
7970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    continue;
7980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                pos += dir;
8000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (getPosition(getChildAt(i)) != pos) {
8010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    throw new RuntimeException("INVALID POSITION FOR CHILD " + i + "\n" +
8020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            layoutToString("ERROR") + "\n msg:" + msg);
8030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
8040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
8050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
8061ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik
8071ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        @Override
8081ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        int gatherPrefetchIndices(int dx, int dy, RecyclerView.State state, int[] outIndices) {
8091ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            if (prefetchLatch != null) prefetchLatch.countDown();
8101ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik            return super.gatherPrefetchIndices(dx, dy, state, outIndices);
8111ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik        }
8120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
8130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    class GridTestAdapter extends TestAdapter {
8150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mOrientation;
8170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mRecyclerViewWidth;
8180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int mRecyclerViewHeight;
8190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Integer mSizeReference = null;
8200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // original ids of items that should be full span
8220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        HashSet<Integer> mFullSpanItems = new HashSet<Integer>();
8230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        protected boolean mViewsHaveEqualSize = false; // size in the scrollable direction
8250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        protected OnBindCallback mOnBindCallback;
8270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        GridTestAdapter(int count, int orientation) {
8290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            super(count);
8300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mOrientation = orientation;
8310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
8320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
8340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public TestViewHolder onCreateViewHolder(ViewGroup parent,
8350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                int viewType) {
8360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mRecyclerViewWidth = parent.getWidth();
8370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mRecyclerViewHeight = parent.getHeight();
8380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            TestViewHolder vh = super.onCreateViewHolder(parent, viewType);
8390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (mOnBindCallback != null) {
8400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mOnBindCallback.onCreatedViewHolder(vh);
8410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
8420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            return vh;
8430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
8440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
8460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public void offsetOriginalIndices(int start, int offset) {
8470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (mFullSpanItems.size() > 0) {
8480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                HashSet<Integer> old = mFullSpanItems;
8490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mFullSpanItems = new HashSet<Integer>();
8500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                for (Integer i : old) {
8510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    if (i < start) {
8520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mFullSpanItems.add(i);
8530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    } else if (offset > 0 || (start + Math.abs(offset)) <= i) {
8540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mFullSpanItems.add(i + offset);
8550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    } else if (DEBUG) {
8560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        Log.d(TAG, "removed full span item " + i);
8570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
8580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
8590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
8600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            super.offsetOriginalIndices(start, offset);
8610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
8620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
8640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        protected void moveInUIThread(int from, int to) {
8650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            boolean setAsFullSpanAgain = mFullSpanItems.contains(from);
8660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            super.moveInUIThread(from, to);
8670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (setAsFullSpanAgain) {
8680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mFullSpanItems.add(to);
8690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
8700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
8710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        @Override
8730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        public void onBindViewHolder(TestViewHolder holder,
8740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                int position) {
8750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (mSizeReference == null) {
8760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mSizeReference = mOrientation == OrientationHelper.HORIZONTAL ? mRecyclerViewWidth
8770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        / AVG_ITEM_PER_VIEW : mRecyclerViewHeight / AVG_ITEM_PER_VIEW;
8780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
8790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            super.onBindViewHolder(holder, position);
880a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska
8810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Item item = mItems.get(position);
8820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) holder.itemView
8830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    .getLayoutParams();
8840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (lp instanceof StaggeredGridLayoutManager.LayoutParams) {
8854143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                ((StaggeredGridLayoutManager.LayoutParams) lp)
8864143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                        .setFullSpan(mFullSpanItems.contains(item.mAdapterIndex));
8870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
8884143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                StaggeredGridLayoutManager.LayoutParams slp
889a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska                    = (StaggeredGridLayoutManager.LayoutParams) mLayoutManager
890a810d8b02435915c665ee36128ab21e4f4c76d64Aga Madurska                    .generateDefaultLayoutParams();
8910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                holder.itemView.setLayoutParams(slp);
8920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                slp.setFullSpan(mFullSpanItems.contains(item.mAdapterIndex));
8930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lp = slp;
8940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
8950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
8960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (mOnBindCallback == null || mOnBindCallback.assignRandomSize()) {
8970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int minSize = mViewsHaveEqualSize ? mSizeReference :
8980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mSizeReference + 20 * (item.mId % 10);
8990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (mOrientation == OrientationHelper.HORIZONTAL) {
9000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    holder.itemView.setMinimumWidth(minSize);
9010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                } else {
9020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    holder.itemView.setMinimumHeight(minSize);
9030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
9040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lp.topMargin = 3;
9050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lp.leftMargin = 5;
9060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lp.rightMargin = 7;
9070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lp.bottomMargin = 9;
9080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
9090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
9100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (mOnBindCallback != null) {
9110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mOnBindCallback.onBoundItem(holder, position);
9120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
9130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
9140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
9150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar}
916