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