blob: 1cb2900a546ee5768767815e276bf74f23fb897e [file] [log] [blame]
Kyle Horimoto1ed2c922015-08-06 10:29:37 -07001/*
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 McKay0aa72072017-09-29 09:43:40 -070017package com.android.documentsui.selection;
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070018
Steve McKay0aa72072017-09-29 09:43:40 -070019import static com.android.documentsui.selection.GridModel.NOT_SET;
Steve McKay53c8e802017-08-21 12:27:10 -070020import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertFalse;
22import static org.junit.Assert.assertTrue;
Ben Kwa936a7fc2015-12-10 15:21:18 -080023
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070024import android.graphics.Point;
25import android.graphics.Rect;
Steve McKay53c8e802017-08-21 12:27:10 -070026import android.support.test.filters.SmallTest;
27import android.support.test.runner.AndroidJUnit4;
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070028import android.support.v7.widget.RecyclerView.OnScrollListener;
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070029
Steve McKay0aa72072017-09-29 09:43:40 -070030import com.android.documentsui.selection.BandSelectionHelper.BandHost;
Steve McKay0aa72072017-09-29 09:43:40 -070031import com.android.documentsui.selection.testing.SelectionPredicates;
32import com.android.documentsui.selection.testing.TestAdapter;
33import com.android.documentsui.selection.testing.TestStableIdProvider;
Steve McKay37550162015-09-08 17:15:25 -070034
Steve McKay53c8e802017-08-21 12:27:10 -070035import org.junit.After;
36import org.junit.Test;
37import org.junit.runner.RunWith;
38
Steve McKaye45d1f02016-01-05 15:27:17 -080039import java.util.ArrayList;
Ben Kwa0ecd4c42016-03-14 11:56:47 -070040import java.util.List;
Ben Kwad72a1da2015-12-01 19:56:57 -080041import java.util.Set;
42
Steve McKay88456592017-08-24 10:09:01 -070043import javax.annotation.Nullable;
44
Steve McKay53c8e802017-08-21 12:27:10 -070045@RunWith(AndroidJUnit4.class)
Steve McKaybfd87052015-11-12 10:54:31 +090046@SmallTest
Steve McKayfcff0b02017-08-25 10:41:58 -070047public class GridModelTest {
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070048
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 McKay88456592017-08-24 10:09:01 -070052
53 private GridModel mModel;
54 private TestHost mHost;
Steve McKay5a620372017-09-11 12:18:56 -070055 private TestAdapter mAdapter;
Steve McKay88456592017-08-24 10:09:01 -070056 private Set<String> mLastSelection;
57 private int mViewWidth;
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070058
Ben Kwa0ecd4c42016-03-14 11:56:47 -070059 // 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 McKay53c8e802017-08-21 12:27:10 -070070 @After
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070071 public void tearDown() {
Steve McKay88456592017-08-24 10:09:01 -070072 mModel = null;
73 mHost = null;
74 mLastSelection = null;
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070075 }
76
Steve McKay53c8e802017-08-21 12:27:10 -070077 @Test
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070078 public void testSelectionLeftOfItems() {
Steve McKaye45d1f02016-01-05 15:27:17 -080079 initData(20, 5);
Ben Kwa0ecd4c42016-03-14 11:56:47 -070080 startSelection(new Point(0, 10));
81 resizeSelection(new Point(1, 11));
82 assertNoSelection();
Steve McKay88456592017-08-24 10:09:01 -070083 assertEquals(NOT_SET, mModel.getPositionNearestOrigin());
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070084 }
85
Steve McKay53c8e802017-08-21 12:27:10 -070086 @Test
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070087 public void testSelectionRightOfItems() {
Steve McKaye45d1f02016-01-05 15:27:17 -080088 initData(20, 4);
Steve McKay88456592017-08-24 10:09:01 -070089 startSelection(new Point(mViewWidth - 1, 10));
90 resizeSelection(new Point(mViewWidth - 2, 11));
Ben Kwa0ecd4c42016-03-14 11:56:47 -070091 assertNoSelection();
Steve McKay88456592017-08-24 10:09:01 -070092 assertEquals(NOT_SET, mModel.getPositionNearestOrigin());
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070093 }
94
Steve McKay53c8e802017-08-21 12:27:10 -070095 @Test
Kyle Horimoto1ed2c922015-08-06 10:29:37 -070096 public void testSelectionAboveItems() {
Steve McKaye45d1f02016-01-05 15:27:17 -080097 initData(20, 4);
Ben Kwa0ecd4c42016-03-14 11:56:47 -070098 startSelection(new Point(10, 0));
99 resizeSelection(new Point(11, 1));
100 assertNoSelection();
Steve McKay88456592017-08-24 10:09:01 -0700101 assertEquals(NOT_SET, mModel.getPositionNearestOrigin());
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700102 }
103
Steve McKay53c8e802017-08-21 12:27:10 -0700104 @Test
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700105 public void testSelectionBelowItems() {
Steve McKaye45d1f02016-01-05 15:27:17 -0800106 initData(5, 4);
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700107 startSelection(new Point(10, VIEWPORT_HEIGHT - 1));
108 resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2));
109 assertNoSelection();
Steve McKay88456592017-08-24 10:09:01 -0700110 assertEquals(NOT_SET, mModel.getPositionNearestOrigin());
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700111 }
112
Steve McKay53c8e802017-08-21 12:27:10 -0700113 @Test
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700114 public void testVerticalSelectionBetweenItems() {
Steve McKaye45d1f02016-01-05 15:27:17 -0800115 initData(20, 4);
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700116 startSelection(new Point(106, 0));
117 resizeSelection(new Point(107, 200));
118 assertNoSelection();
Steve McKay88456592017-08-24 10:09:01 -0700119 assertEquals(NOT_SET, mModel.getPositionNearestOrigin());
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700120 }
121
Steve McKay53c8e802017-08-21 12:27:10 -0700122 @Test
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700123 public void testHorizontalSelectionBetweenItems() {
Steve McKaye45d1f02016-01-05 15:27:17 -0800124 initData(20, 4);
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700125 startSelection(new Point(0, 105));
126 resizeSelection(new Point(200, 106));
127 assertNoSelection();
Steve McKay88456592017-08-24 10:09:01 -0700128 assertEquals(NOT_SET, mModel.getPositionNearestOrigin());
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700129 }
130
Steve McKay53c8e802017-08-21 12:27:10 -0700131 @Test
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700132 public void testGrowingAndShrinkingSelection() {
Steve McKaye45d1f02016-01-05 15:27:17 -0800133 initData(20, 4);
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700134 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 McKay88456592017-08-24 10:09:01 -0700172 assertEquals(NOT_SET, mModel.getPositionNearestOrigin());
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700173 }
174
Steve McKay53c8e802017-08-21 12:27:10 -0700175 @Test
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700176 public void testSelectionMovingAroundOrigin() {
Steve McKaye45d1f02016-01-05 15:27:17 -0800177 initData(16, 4);
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700178
179 startSelection(new Point(210, 210));
Steve McKay88456592017-08-24 10:09:01 -0700180 resizeSelection(new Point(mViewWidth - 1, 0));
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700181 verifySelection();
182
183 resizeSelection(new Point(0, 0));
184 verifySelection();
185
186 resizeSelection(new Point(0, 420));
187 verifySelection();
188
Steve McKay88456592017-08-24 10:09:01 -0700189 resizeSelection(new Point(mViewWidth - 1, 420));
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700190 verifySelection();
191
192 // This is manually figured and will need to be adjusted if the separator position is
193 // changed.
Steve McKay88456592017-08-24 10:09:01 -0700194 assertEquals(7, mModel.getPositionNearestOrigin());
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700195 }
196
Steve McKay53c8e802017-08-21 12:27:10 -0700197 @Test
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700198 public void testScrollingBandSelect() {
Steve McKaye45d1f02016-01-05 15:27:17 -0800199 initData(40, 4);
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700200
201 startSelection(new Point(0, 0));
202 resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
203 verifySelection();
204
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700205 scroll(CHILD_VIEW_EDGE_PX);
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700206 verifySelection();
207
208 resizeSelection(new Point(200, VIEWPORT_HEIGHT - 1));
209 verifySelection();
210
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700211 scroll(CHILD_VIEW_EDGE_PX);
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700212 verifySelection();
213
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700214 scroll(-2 * CHILD_VIEW_EDGE_PX);
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700215 verifySelection();
216
217 resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
218 verifySelection();
219
Steve McKay88456592017-08-24 10:09:01 -0700220 assertEquals(0, mModel.getPositionNearestOrigin());
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700221 }
222
Steve McKay53c8e802017-08-21 12:27:10 -0700223 private void initData(final int numChildren, int numColumns) {
Steve McKay88456592017-08-24 10:09:01 -0700224 mHost = new TestHost(numChildren, numColumns);
Steve McKay5a620372017-09-11 12:18:56 -0700225 mAdapter = new TestAdapter() {
Steve McKay53c8e802017-08-21 12:27:10 -0700226 @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 McKay88456592017-08-24 10:09:01 -0700237 mViewWidth = VIEW_PADDING_PX + numColumns * (VIEW_PADDING_PX + CHILD_VIEW_EDGE_PX);
238
Steve McKay5a620372017-09-11 12:18:56 -0700239 mModel = new GridModel(
240 mHost,
241 new TestStableIdProvider(mAdapter),
242 SelectionPredicates.CAN_SET_ANYTHING);
243
Steve McKay88456592017-08-24 10:09:01 -0700244 mModel.addOnSelectionChangedListener(
Steve McKay5a620372017-09-11 12:18:56 -0700245 new GridModel.SelectionObserver() {
Steve McKay53c8e802017-08-21 12:27:10 -0700246 @Override
247 public void onSelectionChanged(Set<String> updatedSelection) {
Steve McKay88456592017-08-24 10:09:01 -0700248 mLastSelection = updatedSelection;
Steve McKay53c8e802017-08-21 12:27:10 -0700249 }
250 });
251 }
252
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700253 /** 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 McKay88456592017-08-24 10:09:01 -0700268 assertEquals("Unexpected items " + mLastSelection + " in selection " + getSelectionArea(),
269 0, mLastSelection.size());
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700270 }
271
272 /** Verifies the selection using actual bbox checks. */
273 private void verifySelection() {
274 Rect selectionArea = getSelectionArea();
Steve McKay88456592017-08-24 10:09:01 -0700275 for (TestHost.Item item: mHost.items) {
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700276 if (Rect.intersects(selectionArea, item.rect)) {
277 assertTrue("Expected item " + item + " was not in selection " + selectionArea,
Steve McKay88456592017-08-24 10:09:01 -0700278 mLastSelection.contains(item.name));
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700279 } else {
280 assertFalse("Unexpected item " + item + " in selection" + selectionArea,
Steve McKay88456592017-08-24 10:09:01 -0700281 mLastSelection.contains(item.name));
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700282 }
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700283 }
284 }
285
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700286 private void startSelection(Point p) {
Steve McKay5bf51ec2017-09-29 13:43:48 -0700287 mModel.startCapturing(p);
Steve McKay88456592017-08-24 10:09:01 -0700288 mSelectionOrigin = mHost.createAbsolutePoint(p);
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700289 }
290
291 private void resizeSelection(Point p) {
Steve McKay88456592017-08-24 10:09:01 -0700292 mModel.resizeSelection(p);
293 mSelectionPoint = mHost.createAbsolutePoint(p);
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700294 }
295
Steve McKaye45d1f02016-01-05 15:27:17 -0800296 private void scroll(int dy) {
Steve McKay88456592017-08-24 10:09:01 -0700297 assertTrue(mHost.verticalOffset + VIEWPORT_HEIGHT + dy <= mHost.getTotalHeight());
298 mHost.verticalOffset += dy;
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700299 // Correct the cached selection point as well.
300 mSelectionPoint.y += dy;
Steve McKay88456592017-08-24 10:09:01 -0700301 mHost.mScrollListener.onScrolled(null, 0, dy);
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700302 }
303
Steve McKay5a620372017-09-11 12:18:56 -0700304 private static final class TestHost extends BandHost {
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700305
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700306 private final int mNumColumns;
307 private final int mNumRows;
308 private final int mNumChildren;
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700309 private final int mSeparatorPosition;
310
311 public int horizontalOffset = 0;
312 public int verticalOffset = 0;
313 private List<Item> items = new ArrayList<>();
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700314
Steve McKay88456592017-08-24 10:09:01 -0700315 // Installed by GridModel on construction.
316 private @Nullable OnScrollListener mScrollListener;
317
318 public TestHost(int numChildren, int numColumns) {
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700319 mNumChildren = numChildren;
320 mNumColumns = numColumns;
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700321 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 Horimoto1ed2c922015-08-06 10:29:37 -0700356 }
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 Kwa0ecd4c42016-03-14 11:56:47 -0700374 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 Horimoto1ed2c922015-08-06 10:29:37 -0700384 }
385
386 return mNumColumns;
387 }
388
389 @Override
Steve McKay88456592017-08-24 10:09:01 -0700390 public void addOnScrollListener(OnScrollListener listener) {
391 mScrollListener = listener;
392 }
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700393
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 Kwa936a7fc2015-12-10 15:21:18 -0800413 public int getAdapterPositionAt(int index) {
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700414 // 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 Kwa936a7fc2015-12-10 15:21:18 -0800420 }
421
422 @Override
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700423 public Rect getAbsoluteRectForChildViewAt(int index) {
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700424 int adapterPosition = getAdapterPositionAt(index);
425 return items.get(adapterPosition).rect;
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700426 }
427
428 @Override
Steve McKay37550162015-09-08 17:15:25 -0700429 public int getChildCount() {
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700430 return mNumChildren;
431 }
432
433 @Override
Steve McKay37550162015-09-08 17:15:25 -0700434 public int getColumnCount() {
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700435 return mNumColumns;
436 }
437
438 @Override
Steve McKay37550162015-09-08 17:15:25 -0700439 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 Kwaa017bc12015-10-07 14:15:12 -0700472
473 @Override
Tomasz Mikolajewskif2855f02016-04-14 14:22:00 +0900474 public boolean hasView(int adapterPosition) {
475 return true;
476 }
477
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700478 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 McKay53c8e802017-08-21 12:27:10 -0700487 @Override
Ben Kwa0ecd4c42016-03-14 11:56:47 -0700488 public String toString() {
489 return name + ": " + rect;
490 }
491 }
Kyle Horimoto1ed2c922015-08-06 10:29:37 -0700492 }
493}