1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.recyclerview.widget;
18
19import static androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL;
20
21import static org.junit.Assert.assertEquals;
22import static org.junit.Assert.assertSame;
23
24import android.support.test.filters.MediumTest;
25import android.view.View;
26
27import androidx.annotation.NonNull;
28
29import org.junit.Test;
30import org.junit.runner.RunWith;
31import org.junit.runners.Parameterized;
32
33import java.util.ArrayList;
34import java.util.List;
35
36@RunWith(Parameterized.class)
37public class LinearLayoutManagerPrepareForDropTest extends BaseLinearLayoutManagerTest {
38
39    final BaseLinearLayoutManagerTest.Config mConfig;
40    final SelectTargetChildren mSelectTargetChildren;
41
42    public LinearLayoutManagerPrepareForDropTest(
43            Config config, SelectTargetChildren selectTargetChildren) {
44        mConfig = config;
45        mSelectTargetChildren = selectTargetChildren;
46    }
47
48    @Parameterized.Parameters(name = "{0},selectTargetChildren:{1}")
49    public static Iterable<Object[]> params() {
50        SelectTargetChildren[] selectors
51                = new SelectTargetChildren[]{
52                new SelectTargetChildren() {
53                    @Override
54                    public int[] selectTargetChildren(int childCount) {
55                        return new int[]{1, 0};
56                    }
57                    @Override
58                    public String toString() {
59                        return "{1,0}";
60                    }
61                },
62                new SelectTargetChildren() {
63                    @Override
64                    public int[] selectTargetChildren(int childCount) {
65                        return new int[]{0, 1};
66                    }
67                    @Override
68                    public String toString() {
69                        return "{0,1}";
70                    }
71                },
72                new SelectTargetChildren() {
73                    @Override
74                    public int[] selectTargetChildren(int childCount) {
75                        return new int[]{childCount - 1, childCount - 2};
76                    }
77                    @Override
78                    public String toString() {
79                        return "{childCount-1,childCount-2}";
80                    }
81                },
82                new SelectTargetChildren() {
83                    @Override
84                    public int[] selectTargetChildren(int childCount) {
85                        return new int[]{childCount - 2, childCount - 1};
86                    }
87                    @Override
88                    public String toString() {
89                        return "{childCount-2,childCount-1}";
90                    }
91                },
92                new SelectTargetChildren() {
93                    @Override
94                    public int[] selectTargetChildren(int childCount) {
95                        return new int[]{childCount / 2, childCount / 2 + 1};
96                    }
97                    @Override
98                    public String toString() {
99                        return "{childCount/2,childCount/2+1}";
100                    }
101                },
102                new SelectTargetChildren() {
103                    @Override
104                    public int[] selectTargetChildren(int childCount) {
105                        return new int[]{childCount / 2 + 1, childCount / 2};
106                    }
107                    @Override
108                    public String toString() {
109                        return "{childCount/2+1,childCount/2}";
110                    }
111                }
112        };
113        List<Object[]> variations = new ArrayList<>();
114        for (SelectTargetChildren selector : selectors) {
115            for (BaseLinearLayoutManagerTest.Config config : createBaseVariations()) {
116                variations.add(new Object[]{config, selector});
117            }
118        }
119        return variations;
120    }
121
122    @Test
123    @MediumTest
124    public void prepareForDropTest()
125            throws Throwable {
126        final Config config = (Config) mConfig.clone();
127        config.mTestAdapter = new BaseRecyclerViewInstrumentationTest.TestAdapter(100) {
128            @Override
129            public void onBindViewHolder(
130                    @NonNull BaseRecyclerViewInstrumentationTest.TestViewHolder holder,
131                    int position) {
132                super.onBindViewHolder(holder, position);
133                if (config.mOrientation == HORIZONTAL) {
134                    final int base = mLayoutManager.getWidth() / 5;
135                    final int itemRand = holder.mBoundItem.mText.hashCode() % base;
136                    holder.itemView.setMinimumWidth(base + itemRand);
137                } else {
138                    final int base = mLayoutManager.getHeight() / 5;
139                    final int itemRand = holder.mBoundItem.mText.hashCode() % base;
140                    holder.itemView.setMinimumHeight(base + itemRand);
141                }
142            }
143        };
144        setupByConfig(config, true);
145        mLayoutManager.expectLayouts(1);
146        scrollToPosition(mTestAdapter.getItemCount() / 2);
147        mLayoutManager.waitForLayout(1);
148        int[] positions = mSelectTargetChildren.selectTargetChildren(mRecyclerView.getChildCount());
149        final View fromChild = mLayoutManager.getChildAt(positions[0]);
150        final int fromPos = mLayoutManager.getPosition(fromChild);
151        final View onChild = mLayoutManager.getChildAt(positions[1]);
152        final int toPos = mLayoutManager.getPosition(onChild);
153        final OrientationHelper helper = mLayoutManager.mOrientationHelper;
154        final int dragCoordinate;
155        final boolean towardsHead = toPos < fromPos;
156        final int referenceLine;
157        if (config.mReverseLayout == towardsHead) {
158            referenceLine = helper.getDecoratedEnd(onChild);
159            dragCoordinate = referenceLine + 3 -
160                    helper.getDecoratedMeasurement(fromChild);
161        } else {
162            referenceLine = helper.getDecoratedStart(onChild);
163            dragCoordinate = referenceLine - 3;
164        }
165        mLayoutManager.expectLayouts(2);
166
167        final int x, y;
168        if (config.mOrientation == HORIZONTAL) {
169            x = dragCoordinate;
170            y = fromChild.getTop();
171        } else {
172            y = dragCoordinate;
173            x = fromChild.getLeft();
174        }
175        mActivityRule.runOnUiThread(new Runnable() {
176            @Override
177            public void run() {
178                mTestAdapter.moveInUIThread(fromPos, toPos);
179                mTestAdapter.notifyItemMoved(fromPos, toPos);
180                mLayoutManager.prepareForDrop(fromChild, onChild, x, y);
181            }
182        });
183        mLayoutManager.waitForLayout(2);
184
185        assertSame(fromChild, mRecyclerView.findViewHolderForAdapterPosition(toPos).itemView);
186        // make sure it has the position we wanted
187        if (config.mReverseLayout == towardsHead) {
188            assertEquals(referenceLine, helper.getDecoratedEnd(fromChild));
189        } else {
190            assertEquals(referenceLine, helper.getDecoratedStart(fromChild));
191        }
192    }
193
194    protected interface SelectTargetChildren {
195        int[] selectTargetChildren(int childCount);
196    }
197}
198