Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 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 | |
Steve McKay | 0aa7207 | 2017-09-29 09:43:40 -0700 | [diff] [blame] | 17 | package com.android.documentsui.selection; |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 18 | |
Steve McKay | 0aa7207 | 2017-09-29 09:43:40 -0700 | [diff] [blame] | 19 | import static com.android.documentsui.selection.GridModel.NOT_SET; |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 20 | import static org.junit.Assert.assertEquals; |
| 21 | import static org.junit.Assert.assertFalse; |
| 22 | import static org.junit.Assert.assertTrue; |
Ben Kwa | 936a7fc | 2015-12-10 15:21:18 -0800 | [diff] [blame] | 23 | |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 24 | import android.graphics.Point; |
| 25 | import android.graphics.Rect; |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 26 | import android.support.test.filters.SmallTest; |
| 27 | import android.support.test.runner.AndroidJUnit4; |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 28 | import android.support.v7.widget.RecyclerView.OnScrollListener; |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 29 | |
Steve McKay | 0aa7207 | 2017-09-29 09:43:40 -0700 | [diff] [blame] | 30 | import com.android.documentsui.selection.BandSelectionHelper.BandHost; |
Steve McKay | 0aa7207 | 2017-09-29 09:43:40 -0700 | [diff] [blame] | 31 | import com.android.documentsui.selection.testing.SelectionPredicates; |
| 32 | import com.android.documentsui.selection.testing.TestAdapter; |
| 33 | import com.android.documentsui.selection.testing.TestStableIdProvider; |
Steve McKay | 3755016 | 2015-09-08 17:15:25 -0700 | [diff] [blame] | 34 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 35 | import org.junit.After; |
| 36 | import org.junit.Test; |
| 37 | import org.junit.runner.RunWith; |
| 38 | |
Steve McKay | e45d1f0 | 2016-01-05 15:27:17 -0800 | [diff] [blame] | 39 | import java.util.ArrayList; |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 40 | import java.util.List; |
Ben Kwa | d72a1da | 2015-12-01 19:56:57 -0800 | [diff] [blame] | 41 | import java.util.Set; |
| 42 | |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 43 | import javax.annotation.Nullable; |
| 44 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 45 | @RunWith(AndroidJUnit4.class) |
Steve McKay | bfd8705 | 2015-11-12 10:54:31 +0900 | [diff] [blame] | 46 | @SmallTest |
Steve McKay | fcff0b0 | 2017-08-25 10:41:58 -0700 | [diff] [blame] | 47 | public class GridModelTest { |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 48 | |
| 49 | private static final int VIEW_PADDING_PX = 5; |
| 50 | private static final int CHILD_VIEW_EDGE_PX = 100; |
| 51 | private static final int VIEWPORT_HEIGHT = 500; |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 52 | |
| 53 | private GridModel mModel; |
| 54 | private TestHost mHost; |
Steve McKay | 5a62037 | 2017-09-11 12:18:56 -0700 | [diff] [blame] | 55 | private TestAdapter mAdapter; |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 56 | private Set<String> mLastSelection; |
| 57 | private int mViewWidth; |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 58 | |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 59 | // TLDR: Don't call model.{start|resize}Selection; use the local #startSelection and |
| 60 | // #resizeSelection methods instead. |
| 61 | // |
| 62 | // The reason for this is that selection is stateful and involves operations that take the |
| 63 | // current UI state (e.g scrolling) into account. This test maintains its own copy of the |
| 64 | // selection bounds as control data for verifying selections. Keep this data in sync by calling |
| 65 | // #startSelection and |
| 66 | // #resizeSelection. |
| 67 | private Point mSelectionOrigin; |
| 68 | private Point mSelectionPoint; |
| 69 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 70 | @After |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 71 | public void tearDown() { |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 72 | mModel = null; |
| 73 | mHost = null; |
| 74 | mLastSelection = null; |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 75 | } |
| 76 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 77 | @Test |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 78 | public void testSelectionLeftOfItems() { |
Steve McKay | e45d1f0 | 2016-01-05 15:27:17 -0800 | [diff] [blame] | 79 | initData(20, 5); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 80 | startSelection(new Point(0, 10)); |
| 81 | resizeSelection(new Point(1, 11)); |
| 82 | assertNoSelection(); |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 83 | assertEquals(NOT_SET, mModel.getPositionNearestOrigin()); |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 84 | } |
| 85 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 86 | @Test |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 87 | public void testSelectionRightOfItems() { |
Steve McKay | e45d1f0 | 2016-01-05 15:27:17 -0800 | [diff] [blame] | 88 | initData(20, 4); |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 89 | startSelection(new Point(mViewWidth - 1, 10)); |
| 90 | resizeSelection(new Point(mViewWidth - 2, 11)); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 91 | assertNoSelection(); |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 92 | assertEquals(NOT_SET, mModel.getPositionNearestOrigin()); |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 93 | } |
| 94 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 95 | @Test |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 96 | public void testSelectionAboveItems() { |
Steve McKay | e45d1f0 | 2016-01-05 15:27:17 -0800 | [diff] [blame] | 97 | initData(20, 4); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 98 | startSelection(new Point(10, 0)); |
| 99 | resizeSelection(new Point(11, 1)); |
| 100 | assertNoSelection(); |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 101 | assertEquals(NOT_SET, mModel.getPositionNearestOrigin()); |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 102 | } |
| 103 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 104 | @Test |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 105 | public void testSelectionBelowItems() { |
Steve McKay | e45d1f0 | 2016-01-05 15:27:17 -0800 | [diff] [blame] | 106 | initData(5, 4); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 107 | startSelection(new Point(10, VIEWPORT_HEIGHT - 1)); |
| 108 | resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2)); |
| 109 | assertNoSelection(); |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 110 | assertEquals(NOT_SET, mModel.getPositionNearestOrigin()); |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 111 | } |
| 112 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 113 | @Test |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 114 | public void testVerticalSelectionBetweenItems() { |
Steve McKay | e45d1f0 | 2016-01-05 15:27:17 -0800 | [diff] [blame] | 115 | initData(20, 4); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 116 | startSelection(new Point(106, 0)); |
| 117 | resizeSelection(new Point(107, 200)); |
| 118 | assertNoSelection(); |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 119 | assertEquals(NOT_SET, mModel.getPositionNearestOrigin()); |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 120 | } |
| 121 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 122 | @Test |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 123 | public void testHorizontalSelectionBetweenItems() { |
Steve McKay | e45d1f0 | 2016-01-05 15:27:17 -0800 | [diff] [blame] | 124 | initData(20, 4); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 125 | startSelection(new Point(0, 105)); |
| 126 | resizeSelection(new Point(200, 106)); |
| 127 | assertNoSelection(); |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 128 | assertEquals(NOT_SET, mModel.getPositionNearestOrigin()); |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 129 | } |
| 130 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 131 | @Test |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 132 | public void testGrowingAndShrinkingSelection() { |
Steve McKay | e45d1f0 | 2016-01-05 15:27:17 -0800 | [diff] [blame] | 133 | initData(20, 4); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 134 | startSelection(new Point(0, 0)); |
| 135 | |
| 136 | resizeSelection(new Point(5, 5)); |
| 137 | verifySelection(); |
| 138 | |
| 139 | resizeSelection(new Point(109, 109)); |
| 140 | verifySelection(); |
| 141 | |
| 142 | resizeSelection(new Point(110, 109)); |
| 143 | verifySelection(); |
| 144 | |
| 145 | resizeSelection(new Point(110, 110)); |
| 146 | verifySelection(); |
| 147 | |
| 148 | resizeSelection(new Point(214, 214)); |
| 149 | verifySelection(); |
| 150 | |
| 151 | resizeSelection(new Point(215, 214)); |
| 152 | verifySelection(); |
| 153 | |
| 154 | resizeSelection(new Point(214, 214)); |
| 155 | verifySelection(); |
| 156 | |
| 157 | resizeSelection(new Point(110, 110)); |
| 158 | verifySelection(); |
| 159 | |
| 160 | resizeSelection(new Point(110, 109)); |
| 161 | verifySelection(); |
| 162 | |
| 163 | resizeSelection(new Point(109, 109)); |
| 164 | verifySelection(); |
| 165 | |
| 166 | resizeSelection(new Point(5, 5)); |
| 167 | verifySelection(); |
| 168 | |
| 169 | resizeSelection(new Point(0, 0)); |
| 170 | verifySelection(); |
| 171 | |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 172 | assertEquals(NOT_SET, mModel.getPositionNearestOrigin()); |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 173 | } |
| 174 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 175 | @Test |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 176 | public void testSelectionMovingAroundOrigin() { |
Steve McKay | e45d1f0 | 2016-01-05 15:27:17 -0800 | [diff] [blame] | 177 | initData(16, 4); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 178 | |
| 179 | startSelection(new Point(210, 210)); |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 180 | resizeSelection(new Point(mViewWidth - 1, 0)); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 181 | verifySelection(); |
| 182 | |
| 183 | resizeSelection(new Point(0, 0)); |
| 184 | verifySelection(); |
| 185 | |
| 186 | resizeSelection(new Point(0, 420)); |
| 187 | verifySelection(); |
| 188 | |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 189 | resizeSelection(new Point(mViewWidth - 1, 420)); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 190 | verifySelection(); |
| 191 | |
| 192 | // This is manually figured and will need to be adjusted if the separator position is |
| 193 | // changed. |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 194 | assertEquals(7, mModel.getPositionNearestOrigin()); |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 195 | } |
| 196 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 197 | @Test |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 198 | public void testScrollingBandSelect() { |
Steve McKay | e45d1f0 | 2016-01-05 15:27:17 -0800 | [diff] [blame] | 199 | initData(40, 4); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 200 | |
| 201 | startSelection(new Point(0, 0)); |
| 202 | resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1)); |
| 203 | verifySelection(); |
| 204 | |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 205 | scroll(CHILD_VIEW_EDGE_PX); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 206 | verifySelection(); |
| 207 | |
| 208 | resizeSelection(new Point(200, VIEWPORT_HEIGHT - 1)); |
| 209 | verifySelection(); |
| 210 | |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 211 | scroll(CHILD_VIEW_EDGE_PX); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 212 | verifySelection(); |
| 213 | |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 214 | scroll(-2 * CHILD_VIEW_EDGE_PX); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 215 | verifySelection(); |
| 216 | |
| 217 | resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1)); |
| 218 | verifySelection(); |
| 219 | |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 220 | assertEquals(0, mModel.getPositionNearestOrigin()); |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 221 | } |
| 222 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 223 | private void initData(final int numChildren, int numColumns) { |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 224 | mHost = new TestHost(numChildren, numColumns); |
Steve McKay | 5a62037 | 2017-09-11 12:18:56 -0700 | [diff] [blame] | 225 | mAdapter = new TestAdapter() { |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 226 | @Override |
| 227 | public String getStableId(int position) { |
| 228 | return Integer.toString(position); |
| 229 | } |
| 230 | |
| 231 | @Override |
| 232 | public int getItemCount() { |
| 233 | return numChildren; |
| 234 | } |
| 235 | }; |
| 236 | |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 237 | mViewWidth = VIEW_PADDING_PX + numColumns * (VIEW_PADDING_PX + CHILD_VIEW_EDGE_PX); |
| 238 | |
Steve McKay | 5a62037 | 2017-09-11 12:18:56 -0700 | [diff] [blame] | 239 | mModel = new GridModel( |
| 240 | mHost, |
| 241 | new TestStableIdProvider(mAdapter), |
| 242 | SelectionPredicates.CAN_SET_ANYTHING); |
| 243 | |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 244 | mModel.addOnSelectionChangedListener( |
Steve McKay | 5a62037 | 2017-09-11 12:18:56 -0700 | [diff] [blame] | 245 | new GridModel.SelectionObserver() { |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 246 | @Override |
| 247 | public void onSelectionChanged(Set<String> updatedSelection) { |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 248 | mLastSelection = updatedSelection; |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 249 | } |
| 250 | }); |
| 251 | } |
| 252 | |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 253 | /** Returns the current selection area as a Rect. */ |
| 254 | private Rect getSelectionArea() { |
| 255 | // Construct a rect from the two selection points. |
| 256 | Rect selectionArea = new Rect( |
| 257 | mSelectionOrigin.x, mSelectionOrigin.y, mSelectionOrigin.x, mSelectionOrigin.y); |
| 258 | selectionArea.union(mSelectionPoint.x, mSelectionPoint.y); |
| 259 | // Rect intersection tests are exclusive of bounds, while the MSM's selection code is |
| 260 | // inclusive. Expand the rect by 1 pixel in all directions to account for this. |
| 261 | selectionArea.inset(-1, -1); |
| 262 | |
| 263 | return selectionArea; |
| 264 | } |
| 265 | |
| 266 | /** Asserts that the selection is currently empty. */ |
| 267 | private void assertNoSelection() { |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 268 | assertEquals("Unexpected items " + mLastSelection + " in selection " + getSelectionArea(), |
| 269 | 0, mLastSelection.size()); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 270 | } |
| 271 | |
| 272 | /** Verifies the selection using actual bbox checks. */ |
| 273 | private void verifySelection() { |
| 274 | Rect selectionArea = getSelectionArea(); |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 275 | for (TestHost.Item item: mHost.items) { |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 276 | if (Rect.intersects(selectionArea, item.rect)) { |
| 277 | assertTrue("Expected item " + item + " was not in selection " + selectionArea, |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 278 | mLastSelection.contains(item.name)); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 279 | } else { |
| 280 | assertFalse("Unexpected item " + item + " in selection" + selectionArea, |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 281 | mLastSelection.contains(item.name)); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 282 | } |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 283 | } |
| 284 | } |
| 285 | |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 286 | private void startSelection(Point p) { |
Steve McKay | 5bf51ec | 2017-09-29 13:43:48 -0700 | [diff] [blame^] | 287 | mModel.startCapturing(p); |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 288 | mSelectionOrigin = mHost.createAbsolutePoint(p); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 289 | } |
| 290 | |
| 291 | private void resizeSelection(Point p) { |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 292 | mModel.resizeSelection(p); |
| 293 | mSelectionPoint = mHost.createAbsolutePoint(p); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 294 | } |
| 295 | |
Steve McKay | e45d1f0 | 2016-01-05 15:27:17 -0800 | [diff] [blame] | 296 | private void scroll(int dy) { |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 297 | assertTrue(mHost.verticalOffset + VIEWPORT_HEIGHT + dy <= mHost.getTotalHeight()); |
| 298 | mHost.verticalOffset += dy; |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 299 | // Correct the cached selection point as well. |
| 300 | mSelectionPoint.y += dy; |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 301 | mHost.mScrollListener.onScrolled(null, 0, dy); |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 302 | } |
| 303 | |
Steve McKay | 5a62037 | 2017-09-11 12:18:56 -0700 | [diff] [blame] | 304 | private static final class TestHost extends BandHost { |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 305 | |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 306 | private final int mNumColumns; |
| 307 | private final int mNumRows; |
| 308 | private final int mNumChildren; |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 309 | private final int mSeparatorPosition; |
| 310 | |
| 311 | public int horizontalOffset = 0; |
| 312 | public int verticalOffset = 0; |
| 313 | private List<Item> items = new ArrayList<>(); |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 314 | |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 315 | // Installed by GridModel on construction. |
| 316 | private @Nullable OnScrollListener mScrollListener; |
| 317 | |
| 318 | public TestHost(int numChildren, int numColumns) { |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 319 | mNumChildren = numChildren; |
| 320 | mNumColumns = numColumns; |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 321 | mSeparatorPosition = mNumColumns + 1; |
| 322 | mNumRows = setupGrid(); |
| 323 | } |
| 324 | |
| 325 | private int setupGrid() { |
| 326 | // Split the input set into folders and documents. Do this such that there is a |
| 327 | // partially-populated row in the middle of the grid, to test corner cases in layout |
| 328 | // code. |
| 329 | int y = VIEW_PADDING_PX; |
| 330 | int i = 0; |
| 331 | int numRows = 0; |
| 332 | while (i < mNumChildren) { |
| 333 | int top = y; |
| 334 | int height = CHILD_VIEW_EDGE_PX; |
| 335 | int width = CHILD_VIEW_EDGE_PX; |
| 336 | for (int j = 0; j < mNumColumns && i < mNumChildren; j++) { |
| 337 | int left = VIEW_PADDING_PX + (j * (width + VIEW_PADDING_PX)); |
| 338 | items.add(new Item( |
| 339 | Integer.toString(i), |
| 340 | new Rect( |
| 341 | left, |
| 342 | top, |
| 343 | left + width - 1, |
| 344 | top + height - 1))); |
| 345 | |
| 346 | // Create a partially populated row at the separator position. |
| 347 | if (++i == mSeparatorPosition) { |
| 348 | break; |
| 349 | } |
| 350 | } |
| 351 | y += height + VIEW_PADDING_PX; |
| 352 | numRows++; |
| 353 | } |
| 354 | |
| 355 | return numRows; |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 356 | } |
| 357 | |
| 358 | private int getTotalHeight() { |
| 359 | return CHILD_VIEW_EDGE_PX * mNumRows + VIEW_PADDING_PX * (mNumRows + 1); |
| 360 | } |
| 361 | |
| 362 | private int getFirstVisibleRowIndex() { |
| 363 | return verticalOffset / (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX); |
| 364 | } |
| 365 | |
| 366 | private int getLastVisibleRowIndex() { |
| 367 | int lastVisibleRowUncapped = |
| 368 | (VIEWPORT_HEIGHT + verticalOffset - 1) / (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX); |
| 369 | return Math.min(lastVisibleRowUncapped, mNumRows - 1); |
| 370 | } |
| 371 | |
| 372 | private int getNumItemsInRow(int index) { |
| 373 | assertTrue(index >= 0 && index < mNumRows); |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 374 | int mod = mSeparatorPosition % mNumColumns; |
| 375 | if (index == (mSeparatorPosition / mNumColumns)) { |
| 376 | // The row containing the separator may be incomplete |
| 377 | return mod > 0 ? mod : mNumColumns; |
| 378 | } |
| 379 | // Account for the partial separator row in the final row tally. |
| 380 | if (index == mNumRows - 1) { |
| 381 | // The last row may be incomplete |
| 382 | int finalRowCount = (mNumChildren - mod) % mNumColumns; |
| 383 | return finalRowCount > 0 ? finalRowCount : mNumColumns; |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 384 | } |
| 385 | |
| 386 | return mNumColumns; |
| 387 | } |
| 388 | |
| 389 | @Override |
Steve McKay | 8845659 | 2017-08-24 10:09:01 -0700 | [diff] [blame] | 390 | public void addOnScrollListener(OnScrollListener listener) { |
| 391 | mScrollListener = listener; |
| 392 | } |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 393 | |
| 394 | @Override |
| 395 | public void removeOnScrollListener(OnScrollListener listener) {} |
| 396 | |
| 397 | @Override |
| 398 | public Point createAbsolutePoint(Point relativePoint) { |
| 399 | return new Point( |
| 400 | relativePoint.x + horizontalOffset, relativePoint.y + verticalOffset); |
| 401 | } |
| 402 | |
| 403 | @Override |
| 404 | public int getVisibleChildCount() { |
| 405 | int childCount = 0; |
| 406 | for (int i = getFirstVisibleRowIndex(); i <= getLastVisibleRowIndex(); i++) { |
| 407 | childCount += getNumItemsInRow(i); |
| 408 | } |
| 409 | return childCount; |
| 410 | } |
| 411 | |
| 412 | @Override |
Ben Kwa | 936a7fc | 2015-12-10 15:21:18 -0800 | [diff] [blame] | 413 | public int getAdapterPositionAt(int index) { |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 414 | // Account for partial rows by actually tallying up the items in hidden rows. |
| 415 | int hiddenCount = 0; |
| 416 | for (int i = 0; i < getFirstVisibleRowIndex(); i++) { |
| 417 | hiddenCount += getNumItemsInRow(i); |
| 418 | } |
| 419 | return index + hiddenCount; |
Ben Kwa | 936a7fc | 2015-12-10 15:21:18 -0800 | [diff] [blame] | 420 | } |
| 421 | |
| 422 | @Override |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 423 | public Rect getAbsoluteRectForChildViewAt(int index) { |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 424 | int adapterPosition = getAdapterPositionAt(index); |
| 425 | return items.get(adapterPosition).rect; |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 426 | } |
| 427 | |
| 428 | @Override |
Steve McKay | 3755016 | 2015-09-08 17:15:25 -0700 | [diff] [blame] | 429 | public int getChildCount() { |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 430 | return mNumChildren; |
| 431 | } |
| 432 | |
| 433 | @Override |
Steve McKay | 3755016 | 2015-09-08 17:15:25 -0700 | [diff] [blame] | 434 | public int getColumnCount() { |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 435 | return mNumColumns; |
| 436 | } |
| 437 | |
| 438 | @Override |
Steve McKay | 3755016 | 2015-09-08 17:15:25 -0700 | [diff] [blame] | 439 | public void showBand(Rect rect) { |
| 440 | throw new UnsupportedOperationException(); |
| 441 | } |
| 442 | |
| 443 | @Override |
| 444 | public void hideBand() { |
| 445 | throw new UnsupportedOperationException(); |
| 446 | } |
| 447 | |
| 448 | @Override |
| 449 | public void scrollBy(int dy) { |
| 450 | throw new UnsupportedOperationException(); |
| 451 | } |
| 452 | |
| 453 | @Override |
| 454 | public int getHeight() { |
| 455 | throw new UnsupportedOperationException(); |
| 456 | } |
| 457 | |
| 458 | @Override |
| 459 | public void invalidateView() { |
| 460 | throw new UnsupportedOperationException(); |
| 461 | } |
| 462 | |
| 463 | @Override |
| 464 | public void runAtNextFrame(Runnable r) { |
| 465 | throw new UnsupportedOperationException(); |
| 466 | } |
| 467 | |
| 468 | @Override |
| 469 | public void removeCallback(Runnable r) { |
| 470 | throw new UnsupportedOperationException(); |
| 471 | } |
Ben Kwa | a017bc1 | 2015-10-07 14:15:12 -0700 | [diff] [blame] | 472 | |
| 473 | @Override |
Tomasz Mikolajewski | f2855f0 | 2016-04-14 14:22:00 +0900 | [diff] [blame] | 474 | public boolean hasView(int adapterPosition) { |
| 475 | return true; |
| 476 | } |
| 477 | |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 478 | public static final class Item { |
| 479 | public String name; |
| 480 | public Rect rect; |
| 481 | |
| 482 | public Item(String n, Rect r) { |
| 483 | name = n; |
| 484 | rect = r; |
| 485 | } |
| 486 | |
Steve McKay | 53c8e80 | 2017-08-21 12:27:10 -0700 | [diff] [blame] | 487 | @Override |
Ben Kwa | 0ecd4c4 | 2016-03-14 11:56:47 -0700 | [diff] [blame] | 488 | public String toString() { |
| 489 | return name + ": " + rect; |
| 490 | } |
| 491 | } |
Kyle Horimoto | 1ed2c92 | 2015-08-06 10:29:37 -0700 | [diff] [blame] | 492 | } |
| 493 | } |