19aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar/*
29aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * Copyright (C) 2016 The Android Open Source Project
39aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar *
49aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
59aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * you may not use this file except in compliance with the License.
69aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * You may obtain a copy of the License at
79aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar *
89aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
99aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar *
109aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * Unless required by applicable law or agreed to in writing, software
119aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
129aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * See the License for the specific language governing permissions and
149aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * limitations under the License.
159aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar */
169aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
179aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarpackage android.support.v7.widget;
189aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
199aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport static android.view.View.MeasureSpec.AT_MOST;
209aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport static android.view.View.MeasureSpec.EXACTLY;
219aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport static android.view.View.MeasureSpec.makeMeasureSpec;
229aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
239aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport static org.hamcrest.CoreMatchers.is;
249aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport static org.hamcrest.CoreMatchers.notNullValue;
259aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport static org.hamcrest.MatcherAssert.assertThat;
269aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
279aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport android.graphics.Rect;
289aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport android.test.suitebuilder.annotation.MediumTest;
299aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport android.view.View;
309aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
319aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport org.junit.Test;
329aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport org.junit.runner.RunWith;
339aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport org.junit.runners.Parameterized;
349aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
359aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport java.util.ArrayList;
369aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport java.util.HashMap;
379aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport java.util.List;
389aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarimport java.util.Map;
399aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
409aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar/**
419aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * Tests whether the layout manager can keep its children positions properly after it is re-laid
429aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar * out with larger/smaller intermediate size but the same final size.
439aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar */
449aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar@MediumTest
459aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar@RunWith(Parameterized.class)
469aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyarpublic class TestResizingRelayoutWithAutoMeasure extends BaseRecyclerViewInstrumentationTest {
479aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    private final RecyclerView.LayoutManager mLayoutManager;
489aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    private final float mWidthMultiplier;
499aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    private final float mHeightMultiplier;
509aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
519aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    public TestResizingRelayoutWithAutoMeasure(@SuppressWarnings("UnusedParameters") String name,
529aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            RecyclerView.LayoutManager layoutManager, float widthMultiplier,
539aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            float heightMultiplier) {
549aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        mLayoutManager = layoutManager;
559aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        mWidthMultiplier = widthMultiplier;
569aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        mHeightMultiplier = heightMultiplier;
579aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    }
589aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
599aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    @Parameterized.Parameters(name = "{0} w:{2} h:{3}")
609aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    public static List<Object[]> getParams() {
619aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        List<Object[]> params = new ArrayList<>();
629aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        for (float w : new float[]{.5f, 1f, 2f}) {
639aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            for (float h : new float[]{.5f, 1f, 2f}) {
649aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                params.add(
659aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                        new Object[]{"linear layout", new LinearLayoutManager(null), w, h}
669aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                );
679aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                params.add(
689aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                        new Object[]{"grid layout", new GridLayoutManager(null, 3), w, h}
699aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                );
709aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                params.add(
719aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                        new Object[]{"staggered", new StaggeredGridLayoutManager(3,
729aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                                StaggeredGridLayoutManager.VERTICAL), w, h}
739aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                );
749aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            }
759aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        }
769aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        return params;
779aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    }
789aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
799aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    @Test
809aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    public void testResizeDuringMeasurements() throws Throwable {
819aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
829aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        recyclerView.setLayoutManager(mLayoutManager);
839aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        recyclerView.setAdapter(new TestAdapter(500));
849aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        setRecyclerView(recyclerView);
859aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        getInstrumentation().waitForIdleSync();
869aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        assertThat("Test sanity", recyclerView.getChildCount() > 0, is(true));
879aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        final int lastPosition = recyclerView.getAdapter().getItemCount() - 1;
889aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        smoothScrollToPosition(lastPosition);
899aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        assertThat("test sanity", recyclerView.findViewHolderForAdapterPosition(lastPosition),
909aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                notNullValue());
919aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        runTestOnUiThread(new Runnable() {
929aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            @Override
939aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            public void run() {
949aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                int startHeight = recyclerView.getMeasuredHeight();
959aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                int startWidth = recyclerView.getMeasuredWidth();
969aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                Map<Integer, Rect> startPositions = capturePositions(recyclerView);
979aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                recyclerView.measure(
989aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                        makeMeasureSpec((int) (startWidth * mWidthMultiplier),
999aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                                mWidthMultiplier == 1f ? EXACTLY : AT_MOST),
1009aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                        makeMeasureSpec((int) (startHeight * mHeightMultiplier),
1019aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                                mHeightMultiplier == 1f ? EXACTLY : AT_MOST));
1029aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
1039aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                recyclerView.measure(
1049aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                        makeMeasureSpec(startWidth, EXACTLY),
1059aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                        makeMeasureSpec(startHeight, EXACTLY));
1069aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                recyclerView.dispatchLayout();
1079aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                Map<Integer, Rect> endPositions = capturePositions(recyclerView);
1089aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                assertStartItemPositions(startPositions, endPositions);
1099aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            }
1109aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        });
1119aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    }
1129aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
1139aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    private void assertStartItemPositions(Map<Integer, Rect> startPositions,
1149aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            Map<Integer, Rect> endPositions) {
1159aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        for (Map.Entry<Integer, Rect> entry : startPositions.entrySet()) {
1169aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            Rect rect = endPositions.get(entry.getKey());
1179aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            assertThat("view for position " + entry.getKey() + " at" + entry.getValue(), rect,
1189aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                    notNullValue());
1199aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            assertThat("rect for position " + entry.getKey(), entry.getValue(), is(rect));
1209aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        }
1219aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    }
1229aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar
1239aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    private Map<Integer, Rect> capturePositions(RecyclerView recyclerView) {
1249aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        Map<Integer, Rect> positions = new HashMap<>();
1259aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
1269aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            View view = mLayoutManager.getChildAt(i);
1279aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            int childAdapterPosition = recyclerView.getChildAdapterPosition(view);
1289aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            Rect outRect = new Rect();
1299aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            mLayoutManager.getDecoratedBoundsWithMargins(view, outRect);
1309aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            // only record if outRect is visible
1319aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            if (outRect.left >= mRecyclerView.getWidth() ||
1329aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                    outRect.top >= mRecyclerView.getHeight() ||
1339aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                    outRect.right < 0 ||
1349aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                    outRect.bottom < 0) {
1359aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar                continue;
1369aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            }
1379aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar            positions.put(childAdapterPosition, outRect);
1389aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        }
1399aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar        return positions;
1409aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar    }
1419aae13b01e2a00d892ab82677f613bcc6e380baeYigit Boyar}