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 package androidx.leanback.widget; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertFalse; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertNotSame; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertSame; 24 import static org.junit.Assert.assertTrue; 25 import static org.mockito.Mockito.any; 26 import static org.mockito.Mockito.anyInt; 27 import static org.mockito.Mockito.mock; 28 import static org.mockito.Mockito.timeout; 29 import static org.mockito.Mockito.times; 30 import static org.mockito.Mockito.verify; 31 32 import android.content.Intent; 33 import android.graphics.Canvas; 34 import android.graphics.Color; 35 import android.graphics.Rect; 36 import android.graphics.drawable.ColorDrawable; 37 import android.os.Build; 38 import android.os.Parcelable; 39 import android.support.test.InstrumentationRegistry; 40 import android.support.test.filters.LargeTest; 41 import android.support.test.filters.SdkSuppress; 42 import android.support.test.rule.ActivityTestRule; 43 import android.support.test.runner.AndroidJUnit4; 44 import android.text.Selection; 45 import android.text.Spannable; 46 import android.util.DisplayMetrics; 47 import android.util.SparseArray; 48 import android.util.SparseIntArray; 49 import android.util.TypedValue; 50 import android.view.KeyEvent; 51 import android.view.View; 52 import android.view.ViewGroup; 53 import android.widget.TextView; 54 55 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; 56 import androidx.leanback.test.R; 57 import androidx.leanback.testutils.PollingCheck; 58 import androidx.recyclerview.widget.DefaultItemAnimator; 59 import androidx.recyclerview.widget.RecyclerView; 60 import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate; 61 62 import org.junit.After; 63 import org.junit.Rule; 64 import org.junit.Test; 65 import org.junit.rules.TestName; 66 import org.junit.runner.RunWith; 67 import org.mockito.Mockito; 68 69 import java.util.ArrayList; 70 import java.util.Arrays; 71 import java.util.Comparator; 72 import java.util.HashMap; 73 import java.util.HashSet; 74 import java.util.List; 75 76 @LargeTest 77 @RunWith(AndroidJUnit4.class) 78 public class GridWidgetTest { 79 80 private static final float DELTA = 1f; 81 private static final boolean HUMAN_DELAY = false; 82 private static final long WAIT_FOR_SCROLL_IDLE_TIMEOUT_MS = 60000; 83 private static final int WAIT_FOR_LAYOUT_PASS_TIMEOUT_MS = 2000; 84 private static final int WAIT_FOR_ITEM_ANIMATION_FINISH_TIMEOUT_MS = 6000; 85 86 protected ActivityTestRule<GridActivity> mActivityTestRule; 87 protected GridActivity mActivity; 88 protected BaseGridView mGridView; 89 protected GridLayoutManager mLayoutManager; 90 private GridLayoutManager.OnLayoutCompleteListener mWaitLayoutListener; 91 protected int mOrientation; 92 protected int mNumRows; 93 protected int[] mRemovedItems; 94 95 private final Comparator<View> mRowSortComparator = new Comparator<View>() { 96 @Override 97 public int compare(View lhs, View rhs) { 98 if (mOrientation == BaseGridView.HORIZONTAL) { 99 return lhs.getLeft() - rhs.getLeft(); 100 } else { 101 return lhs.getTop() - rhs.getTop(); 102 } 103 }; 104 }; 105 106 /** 107 * Verify margins between items on same row are same. 108 */ 109 private final Runnable mVerifyLayout = new Runnable() { 110 @Override 111 public void run() { 112 verifyMargin(); 113 } 114 }; 115 116 @Rule public TestName testName = new TestName(); 117 118 public static void sendKey(int keyCode) { 119 InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keyCode); 120 } 121 122 public static void sendRepeatedKeys(int repeats, int keyCode) { 123 for (int i = 0; i < repeats; i++) { 124 InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keyCode); 125 } 126 } 127 128 private void humanDelay(int delay) throws InterruptedException { 129 if (HUMAN_DELAY) Thread.sleep(delay); 130 } 131 /** 132 * Change size of the Adapter and notifyDataSetChanged. 133 */ 134 private void changeArraySize(final int size) throws Throwable { 135 performAndWaitForAnimation(new Runnable() { 136 @Override 137 public void run() { 138 mActivity.changeArraySize(size); 139 } 140 }); 141 } 142 143 static String dumpGridView(BaseGridView gridView) { 144 return "findFocus:" + gridView.getRootView().findFocus() 145 + " isLayoutRequested:" + gridView.isLayoutRequested() 146 + " selectedPosition:" + gridView.getSelectedPosition() 147 + " adapter.itemCount:" + gridView.getAdapter().getItemCount() 148 + " itemAnimator.isRunning:" + gridView.getItemAnimator().isRunning() 149 + " scrollState:" + gridView.getScrollState(); 150 } 151 152 /** 153 * Change selected position. 154 */ 155 private void setSelectedPosition(final int position, final int scrollExtra) throws Throwable { 156 startWaitLayout(); 157 mActivityTestRule.runOnUiThread(new Runnable() { 158 @Override 159 public void run() { 160 mGridView.setSelectedPosition(position, scrollExtra); 161 } 162 }); 163 waitForLayout(false); 164 } 165 166 private void setSelectedPosition(final int position) throws Throwable { 167 setSelectedPosition(position, 0); 168 } 169 170 private void setSelectedPositionSmooth(final int position) throws Throwable { 171 mActivityTestRule.runOnUiThread(new Runnable() { 172 @Override 173 public void run() { 174 mGridView.setSelectedPositionSmooth(position); 175 } 176 }); 177 } 178 /** 179 * Scrolls using given key. 180 */ 181 protected void scroll(int key, Runnable verify) throws Throwable { 182 do { 183 if (verify != null) { 184 mActivityTestRule.runOnUiThread(verify); 185 } 186 sendRepeatedKeys(10, key); 187 try { 188 Thread.sleep(300); 189 } catch (InterruptedException ex) { 190 break; 191 } 192 } while (mGridView.getLayoutManager().isSmoothScrolling() 193 || mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE); 194 } 195 196 protected void scrollToBegin(Runnable verify) throws Throwable { 197 int key; 198 // first move to first column/row 199 if (mOrientation == BaseGridView.HORIZONTAL) { 200 key = KeyEvent.KEYCODE_DPAD_UP; 201 } else { 202 if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) { 203 key = KeyEvent.KEYCODE_DPAD_RIGHT; 204 } else { 205 key = KeyEvent.KEYCODE_DPAD_LEFT; 206 } 207 } 208 scroll(key, null); 209 if (mOrientation == BaseGridView.HORIZONTAL) { 210 if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) { 211 key = KeyEvent.KEYCODE_DPAD_RIGHT; 212 } else { 213 key = KeyEvent.KEYCODE_DPAD_LEFT; 214 } 215 } else { 216 key = KeyEvent.KEYCODE_DPAD_UP; 217 } 218 scroll(key, verify); 219 } 220 221 protected void scrollToEnd(Runnable verify) throws Throwable { 222 int key; 223 // first move to first column/row 224 if (mOrientation == BaseGridView.HORIZONTAL) { 225 key = KeyEvent.KEYCODE_DPAD_UP; 226 } else { 227 if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) { 228 key = KeyEvent.KEYCODE_DPAD_RIGHT; 229 } else { 230 key = KeyEvent.KEYCODE_DPAD_LEFT; 231 } 232 } 233 scroll(key, null); 234 if (mOrientation == BaseGridView.HORIZONTAL) { 235 if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) { 236 key = KeyEvent.KEYCODE_DPAD_LEFT; 237 } else { 238 key = KeyEvent.KEYCODE_DPAD_RIGHT; 239 } 240 } else { 241 key = KeyEvent.KEYCODE_DPAD_DOWN; 242 } 243 scroll(key, verify); 244 } 245 246 /** 247 * Group and sort children by their position on each row (HORIZONTAL) or column(VERTICAL). 248 */ 249 protected View[][] sortByRows() { 250 final HashMap<Integer, ArrayList<View>> rows = new HashMap<Integer, ArrayList<View>>(); 251 ArrayList<Integer> rowLocations = new ArrayList<>(); 252 for (int i = 0; i < mGridView.getChildCount(); i++) { 253 View v = mGridView.getChildAt(i); 254 int rowLocation; 255 if (mOrientation == BaseGridView.HORIZONTAL) { 256 rowLocation = v.getTop(); 257 } else { 258 rowLocation = mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL 259 ? v.getRight() : v.getLeft(); 260 } 261 ArrayList<View> views = rows.get(rowLocation); 262 if (views == null) { 263 views = new ArrayList<View>(); 264 rows.put(rowLocation, views); 265 rowLocations.add(rowLocation); 266 } 267 views.add(v); 268 } 269 Object[] sortedLocations = rowLocations.toArray(); 270 Arrays.sort(sortedLocations); 271 if (mNumRows != rows.size()) { 272 assertEquals("Dump Views by rows "+rows, mNumRows, rows.size()); 273 } 274 View[][] sorted = new View[rows.size()][]; 275 for (int i = 0; i < rowLocations.size(); i++) { 276 Integer rowLocation = rowLocations.get(i); 277 ArrayList<View> arr = rows.get(rowLocation); 278 View[] views = arr.toArray(new View[arr.size()]); 279 Arrays.sort(views, mRowSortComparator); 280 sorted[i] = views; 281 } 282 return sorted; 283 } 284 285 protected void verifyMargin() { 286 View[][] sorted = sortByRows(); 287 for (int row = 0; row < sorted.length; row++) { 288 View[] views = sorted[row]; 289 int margin = -1; 290 for (int i = 1; i < views.length; i++) { 291 if (mOrientation == BaseGridView.HORIZONTAL) { 292 assertEquals(mGridView.getHorizontalMargin(), 293 views[i].getLeft() - views[i - 1].getRight()); 294 } else { 295 assertEquals(mGridView.getVerticalMargin(), 296 views[i].getTop() - views[i - 1].getBottom()); 297 } 298 } 299 } 300 } 301 302 protected void verifyBeginAligned() { 303 View[][] sorted = sortByRows(); 304 int alignedLocation = 0; 305 if (mOrientation == BaseGridView.HORIZONTAL) { 306 if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) { 307 for (int i = 0; i < sorted.length; i++) { 308 if (i == 0) { 309 alignedLocation = sorted[i][sorted[i].length - 1].getRight(); 310 } else { 311 assertEquals(alignedLocation, sorted[i][sorted[i].length - 1].getRight()); 312 } 313 } 314 } else { 315 for (int i = 0; i < sorted.length; i++) { 316 if (i == 0) { 317 alignedLocation = sorted[i][0].getLeft(); 318 } else { 319 assertEquals(alignedLocation, sorted[i][0].getLeft()); 320 } 321 } 322 } 323 } else { 324 for (int i = 0; i < sorted.length; i++) { 325 if (i == 0) { 326 alignedLocation = sorted[i][0].getTop(); 327 } else { 328 assertEquals(alignedLocation, sorted[i][0].getTop()); 329 } 330 } 331 } 332 } 333 334 protected int[] getEndEdges() { 335 View[][] sorted = sortByRows(); 336 int[] edges = new int[sorted.length]; 337 if (mOrientation == BaseGridView.HORIZONTAL) { 338 if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) { 339 for (int i = 0; i < sorted.length; i++) { 340 edges[i] = sorted[i][0].getLeft(); 341 } 342 } else { 343 for (int i = 0; i < sorted.length; i++) { 344 edges[i] = sorted[i][sorted[i].length - 1].getRight(); 345 } 346 } 347 } else { 348 for (int i = 0; i < sorted.length; i++) { 349 edges[i] = sorted[i][sorted[i].length - 1].getBottom(); 350 } 351 } 352 return edges; 353 } 354 355 protected void verifyEdgesSame(int[] edges, int[] edges2) { 356 assertEquals(edges.length, edges2.length); 357 for (int i = 0; i < edges.length; i++) { 358 assertEquals(edges[i], edges2[i]); 359 } 360 } 361 362 protected void verifyBoundCount(int count) { 363 if (mActivity.getBoundCount() != count) { 364 StringBuffer b = new StringBuffer(); 365 b.append("ItemsLength: "); 366 for (int i = 0; i < mActivity.mItemLengths.length; i++) { 367 b.append(mActivity.mItemLengths[i]).append(","); 368 } 369 assertEquals("Bound count does not match, ItemsLengths: "+ b, 370 count, mActivity.getBoundCount()); 371 } 372 } 373 374 private static int getCenterY(View v) { 375 return (v.getTop() + v.getBottom())/2; 376 } 377 378 private static int getCenterX(View v) { 379 return (v.getLeft() + v.getRight())/2; 380 } 381 382 private void initActivity(Intent intent) throws Throwable { 383 mActivityTestRule = new ActivityTestRule<GridActivity>(GridActivity.class, false, false); 384 mActivity = mActivityTestRule.launchActivity(intent); 385 mActivityTestRule.runOnUiThread(new Runnable() { 386 @Override 387 public void run() { 388 mActivity.setTitle(testName.getMethodName()); 389 } 390 }); 391 Thread.sleep(1000); 392 mGridView = mActivity.mGridView; 393 mLayoutManager = (GridLayoutManager) mGridView.getLayoutManager(); 394 } 395 396 @After 397 public void clearTest() { 398 mWaitLayoutListener = null; 399 mLayoutManager = null; 400 mGridView = null; 401 mActivity = null; 402 mActivityTestRule = null; 403 } 404 405 /** 406 * Must be called before waitForLayout() to prepare layout listener. 407 */ 408 protected void startWaitLayout() { 409 if (mWaitLayoutListener != null) { 410 throw new IllegalStateException("startWaitLayout() already called"); 411 } 412 if (mLayoutManager.mLayoutCompleteListener != null) { 413 throw new IllegalStateException("Cannot startWaitLayout()"); 414 } 415 mWaitLayoutListener = mLayoutManager.mLayoutCompleteListener = 416 mock(GridLayoutManager.OnLayoutCompleteListener.class); 417 } 418 419 /** 420 * wait layout to be called and remove the listener. 421 */ 422 protected void waitForLayout() { 423 waitForLayout(true); 424 } 425 426 /** 427 * wait layout to be called and remove the listener. 428 * @param force True if always wait regardless if layout requested 429 */ 430 protected void waitForLayout(boolean force) { 431 if (mWaitLayoutListener == null) { 432 throw new IllegalStateException("startWaitLayout() not called"); 433 } 434 if (mWaitLayoutListener != mLayoutManager.mLayoutCompleteListener) { 435 throw new IllegalStateException("layout listener inconistent"); 436 } 437 try { 438 if (force || mGridView.isLayoutRequested()) { 439 verify(mWaitLayoutListener, timeout(WAIT_FOR_LAYOUT_PASS_TIMEOUT_MS).atLeastOnce()) 440 .onLayoutCompleted(any(RecyclerView.State.class)); 441 } 442 } finally { 443 mWaitLayoutListener = null; 444 mLayoutManager.mLayoutCompleteListener = null; 445 } 446 } 447 448 /** 449 * If currently running animator, wait for it to finish, otherwise return immediately. 450 * To wait the ItemAnimator start, you can use waitForLayout() to make sure layout pass has 451 * processed adapter change. 452 */ 453 protected void waitForItemAnimation(int timeoutMs) throws Throwable { 454 final RecyclerView.ItemAnimator.ItemAnimatorFinishedListener listener = mock( 455 RecyclerView.ItemAnimator.ItemAnimatorFinishedListener.class); 456 mActivityTestRule.runOnUiThread(new Runnable() { 457 @Override 458 public void run() { 459 mGridView.getItemAnimator().isRunning(listener); 460 } 461 }); 462 verify(listener, timeout(timeoutMs).atLeastOnce()).onAnimationsFinished(); 463 } 464 465 protected void waitForItemAnimation() throws Throwable { 466 waitForItemAnimation(WAIT_FOR_ITEM_ANIMATION_FINISH_TIMEOUT_MS); 467 } 468 469 /** 470 * wait animation start 471 */ 472 protected void waitForItemAnimationStart() throws Throwable { 473 long totalWait = 0; 474 while (!mGridView.getItemAnimator().isRunning()) { 475 Thread.sleep(10); 476 if ((totalWait += 10) > WAIT_FOR_ITEM_ANIMATION_FINISH_TIMEOUT_MS) { 477 throw new RuntimeException("waitForItemAnimationStart Timeout"); 478 } 479 } 480 } 481 482 /** 483 * Run task in UI thread and wait for layout and ItemAnimator finishes. 484 */ 485 protected void performAndWaitForAnimation(Runnable task) throws Throwable { 486 startWaitLayout(); 487 mActivityTestRule.runOnUiThread(task); 488 waitForLayout(); 489 waitForItemAnimation(); 490 } 491 492 protected void waitForScrollIdle() throws Throwable { 493 waitForScrollIdle(null); 494 } 495 496 /** 497 * Wait for grid view stop scroll and optionally verify state of grid view. 498 */ 499 protected void waitForScrollIdle(Runnable verify) throws Throwable { 500 Thread.sleep(100); 501 int total = 0; 502 while (mGridView.getLayoutManager().isSmoothScrolling() 503 || mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE) { 504 if ((total += 100) >= WAIT_FOR_SCROLL_IDLE_TIMEOUT_MS) { 505 throw new RuntimeException("waitForScrollIdle Timeout"); 506 } 507 try { 508 Thread.sleep(100); 509 } catch (InterruptedException ex) { 510 break; 511 } 512 if (verify != null) { 513 mActivityTestRule.runOnUiThread(verify); 514 } 515 } 516 } 517 518 @Test 519 public void testThreeRowHorizontalBasic() throws Throwable { 520 Intent intent = new Intent(); 521 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid); 522 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100); 523 initActivity(intent); 524 mOrientation = BaseGridView.HORIZONTAL; 525 mNumRows = 3; 526 527 scrollToEnd(mVerifyLayout); 528 529 scrollToBegin(mVerifyLayout); 530 531 verifyBeginAligned(); 532 } 533 534 static class DividerDecoration extends RecyclerView.ItemDecoration { 535 536 private ColorDrawable mTopDivider; 537 private ColorDrawable mBottomDivider; 538 private int mLeftOffset; 539 private int mRightOffset; 540 private int mTopOffset; 541 private int mBottomOffset; 542 543 DividerDecoration(int leftOffset, int topOffset, int rightOffset, int bottomOffset) { 544 mLeftOffset = leftOffset; 545 mTopOffset = topOffset; 546 mRightOffset = rightOffset; 547 mBottomOffset = bottomOffset; 548 } 549 550 @Override 551 public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { 552 if (mTopDivider == null) { 553 mTopDivider = new ColorDrawable(Color.RED); 554 } 555 if (mBottomDivider == null) { 556 mBottomDivider = new ColorDrawable(Color.BLUE); 557 } 558 final int childCount = parent.getChildCount(); 559 final int width = parent.getWidth(); 560 for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) { 561 final View view = parent.getChildAt(childViewIndex); 562 mTopDivider.setBounds(0, (int) view.getY() - mTopOffset, width, (int) view.getY()); 563 mTopDivider.draw(c); 564 mBottomDivider.setBounds(0, (int) view.getY() + view.getHeight(), width, 565 (int) view.getY() + view.getHeight() + mBottomOffset); 566 mBottomDivider.draw(c); 567 } 568 } 569 570 @Override 571 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 572 RecyclerView.State state) { 573 outRect.left = mLeftOffset; 574 outRect.top = mTopOffset; 575 outRect.right = mRightOffset; 576 outRect.bottom = mBottomOffset; 577 } 578 } 579 580 @Test 581 public void testItemDecorationAndMargins() throws Throwable { 582 583 final int leftMargin = 3; 584 final int topMargin = 4; 585 final int rightMargin = 7; 586 final int bottomMargin = 8; 587 final int itemHeight = 100; 588 589 Intent intent = new Intent(); 590 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 591 intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{itemHeight, itemHeight, itemHeight}); 592 intent.putExtra(GridActivity.EXTRA_LAYOUT_MARGINS, 593 new int[]{leftMargin, topMargin, rightMargin, bottomMargin}); 594 initActivity(intent); 595 mOrientation = BaseGridView.VERTICAL; 596 mNumRows = 1; 597 598 final int paddingLeft = mGridView.getPaddingLeft(); 599 final int paddingTop = mGridView.getPaddingTop(); 600 final int verticalSpace = mGridView.getVerticalMargin(); 601 final int decorationLeft = 17; 602 final int decorationTop = 1; 603 final int decorationRight = 19; 604 final int decorationBottom = 2; 605 606 performAndWaitForAnimation(new Runnable() { 607 @Override 608 public void run() { 609 mGridView.addItemDecoration(new DividerDecoration(decorationLeft, decorationTop, 610 decorationRight, decorationBottom)); 611 } 612 }); 613 614 View child0 = mGridView.getChildAt(0); 615 View child1 = mGridView.getChildAt(1); 616 View child2 = mGridView.getChildAt(2); 617 618 assertEquals(itemHeight, child0.getBottom() - child0.getTop()); 619 620 // verify left margins 621 assertEquals(paddingLeft + leftMargin + decorationLeft, child0.getLeft()); 622 assertEquals(paddingLeft + leftMargin + decorationLeft, child1.getLeft()); 623 assertEquals(paddingLeft + leftMargin + decorationLeft, child2.getLeft()); 624 // verify top bottom margins and decoration offset 625 assertEquals(paddingTop + topMargin + decorationTop, child0.getTop()); 626 assertEquals(bottomMargin + decorationBottom + verticalSpace + decorationTop + topMargin, 627 child1.getTop() - child0.getBottom()); 628 assertEquals(bottomMargin + decorationBottom + verticalSpace + decorationTop + topMargin, 629 child2.getTop() - child1.getBottom()); 630 631 } 632 633 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP) 634 @Test 635 public void testItemDecorationAndMarginsAndOpticalBounds() throws Throwable { 636 final int leftMargin = 3; 637 final int topMargin = 4; 638 final int rightMargin = 7; 639 final int bottomMargin = 8; 640 final int itemHeight = 100; 641 final int ninePatchDrawableResourceId = R.drawable.lb_card_shadow_focused; 642 643 Intent intent = new Intent(); 644 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 645 intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{itemHeight, itemHeight, itemHeight}); 646 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout); 647 intent.putExtra(GridActivity.EXTRA_LAYOUT_MARGINS, 648 new int[]{leftMargin, topMargin, rightMargin, bottomMargin}); 649 intent.putExtra(GridActivity.EXTRA_NINEPATCH_SHADOW, ninePatchDrawableResourceId); 650 initActivity(intent); 651 mOrientation = BaseGridView.VERTICAL; 652 mNumRows = 1; 653 654 final int paddingLeft = mGridView.getPaddingLeft(); 655 final int paddingTop = mGridView.getPaddingTop(); 656 final int verticalSpace = mGridView.getVerticalMargin(); 657 final int decorationLeft = 17; 658 final int decorationTop = 1; 659 final int decorationRight = 19; 660 final int decorationBottom = 2; 661 662 final Rect opticalPaddings = new Rect(); 663 mGridView.getResources().getDrawable(ninePatchDrawableResourceId) 664 .getPadding(opticalPaddings); 665 final int opticalInsetsLeft = opticalPaddings.left; 666 final int opticalInsetsTop = opticalPaddings.top; 667 final int opticalInsetsRight = opticalPaddings.right; 668 final int opticalInsetsBottom = opticalPaddings.bottom; 669 assertTrue(opticalInsetsLeft > 0); 670 assertTrue(opticalInsetsTop > 0); 671 assertTrue(opticalInsetsRight > 0); 672 assertTrue(opticalInsetsBottom > 0); 673 674 performAndWaitForAnimation(new Runnable() { 675 @Override 676 public void run() { 677 mGridView.addItemDecoration(new DividerDecoration(decorationLeft, decorationTop, 678 decorationRight, decorationBottom)); 679 } 680 }); 681 682 View child0 = mGridView.getChildAt(0); 683 View child1 = mGridView.getChildAt(1); 684 View child2 = mGridView.getChildAt(2); 685 686 assertEquals(itemHeight + opticalInsetsTop + opticalInsetsBottom, 687 child0.getBottom() - child0.getTop()); 688 689 // verify left margins decoration and optical insets 690 assertEquals(paddingLeft + leftMargin + decorationLeft - opticalInsetsLeft, 691 child0.getLeft()); 692 assertEquals(paddingLeft + leftMargin + decorationLeft - opticalInsetsLeft, 693 child1.getLeft()); 694 assertEquals(paddingLeft + leftMargin + decorationLeft - opticalInsetsLeft, 695 child2.getLeft()); 696 // verify top bottom margins decoration offset and optical insets 697 assertEquals(paddingTop + topMargin + decorationTop, child0.getTop() + opticalInsetsTop); 698 assertEquals(bottomMargin + decorationBottom + verticalSpace + decorationTop + topMargin, 699 (child1.getTop() + opticalInsetsTop) - (child0.getBottom() - opticalInsetsBottom)); 700 assertEquals(bottomMargin + decorationBottom + verticalSpace + decorationTop + topMargin, 701 (child2.getTop() + opticalInsetsTop) - (child1.getBottom() - opticalInsetsBottom)); 702 703 } 704 705 @Test 706 public void testThreeColumnVerticalBasic() throws Throwable { 707 708 Intent intent = new Intent(); 709 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid); 710 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200); 711 initActivity(intent); 712 mOrientation = BaseGridView.VERTICAL; 713 mNumRows = 3; 714 715 scrollToEnd(mVerifyLayout); 716 717 scrollToBegin(mVerifyLayout); 718 719 verifyBeginAligned(); 720 } 721 722 @Test 723 public void testRedundantAppendRemove() throws Throwable { 724 Intent intent = new Intent(); 725 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 726 R.layout.vertical_grid_testredundantappendremove); 727 intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{ 728 149,177,128,234,227,187,163,223,146,210,228,148,227,193,182,197,177,142,225,207, 729 157,171,209,204,187,184,123,221,197,153,202,179,193,214,226,173,225,143,188,159, 730 139,193,233,143,227,203,222,124,228,223,164,131,228,126,211,160,165,152,235,184, 731 155,224,149,181,171,229,200,234,177,130,164,172,188,139,132,203,179,220,147,131, 732 226,127,230,239,183,203,206,227,123,170,239,234,200,149,237,204,160,133,202,234, 733 173,122,139,149,151,153,216,231,121,145,227,153,186,174,223,180,123,215,206,216, 734 239,222,219,207,193,218,140,133,171,153,183,132,233,138,159,174,189,171,143,128, 735 152,222,141,202,224,190,134,120,181,231,230,136,132,224,136,210,207,150,128,183, 736 221,194,179,220,126,221,137,205,223,193,172,132,226,209,133,191,227,127,159,171, 737 180,149,237,177,194,207,170,202,161,144,147,199,205,186,164,140,193,203,224,129}); 738 initActivity(intent); 739 mOrientation = BaseGridView.VERTICAL; 740 mNumRows = 3; 741 742 scrollToEnd(mVerifyLayout); 743 744 scrollToBegin(mVerifyLayout); 745 746 verifyBeginAligned(); 747 } 748 749 @Test 750 public void testRedundantAppendRemove2() throws Throwable { 751 Intent intent = new Intent(); 752 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 753 R.layout.horizontal_grid_testredundantappendremove2); 754 intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{ 755 318,333,199,224,246,273,269,289,340,313,265,306,349,269,185,282,257,354,316,252, 756 237,290,283,343,196,313,290,343,191,262,342,228,343,349,251,203,226,305,265,213, 757 216,333,295,188,187,281,288,311,244,232,224,332,290,181,267,276,226,261,335,355, 758 225,217,219,183,234,285,257,304,182,250,244,223,257,219,342,185,347,205,302,315, 759 299,309,292,237,192,309,228,250,347,227,337,298,299,185,185,331,223,284,265,351}); 760 initActivity(intent); 761 mOrientation = BaseGridView.HORIZONTAL; 762 mNumRows = 3; 763 mLayoutManager = (GridLayoutManager) mGridView.getLayoutManager(); 764 765 // test append without staggered result cache 766 scrollToEnd(mVerifyLayout); 767 768 int[] endEdges = getEndEdges(); 769 770 scrollToBegin(mVerifyLayout); 771 772 verifyBeginAligned(); 773 774 // now test append with staggered result cache 775 changeArraySize(3); 776 assertEquals("Staggerd cache should be kept as is when no item size change", 777 100, ((StaggeredGrid) mLayoutManager.mGrid).mLocations.size()); 778 779 changeArraySize(100); 780 781 scrollToEnd(mVerifyLayout); 782 783 // we should get same aligned end edges 784 int[] endEdges2 = getEndEdges(); 785 verifyEdgesSame(endEdges, endEdges2); 786 } 787 788 789 @Test 790 public void testLayoutWhenAViewIsInvalidated() throws Throwable { 791 Intent intent = new Intent(); 792 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 793 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000); 794 intent.putExtra(GridActivity.EXTRA_HAS_STABLE_IDS, true); 795 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 796 mNumRows = 1; 797 initActivity(intent); 798 mOrientation = BaseGridView.VERTICAL; 799 waitOneUiCycle(); 800 801 // push views to cache. 802 mActivityTestRule.runOnUiThread(new Runnable() { 803 @Override 804 public void run() { 805 mActivity.mItemLengths[0] = mActivity.mItemLengths[0] * 3; 806 mActivity.mGridView.getAdapter().notifyItemChanged(0); 807 } 808 }); 809 waitForItemAnimation(); 810 811 // notifyDataSetChange will mark the cached views FLAG_INVALID 812 mActivityTestRule.runOnUiThread(new Runnable() { 813 @Override 814 public void run() { 815 mActivity.mGridView.getAdapter().notifyDataSetChanged(); 816 } 817 }); 818 waitForItemAnimation(); 819 820 // Cached views will be added in prelayout with FLAG_INVALID, in post layout we should 821 // handle it properly 822 mActivityTestRule.runOnUiThread(new Runnable() { 823 @Override 824 public void run() { 825 mActivity.mItemLengths[0] = mActivity.mItemLengths[0] / 3; 826 mActivity.mGridView.getAdapter().notifyItemChanged(0); 827 } 828 }); 829 830 waitForItemAnimation(); 831 } 832 833 @Test 834 public void testWrongInsertViewIndexInFastRelayout() throws Throwable { 835 Intent intent = new Intent(); 836 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 837 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2); 838 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 839 mNumRows = 1; 840 initActivity(intent); 841 mOrientation = BaseGridView.VERTICAL; 842 843 // removing two children, they will be hidden views as first 2 children of RV. 844 mActivityTestRule.runOnUiThread(new Runnable() { 845 @Override 846 public void run() { 847 mGridView.getItemAnimator().setRemoveDuration(2000); 848 mActivity.removeItems(0, 2); 849 } 850 }); 851 waitForItemAnimationStart(); 852 853 // add three views and notify change of the first item. 854 startWaitLayout(); 855 mActivityTestRule.runOnUiThread(new Runnable() { 856 @Override 857 public void run() { 858 mActivity.addItems(0, new int[]{161, 161, 161}); 859 } 860 }); 861 waitForLayout(); 862 startWaitLayout(); 863 mActivityTestRule.runOnUiThread(new Runnable() { 864 @Override 865 public void run() { 866 mGridView.getAdapter().notifyItemChanged(0); 867 } 868 }); 869 waitForLayout(); 870 // after layout, the viewholder should still be the first child of LayoutManager. 871 assertEquals(0, mGridView.getChildAdapterPosition( 872 mGridView.getLayoutManager().getChildAt(0))); 873 } 874 875 @Test 876 public void testMoveIntoPrelayoutItems() throws Throwable { 877 Intent intent = new Intent(); 878 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 879 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000); 880 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 881 mNumRows = 1; 882 initActivity(intent); 883 mOrientation = BaseGridView.VERTICAL; 884 885 final int lastItemPos = mGridView.getChildCount() - 1; 886 assertTrue(mGridView.getChildCount() >= 4); 887 // notify change of 3 items, so prelayout will layout extra 3 items, then move an item 888 // into the extra layout range. Post layout's fastRelayout() should handle this properly. 889 mActivityTestRule.runOnUiThread(new Runnable() { 890 @Override 891 public void run() { 892 mGridView.getAdapter().notifyItemChanged(lastItemPos - 3); 893 mGridView.getAdapter().notifyItemChanged(lastItemPos - 2); 894 mGridView.getAdapter().notifyItemChanged(lastItemPos - 1); 895 mActivity.moveItem(900, lastItemPos + 2, true); 896 } 897 }); 898 waitForItemAnimation(); 899 } 900 901 @Test 902 public void testMoveIntoPrelayoutItems2() throws Throwable { 903 Intent intent = new Intent(); 904 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 905 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000); 906 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 907 mNumRows = 1; 908 initActivity(intent); 909 mOrientation = BaseGridView.VERTICAL; 910 911 setSelectedPosition(999); 912 final int firstItemPos = mGridView.getChildAdapterPosition(mGridView.getChildAt(0)); 913 assertTrue(mGridView.getChildCount() >= 4); 914 // notify change of 3 items, so prelayout will layout extra 3 items, then move an item 915 // into the extra layout range. Post layout's fastRelayout() should handle this properly. 916 mActivityTestRule.runOnUiThread(new Runnable() { 917 @Override 918 public void run() { 919 mGridView.getAdapter().notifyItemChanged(firstItemPos + 1); 920 mGridView.getAdapter().notifyItemChanged(firstItemPos + 2); 921 mGridView.getAdapter().notifyItemChanged(firstItemPos + 3); 922 mActivity.moveItem(0, firstItemPos - 2, true); 923 } 924 }); 925 waitForItemAnimation(); 926 } 927 928 void preparePredictiveLayout() throws Throwable { 929 Intent intent = new Intent(); 930 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear); 931 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100); 932 initActivity(intent); 933 mOrientation = BaseGridView.HORIZONTAL; 934 mNumRows = 1; 935 936 mActivityTestRule.runOnUiThread(new Runnable() { 937 @Override 938 public void run() { 939 mGridView.getItemAnimator().setAddDuration(1000); 940 mGridView.getItemAnimator().setRemoveDuration(1000); 941 mGridView.getItemAnimator().setMoveDuration(1000); 942 mGridView.getItemAnimator().setChangeDuration(1000); 943 mGridView.setSelectedPositionSmooth(50); 944 } 945 }); 946 waitForScrollIdle(mVerifyLayout); 947 } 948 949 @Test 950 public void testPredictiveLayoutAdd1() throws Throwable { 951 preparePredictiveLayout(); 952 mActivityTestRule.runOnUiThread(new Runnable() { 953 @Override 954 public void run() { 955 mActivity.addItems(51, new int[]{300, 300, 300, 300}); 956 } 957 }); 958 waitForItemAnimationStart(); 959 waitForItemAnimation(); 960 assertEquals(50, mGridView.getSelectedPosition()); 961 assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState()); 962 } 963 964 @Test 965 public void testPredictiveLayoutAdd2() throws Throwable { 966 preparePredictiveLayout(); 967 mActivityTestRule.runOnUiThread(new Runnable() { 968 @Override 969 public void run() { 970 mActivity.addItems(50, new int[]{300, 300, 300, 300}); 971 } 972 }); 973 waitForItemAnimationStart(); 974 waitForItemAnimation(); 975 assertEquals(54, mGridView.getSelectedPosition()); 976 assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState()); 977 } 978 979 @Test 980 public void testPredictiveLayoutRemove1() throws Throwable { 981 preparePredictiveLayout(); 982 mActivityTestRule.runOnUiThread(new Runnable() { 983 @Override 984 public void run() { 985 mActivity.removeItems(51, 3); 986 } 987 }); 988 waitForItemAnimationStart(); 989 waitForItemAnimation(); 990 assertEquals(50, mGridView.getSelectedPosition()); 991 assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState()); 992 } 993 994 @Test 995 public void testPredictiveLayoutRemove2() throws Throwable { 996 preparePredictiveLayout(); 997 mActivityTestRule.runOnUiThread(new Runnable() { 998 @Override 999 public void run() { 1000 mActivity.removeItems(47, 3); 1001 } 1002 }); 1003 waitForItemAnimationStart(); 1004 waitForItemAnimation(); 1005 assertEquals(47, mGridView.getSelectedPosition()); 1006 assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState()); 1007 } 1008 1009 @Test 1010 public void testPredictiveLayoutRemove3() throws Throwable { 1011 preparePredictiveLayout(); 1012 mActivityTestRule.runOnUiThread(new Runnable() { 1013 @Override 1014 public void run() { 1015 mActivity.removeItems(0, 51); 1016 } 1017 }); 1018 waitForItemAnimationStart(); 1019 waitForItemAnimation(); 1020 assertEquals(0, mGridView.getSelectedPosition()); 1021 assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState()); 1022 } 1023 1024 @Test 1025 public void testPredictiveOnMeasureWrapContent() throws Throwable { 1026 Intent intent = new Intent(); 1027 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1028 R.layout.horizontal_linear_wrap_content); 1029 int count = 50; 1030 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, count); 1031 initActivity(intent); 1032 mOrientation = BaseGridView.HORIZONTAL; 1033 mNumRows = 1; 1034 1035 waitForScrollIdle(mVerifyLayout); 1036 mActivityTestRule.runOnUiThread(new Runnable() { 1037 @Override 1038 public void run() { 1039 mGridView.setHasFixedSize(false); 1040 } 1041 }); 1042 1043 for (int i = 0; i < 30; i++) { 1044 final int oldCount = count; 1045 final int newCount = i; 1046 mActivityTestRule.runOnUiThread(new Runnable() { 1047 @Override 1048 public void run() { 1049 if (oldCount > 0) { 1050 mActivity.removeItems(0, oldCount); 1051 } 1052 if (newCount > 0) { 1053 int[] newItems = new int[newCount]; 1054 for (int i = 0; i < newCount; i++) { 1055 newItems[i] = 400; 1056 } 1057 mActivity.addItems(0, newItems); 1058 } 1059 } 1060 }); 1061 waitForItemAnimationStart(); 1062 waitForItemAnimation(); 1063 count = newCount; 1064 } 1065 1066 } 1067 1068 @Test 1069 public void testPredictiveLayoutRemove4() throws Throwable { 1070 Intent intent = new Intent(); 1071 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1072 R.layout.horizontal_grid); 1073 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200); 1074 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 1075 initActivity(intent); 1076 mOrientation = BaseGridView.HORIZONTAL; 1077 mNumRows = 3; 1078 1079 mActivityTestRule.runOnUiThread(new Runnable() { 1080 @Override 1081 public void run() { 1082 mGridView.setSelectedPositionSmooth(50); 1083 } 1084 }); 1085 waitForScrollIdle(); 1086 performAndWaitForAnimation(new Runnable() { 1087 @Override 1088 public void run() { 1089 mActivity.removeItems(0, 49); 1090 } 1091 }); 1092 assertEquals(1, mGridView.getSelectedPosition()); 1093 } 1094 1095 @Test 1096 public void testPredictiveLayoutRemove5() throws Throwable { 1097 Intent intent = new Intent(); 1098 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1099 R.layout.horizontal_grid); 1100 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200); 1101 intent.putExtra(GridActivity.EXTRA_STAGGERED, true); 1102 initActivity(intent); 1103 mOrientation = BaseGridView.HORIZONTAL; 1104 mNumRows = 3; 1105 1106 mActivityTestRule.runOnUiThread(new Runnable() { 1107 @Override 1108 public void run() { 1109 mGridView.setSelectedPositionSmooth(50); 1110 } 1111 }); 1112 waitForScrollIdle(); 1113 performAndWaitForAnimation(new Runnable() { 1114 @Override 1115 public void run() { 1116 mActivity.removeItems(50, 40); 1117 } 1118 }); 1119 assertEquals(50, mGridView.getSelectedPosition()); 1120 scrollToBegin(mVerifyLayout); 1121 verifyBeginAligned(); 1122 } 1123 1124 void waitOneUiCycle() throws Throwable { 1125 mActivityTestRule.runOnUiThread(new Runnable() { 1126 @Override 1127 public void run() { 1128 } 1129 }); 1130 } 1131 1132 @Test 1133 public void testDontPruneMovingItem() throws Throwable { 1134 Intent intent = new Intent(); 1135 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear); 1136 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 1137 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000); 1138 initActivity(intent); 1139 mOrientation = BaseGridView.HORIZONTAL; 1140 mNumRows = 1; 1141 1142 mActivityTestRule.runOnUiThread(new Runnable() { 1143 @Override 1144 public void run() { 1145 mGridView.getItemAnimator().setMoveDuration(2000); 1146 mGridView.setSelectedPosition(50); 1147 } 1148 }); 1149 waitForScrollIdle(); 1150 final ArrayList<RecyclerView.ViewHolder> moveViewHolders = new ArrayList(); 1151 for (int i = 51;; i++) { 1152 RecyclerView.ViewHolder vh = mGridView.findViewHolderForAdapterPosition(i); 1153 if (vh == null) { 1154 break; 1155 } 1156 moveViewHolders.add(vh); 1157 } 1158 1159 mActivityTestRule.runOnUiThread(new Runnable() { 1160 @Override 1161 public void run() { 1162 // add a lot of items, so we will push everything to right of 51 out side window 1163 int[] lots_items = new int[1000]; 1164 for (int i = 0; i < lots_items.length; i++) { 1165 lots_items[i] = 300; 1166 } 1167 mActivity.addItems(51, lots_items); 1168 } 1169 }); 1170 waitOneUiCycle(); 1171 // run a scroll pass, the scroll pass should not remove the animating views even they are 1172 // outside visible areas. 1173 mActivityTestRule.runOnUiThread(new Runnable() { 1174 @Override 1175 public void run() { 1176 mGridView.scrollBy(-3, 0); 1177 } 1178 }); 1179 waitOneUiCycle(); 1180 for (int i = 0; i < moveViewHolders.size(); i++) { 1181 assertSame(mGridView, moveViewHolders.get(i).itemView.getParent()); 1182 } 1183 } 1184 1185 @Test 1186 public void testMoveItemToTheRight() throws Throwable { 1187 Intent intent = new Intent(); 1188 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear); 1189 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 1190 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000); 1191 initActivity(intent); 1192 mOrientation = BaseGridView.HORIZONTAL; 1193 mNumRows = 1; 1194 1195 mActivityTestRule.runOnUiThread(new Runnable() { 1196 @Override 1197 public void run() { 1198 mGridView.getItemAnimator().setAddDuration(2000); 1199 mGridView.getItemAnimator().setMoveDuration(2000); 1200 mGridView.setSelectedPosition(50); 1201 } 1202 }); 1203 waitForScrollIdle(); 1204 RecyclerView.ViewHolder moveViewHolder = mGridView.findViewHolderForAdapterPosition(51); 1205 1206 int lastPos = mGridView.getChildAdapterPosition(mGridView.getChildAt( 1207 mGridView.getChildCount() - 1)); 1208 mActivityTestRule.runOnUiThread(new Runnable() { 1209 @Override 1210 public void run() { 1211 mActivity.moveItem(51, 1000, true); 1212 } 1213 }); 1214 final ArrayList<View> moveInViewHolders = new ArrayList(); 1215 waitForItemAnimationStart(); 1216 mActivityTestRule.runOnUiThread(new Runnable() { 1217 @Override 1218 public void run() { 1219 for (int i = 0; i < mGridView.getLayoutManager().getChildCount(); i++) { 1220 View v = mGridView.getLayoutManager().getChildAt(i); 1221 if (mGridView.getChildAdapterPosition(v) >= 51) { 1222 moveInViewHolders.add(v); 1223 } 1224 } 1225 } 1226 }); 1227 waitOneUiCycle(); 1228 assertTrue("prelayout should layout extra items to slide in", 1229 moveInViewHolders.size() > lastPos - 51); 1230 // run a scroll pass, the scroll pass should not remove the animating views even they are 1231 // outside visible areas. 1232 mActivityTestRule.runOnUiThread(new Runnable() { 1233 @Override 1234 public void run() { 1235 mGridView.scrollBy(-3, 0); 1236 } 1237 }); 1238 waitOneUiCycle(); 1239 for (int i = 0; i < moveInViewHolders.size(); i++) { 1240 assertSame(mGridView, moveInViewHolders.get(i).getParent()); 1241 } 1242 assertSame(mGridView, moveViewHolder.itemView.getParent()); 1243 assertFalse(moveViewHolder.isRecyclable()); 1244 waitForItemAnimation(); 1245 assertNull(moveViewHolder.itemView.getParent()); 1246 assertTrue(moveViewHolder.isRecyclable()); 1247 } 1248 1249 @Test 1250 public void testMoveItemToTheLeft() throws Throwable { 1251 Intent intent = new Intent(); 1252 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear); 1253 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 1254 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000); 1255 initActivity(intent); 1256 mOrientation = BaseGridView.HORIZONTAL; 1257 mNumRows = 1; 1258 1259 mActivityTestRule.runOnUiThread(new Runnable() { 1260 @Override 1261 public void run() { 1262 mGridView.getItemAnimator().setAddDuration(2000); 1263 mGridView.getItemAnimator().setMoveDuration(2000); 1264 mGridView.setSelectedPosition(1500); 1265 } 1266 }); 1267 waitForScrollIdle(); 1268 RecyclerView.ViewHolder moveViewHolder = mGridView.findViewHolderForAdapterPosition(1499); 1269 1270 int firstPos = mGridView.getChildAdapterPosition(mGridView.getChildAt(0)); 1271 mActivityTestRule.runOnUiThread(new Runnable() { 1272 @Override 1273 public void run() { 1274 mActivity.moveItem(1499, 1, true); 1275 } 1276 }); 1277 final ArrayList<View> moveInViewHolders = new ArrayList(); 1278 waitForItemAnimationStart(); 1279 mActivityTestRule.runOnUiThread(new Runnable() { 1280 @Override 1281 public void run() { 1282 for (int i = 0; i < mGridView.getLayoutManager().getChildCount(); i++) { 1283 View v = mGridView.getLayoutManager().getChildAt(i); 1284 if (mGridView.getChildAdapterPosition(v) <= 1499) { 1285 moveInViewHolders.add(v); 1286 } 1287 } 1288 } 1289 }); 1290 waitOneUiCycle(); 1291 assertTrue("prelayout should layout extra items to slide in ", 1292 moveInViewHolders.size() > 1499 - firstPos); 1293 // run a scroll pass, the scroll pass should not remove the animating views even they are 1294 // outside visible areas. 1295 mActivityTestRule.runOnUiThread(new Runnable() { 1296 @Override 1297 public void run() { 1298 mGridView.scrollBy(3, 0); 1299 } 1300 }); 1301 waitOneUiCycle(); 1302 for (int i = 0; i < moveInViewHolders.size(); i++) { 1303 assertSame(mGridView, moveInViewHolders.get(i).getParent()); 1304 } 1305 assertSame(mGridView, moveViewHolder.itemView.getParent()); 1306 assertFalse(moveViewHolder.isRecyclable()); 1307 waitForItemAnimation(); 1308 assertNull(moveViewHolder.itemView.getParent()); 1309 assertTrue(moveViewHolder.isRecyclable()); 1310 } 1311 1312 @Test 1313 public void testContinuousSwapForward() throws Throwable { 1314 Intent intent = new Intent(); 1315 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1316 R.layout.horizontal_linear); 1317 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200); 1318 initActivity(intent); 1319 mOrientation = BaseGridView.HORIZONTAL; 1320 mNumRows = 1; 1321 1322 mActivityTestRule.runOnUiThread(new Runnable() { 1323 @Override 1324 public void run() { 1325 mGridView.setSelectedPositionSmooth(150); 1326 } 1327 }); 1328 waitForScrollIdle(mVerifyLayout); 1329 for (int i = 150; i < 199; i++) { 1330 final int swapIndex = i; 1331 mActivityTestRule.runOnUiThread(new Runnable() { 1332 @Override 1333 public void run() { 1334 mActivity.swap(swapIndex, swapIndex + 1); 1335 } 1336 }); 1337 Thread.sleep(10); 1338 } 1339 waitForItemAnimation(); 1340 assertEquals(199, mGridView.getSelectedPosition()); 1341 // check if ItemAnimation finishes at aligned positions: 1342 int leftEdge = mGridView.getLayoutManager().findViewByPosition(199).getLeft(); 1343 mActivityTestRule.runOnUiThread(new Runnable() { 1344 @Override 1345 public void run() { 1346 mGridView.requestLayout(); 1347 } 1348 }); 1349 waitForScrollIdle(); 1350 assertEquals(leftEdge, mGridView.getLayoutManager().findViewByPosition(199).getLeft()); 1351 } 1352 1353 @Test 1354 public void testContinuousSwapBackward() throws Throwable { 1355 Intent intent = new Intent(); 1356 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1357 R.layout.horizontal_linear); 1358 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200); 1359 initActivity(intent); 1360 mOrientation = BaseGridView.HORIZONTAL; 1361 mNumRows = 1; 1362 1363 mActivityTestRule.runOnUiThread(new Runnable() { 1364 @Override 1365 public void run() { 1366 mGridView.setSelectedPositionSmooth(50); 1367 } 1368 }); 1369 waitForScrollIdle(mVerifyLayout); 1370 for (int i = 50; i > 0; i--) { 1371 final int swapIndex = i; 1372 mActivityTestRule.runOnUiThread(new Runnable() { 1373 @Override 1374 public void run() { 1375 mActivity.swap(swapIndex, swapIndex - 1); 1376 } 1377 }); 1378 Thread.sleep(10); 1379 } 1380 waitForItemAnimation(); 1381 assertEquals(0, mGridView.getSelectedPosition()); 1382 // check if ItemAnimation finishes at aligned positions: 1383 int leftEdge = mGridView.getLayoutManager().findViewByPosition(0).getLeft(); 1384 mActivityTestRule.runOnUiThread(new Runnable() { 1385 @Override 1386 public void run() { 1387 mGridView.requestLayout(); 1388 } 1389 }); 1390 waitForScrollIdle(); 1391 assertEquals(leftEdge, mGridView.getLayoutManager().findViewByPosition(0).getLeft()); 1392 } 1393 1394 void testSetSelectedPosition(final boolean inSmoothScroll, final boolean layoutRequested, 1395 final boolean viewVisible, final boolean smooth, 1396 final boolean resultLayoutRequested, final boolean resultSmoothScroller, 1397 final int resultScrollState) throws Throwable { 1398 Intent intent = new Intent(); 1399 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 1400 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1500); 1401 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 1402 mNumRows = 1; 1403 initActivity(intent); 1404 mOrientation = BaseGridView.VERTICAL; 1405 1406 if (inSmoothScroll) { 1407 setSelectedPositionSmooth(500); 1408 } 1409 mActivityTestRule.runOnUiThread(new Runnable() { 1410 @Override 1411 public void run() { 1412 if (layoutRequested) { 1413 mGridView.requestLayout(); 1414 } 1415 final int position; 1416 if (viewVisible) { 1417 position = mGridView.getChildAdapterPosition(mGridView.getChildAt( 1418 mGridView.getChildCount() - 1)); 1419 } else { 1420 position = 1000; 1421 } 1422 if (smooth) { 1423 mGridView.setSelectedPositionSmooth(position); 1424 } else { 1425 mGridView.setSelectedPosition(position); 1426 } 1427 assertEquals("isLayoutRequested", resultLayoutRequested, 1428 mGridView.isLayoutRequested()); 1429 assertEquals("isSmoothScrolling", resultSmoothScroller, 1430 mGridView.getLayoutManager().isSmoothScrolling()); 1431 if (!resultSmoothScroller) { 1432 // getScrollState() only matters when is not running smoothScroller 1433 assertEquals("getScrollState", resultScrollState, 1434 mGridView.getScrollState()); 1435 } 1436 assertEquals("isLayoutRequested", resultLayoutRequested, 1437 mGridView.isLayoutRequested()); 1438 } 1439 }); 1440 } 1441 1442 @Test 1443 public void testSelectedPosition01() throws Throwable { 1444 testSetSelectedPosition(false, false, false, false, 1445 true, false, RecyclerView.SCROLL_STATE_IDLE); 1446 } 1447 1448 @Test 1449 public void testSelectedPosition02() throws Throwable { 1450 testSetSelectedPosition(false, false, false, true, 1451 false, true, RecyclerView.SCROLL_STATE_IDLE); 1452 } 1453 1454 @Test 1455 public void testSelectedPosition03() throws Throwable { 1456 testSetSelectedPosition(false, false, true, false, 1457 false, false, RecyclerView.SCROLL_STATE_IDLE); 1458 } 1459 1460 @Test 1461 public void testSelectedPosition04() throws Throwable { 1462 testSetSelectedPosition(false, false, true, true, 1463 false, false, RecyclerView.SCROLL_STATE_SETTLING); 1464 } 1465 1466 @Test 1467 public void testSelectedPosition05() throws Throwable { 1468 testSetSelectedPosition(false, true, false, false, 1469 true, false, RecyclerView.SCROLL_STATE_IDLE); 1470 } 1471 1472 @Test 1473 public void testSelectedPosition06() throws Throwable { 1474 testSetSelectedPosition(false, true, false, true, 1475 true, false, RecyclerView.SCROLL_STATE_IDLE); 1476 } 1477 1478 @Test 1479 public void testSelectedPosition07() throws Throwable { 1480 testSetSelectedPosition(false, true, true, false, 1481 true, false, RecyclerView.SCROLL_STATE_IDLE); 1482 } 1483 1484 @Test 1485 public void testSelectedPosition08() throws Throwable { 1486 testSetSelectedPosition(false, true, true, true, 1487 true, false, RecyclerView.SCROLL_STATE_IDLE); 1488 } 1489 1490 @Test 1491 public void testSelectedPosition09() throws Throwable { 1492 testSetSelectedPosition(true, false, false, false, 1493 true, false, RecyclerView.SCROLL_STATE_IDLE); 1494 } 1495 1496 @Test 1497 public void testSelectedPosition10() throws Throwable { 1498 testSetSelectedPosition(true, false, false, true, 1499 false, true, RecyclerView.SCROLL_STATE_IDLE); 1500 } 1501 1502 @Test 1503 public void testSelectedPosition11() throws Throwable { 1504 testSetSelectedPosition(true, false, true, false, 1505 false, false, RecyclerView.SCROLL_STATE_IDLE); 1506 } 1507 1508 @Test 1509 public void testSelectedPosition12() throws Throwable { 1510 testSetSelectedPosition(true, false, true, true, 1511 false, true, RecyclerView.SCROLL_STATE_IDLE); 1512 } 1513 1514 @Test 1515 public void testSelectedPosition13() throws Throwable { 1516 testSetSelectedPosition(true, true, false, false, 1517 true, false, RecyclerView.SCROLL_STATE_IDLE); 1518 } 1519 1520 @Test 1521 public void testSelectedPosition14() throws Throwable { 1522 testSetSelectedPosition(true, true, false, true, 1523 true, false, RecyclerView.SCROLL_STATE_IDLE); 1524 } 1525 1526 @Test 1527 public void testSelectedPosition15() throws Throwable { 1528 testSetSelectedPosition(true, true, true, false, 1529 true, false, RecyclerView.SCROLL_STATE_IDLE); 1530 } 1531 1532 @Test 1533 public void testSelectedPosition16() throws Throwable { 1534 testSetSelectedPosition(true, true, true, true, 1535 true, false, RecyclerView.SCROLL_STATE_IDLE); 1536 } 1537 1538 @Test 1539 public void testScrollAndStuck() throws Throwable { 1540 // see b/67370222 fastRelayout() may be stuck. 1541 final int numItems = 19; 1542 final int[] itemsLength = new int[numItems]; 1543 for (int i = 0; i < numItems; i++) { 1544 itemsLength[i] = 288; 1545 } 1546 Intent intent = new Intent(); 1547 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1548 R.layout.horizontal_linear); 1549 intent.putExtra(GridActivity.EXTRA_ITEMS, itemsLength); 1550 initActivity(intent); 1551 mOrientation = BaseGridView.HORIZONTAL; 1552 mNumRows = 1; 1553 1554 // set left right padding to 112, space between items to be 16. 1555 mActivityTestRule.runOnUiThread(new Runnable() { 1556 @Override 1557 public void run() { 1558 ViewGroup.LayoutParams lp = mGridView.getLayoutParams(); 1559 lp.width = 1920; 1560 mGridView.setLayoutParams(lp); 1561 mGridView.setPadding(112, mGridView.getPaddingTop(), 112, 1562 mGridView.getPaddingBottom()); 1563 mGridView.setItemSpacing(16); 1564 } 1565 }); 1566 waitOneUiCycle(); 1567 1568 int scrollPos = 0; 1569 while (true) { 1570 final View view = mGridView.getChildAt(mGridView.getChildCount() - 1); 1571 final int pos = mGridView.getChildViewHolder(view).getAdapterPosition(); 1572 if (scrollPos != pos) { 1573 scrollPos = pos; 1574 mActivityTestRule.runOnUiThread(new Runnable() { 1575 @Override 1576 public void run() { 1577 mGridView.smoothScrollToPosition(pos); 1578 } 1579 }); 1580 } 1581 // wait until we see 2nd from last: 1582 if (pos >= 17) { 1583 if (pos == 17) { 1584 // great we can test fastRelayout() bug. 1585 Thread.sleep(50); 1586 mActivityTestRule.runOnUiThread(new Runnable() { 1587 @Override 1588 public void run() { 1589 view.requestLayout(); 1590 } 1591 }); 1592 } 1593 break; 1594 } 1595 Thread.sleep(16); 1596 } 1597 waitForScrollIdle(); 1598 } 1599 1600 @Test 1601 public void testSwapAfterScroll() throws Throwable { 1602 Intent intent = new Intent(); 1603 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1604 R.layout.horizontal_linear); 1605 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200); 1606 initActivity(intent); 1607 mOrientation = BaseGridView.HORIZONTAL; 1608 mNumRows = 1; 1609 1610 mActivityTestRule.runOnUiThread(new Runnable() { 1611 @Override 1612 public void run() { 1613 mGridView.getItemAnimator().setMoveDuration(1000); 1614 mGridView.setSelectedPositionSmooth(150); 1615 } 1616 }); 1617 waitForScrollIdle(); 1618 mActivityTestRule.runOnUiThread(new Runnable() { 1619 @Override 1620 public void run() { 1621 mGridView.setSelectedPositionSmooth(151); 1622 } 1623 }); 1624 mActivityTestRule.runOnUiThread(new Runnable() { 1625 @Override 1626 public void run() { 1627 // we want to swap and select new target which is at 150 before swap 1628 mGridView.setSelectedPositionSmooth(150); 1629 mActivity.swap(150, 151); 1630 } 1631 }); 1632 waitForItemAnimation(); 1633 waitForScrollIdle(); 1634 assertEquals(151, mGridView.getSelectedPosition()); 1635 // check if ItemAnimation finishes at aligned positions: 1636 int leftEdge = mGridView.getLayoutManager().findViewByPosition(151).getLeft(); 1637 mActivityTestRule.runOnUiThread(new Runnable() { 1638 @Override 1639 public void run() { 1640 mGridView.requestLayout(); 1641 } 1642 }); 1643 waitForScrollIdle(); 1644 assertEquals(leftEdge, mGridView.getLayoutManager().findViewByPosition(151).getLeft()); 1645 } 1646 1647 void testScrollInSmoothScrolling(final boolean smooth, final boolean scrollToInvisible, 1648 final boolean useRecyclerViewMethod) throws Throwable { 1649 final int numItems = 100; 1650 final int[] itemsLength = new int[numItems]; 1651 for (int i = 0; i < numItems; i++) { 1652 itemsLength[i] = 288; 1653 } 1654 Intent intent = new Intent(); 1655 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1656 R.layout.horizontal_linear); 1657 intent.putExtra(GridActivity.EXTRA_ITEMS, itemsLength); 1658 initActivity(intent); 1659 mOrientation = BaseGridView.HORIZONTAL; 1660 mNumRows = 1; 1661 1662 // start a smoothScroller 1663 final int selectedPosition = 99; 1664 mActivityTestRule.runOnUiThread(new Runnable() { 1665 @Override 1666 public void run() { 1667 mGridView.smoothScrollToPosition(selectedPosition); 1668 } 1669 }); 1670 Thread.sleep(50); 1671 // while smoothScroller is still running, scroll to a different position 1672 final int[] existing_position = new int[1]; 1673 mActivityTestRule.runOnUiThread(new Runnable() { 1674 @Override 1675 public void run() { 1676 existing_position[0] = mGridView.getChildAdapterPosition( 1677 mGridView.getChildAt(mGridView.getChildCount() - 1)); 1678 if (scrollToInvisible) { 1679 existing_position[0] = existing_position[0] + 3; 1680 } 1681 if (useRecyclerViewMethod) { 1682 if (smooth) { 1683 mGridView.smoothScrollToPosition(existing_position[0]); 1684 } else { 1685 mGridView.scrollToPosition(existing_position[0]); 1686 } 1687 } else { 1688 if (smooth) { 1689 mGridView.setSelectedPositionSmooth(existing_position[0]); 1690 } else { 1691 mGridView.setSelectedPosition(existing_position[0]); 1692 } 1693 } 1694 } 1695 }); 1696 waitForScrollIdle(); 1697 assertEquals(existing_position[0], mGridView.getSelectedPosition()); 1698 assertTrue(mGridView.findViewHolderForAdapterPosition(existing_position[0]) 1699 .itemView.hasFocus()); 1700 } 1701 1702 @Test 1703 public void testScrollInSmoothScrolling1() throws Throwable { 1704 testScrollInSmoothScrolling(false, false, false); 1705 } 1706 1707 @Test 1708 public void testScrollInSmoothScrolling2() throws Throwable { 1709 testScrollInSmoothScrolling(false, false, true); 1710 } 1711 1712 @Test 1713 public void testScrollInSmoothScrolling3() throws Throwable { 1714 testScrollInSmoothScrolling(false, true, false); 1715 } 1716 1717 @Test 1718 public void testScrollInSmoothScrolling4() throws Throwable { 1719 testScrollInSmoothScrolling(false, true, true); 1720 } 1721 1722 @Test 1723 public void testScrollInSmoothScrolling5() throws Throwable { 1724 testScrollInSmoothScrolling(true, false, false); 1725 } 1726 1727 @Test 1728 public void testScrollInSmoothScrolling6() throws Throwable { 1729 testScrollInSmoothScrolling(true, false, true); 1730 } 1731 1732 @Test 1733 public void testScrollInSmoothScrolling7() throws Throwable { 1734 testScrollInSmoothScrolling(true, true, false); 1735 } 1736 1737 @Test 1738 public void testScrollInSmoothScrolling8() throws Throwable { 1739 testScrollInSmoothScrolling(true, true, true); 1740 } 1741 1742 @Test 1743 public void testScrollAfterRequestLayout() throws Throwable { 1744 Intent intent = new Intent(); 1745 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1746 R.layout.horizontal_linear); 1747 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 10); 1748 initActivity(intent); 1749 mOrientation = BaseGridView.HORIZONTAL; 1750 mNumRows = 1; 1751 mActivityTestRule.runOnUiThread(new Runnable() { 1752 @Override 1753 public void run() { 1754 mGridView.setHasFixedSize(false); 1755 mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE); 1756 mGridView.setWindowAlignmentOffsetPercent(30); 1757 } 1758 }); 1759 waitOneUiCycle(); 1760 1761 final boolean[] scrolled = new boolean[1]; 1762 mGridView.addOnScrollListener(new RecyclerView.OnScrollListener() { 1763 @Override 1764 public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 1765 if (dx != 0) scrolled[0] = true; 1766 } 1767 }); 1768 mActivityTestRule.runOnUiThread(new Runnable() { 1769 @Override 1770 public void run() { 1771 mGridView.requestLayout(); 1772 mGridView.setSelectedPosition(1); 1773 } 1774 }); 1775 waitOneUiCycle(); 1776 assertFalse(scrolled[0]); 1777 } 1778 1779 @Test 1780 public void testScrollAfterItemAnimator() throws Throwable { 1781 Intent intent = new Intent(); 1782 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1783 R.layout.horizontal_linear); 1784 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 10); 1785 initActivity(intent); 1786 mOrientation = BaseGridView.HORIZONTAL; 1787 mNumRows = 1; 1788 mActivityTestRule.runOnUiThread(new Runnable() { 1789 @Override 1790 public void run() { 1791 mGridView.setHasFixedSize(false); 1792 mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE); 1793 mGridView.setWindowAlignmentOffsetPercent(30); 1794 } 1795 }); 1796 waitOneUiCycle(); 1797 1798 final boolean[] scrolled = new boolean[1]; 1799 mGridView.addOnScrollListener(new RecyclerView.OnScrollListener() { 1800 @Override 1801 public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 1802 if (dx != 0) scrolled[0] = true; 1803 } 1804 }); 1805 mActivityTestRule.runOnUiThread(new Runnable() { 1806 @Override 1807 public void run() { 1808 mActivity.changeItem(0, 10); 1809 mGridView.setSelectedPosition(1); 1810 } 1811 }); 1812 waitOneUiCycle(); 1813 assertFalse(scrolled[0]); 1814 } 1815 1816 @Test 1817 public void testItemMovedHorizontal() throws Throwable { 1818 Intent intent = new Intent(); 1819 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1820 R.layout.horizontal_grid); 1821 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200); 1822 initActivity(intent); 1823 mOrientation = BaseGridView.HORIZONTAL; 1824 mNumRows = 3; 1825 1826 mActivityTestRule.runOnUiThread(new Runnable() { 1827 @Override 1828 public void run() { 1829 mGridView.setSelectedPositionSmooth(150); 1830 } 1831 }); 1832 waitForScrollIdle(mVerifyLayout); 1833 performAndWaitForAnimation(new Runnable() { 1834 @Override 1835 public void run() { 1836 mActivity.swap(150, 152); 1837 } 1838 }); 1839 mActivityTestRule.runOnUiThread(mVerifyLayout); 1840 1841 scrollToBegin(mVerifyLayout); 1842 1843 verifyBeginAligned(); 1844 } 1845 1846 @Test 1847 public void testItemMovedHorizontalRtl() throws Throwable { 1848 Intent intent = new Intent(); 1849 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1850 R.layout.horizontal_linear_rtl); 1851 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 1852 intent.putExtra(GridActivity.EXTRA_ITEMS, new int[] {40, 40, 40}); 1853 initActivity(intent); 1854 mOrientation = BaseGridView.HORIZONTAL; 1855 mNumRows = 1; 1856 1857 performAndWaitForAnimation(new Runnable() { 1858 @Override 1859 public void run() { 1860 mActivity.moveItem(0, 1, true); 1861 } 1862 }); 1863 assertEquals(mGridView.getWidth() - mGridView.getPaddingRight(), 1864 mGridView.findViewHolderForAdapterPosition(0).itemView.getRight()); 1865 } 1866 1867 @Test 1868 public void testScrollSecondaryCannotScroll() throws Throwable { 1869 Intent intent = new Intent(); 1870 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1871 R.layout.horizontal_grid); 1872 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 1873 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000); 1874 initActivity(intent); 1875 mOrientation = BaseGridView.HORIZONTAL; 1876 mNumRows = 3; 1877 final int topPadding = 2; 1878 final int bottomPadding = 2; 1879 final int height = mGridView.getHeight(); 1880 final int spacing = 2; 1881 final int rowHeight = (height - topPadding - bottomPadding) / 4 - spacing; 1882 final HorizontalGridView horizontalGridView = (HorizontalGridView) mGridView; 1883 1884 startWaitLayout(); 1885 mActivityTestRule.runOnUiThread(new Runnable() { 1886 @Override 1887 public void run() { 1888 horizontalGridView.setPadding(0, topPadding, 0, bottomPadding); 1889 horizontalGridView.setItemSpacing(spacing); 1890 horizontalGridView.setNumRows(mNumRows); 1891 horizontalGridView.setRowHeight(rowHeight); 1892 } 1893 }); 1894 waitForLayout(); 1895 // navigate vertically in first column, first row should always be aligned to top padding 1896 for (int i = 0; i < 3; i++) { 1897 setSelectedPosition(i); 1898 assertEquals(topPadding, mGridView.findViewHolderForAdapterPosition(0).itemView 1899 .getTop()); 1900 } 1901 // navigate vertically in 100th column, first row should always be aligned to top padding 1902 for (int i = 300; i < 301; i++) { 1903 setSelectedPosition(i); 1904 assertEquals(topPadding, mGridView.findViewHolderForAdapterPosition(300).itemView 1905 .getTop()); 1906 } 1907 } 1908 1909 @Test 1910 public void testScrollSecondaryNeedScroll() throws Throwable { 1911 Intent intent = new Intent(); 1912 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1913 R.layout.horizontal_grid); 1914 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 1915 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000); 1916 initActivity(intent); 1917 mOrientation = BaseGridView.HORIZONTAL; 1918 // test a lot of rows so we have to scroll vertically to reach 1919 mNumRows = 9; 1920 final int topPadding = 2; 1921 final int bottomPadding = 2; 1922 final int height = mGridView.getHeight(); 1923 final int spacing = 2; 1924 final int rowHeight = (height - topPadding - bottomPadding) / 4 - spacing; 1925 final HorizontalGridView horizontalGridView = (HorizontalGridView) mGridView; 1926 1927 startWaitLayout(); 1928 mActivityTestRule.runOnUiThread(new Runnable() { 1929 @Override 1930 public void run() { 1931 horizontalGridView.setPadding(0, topPadding, 0, bottomPadding); 1932 horizontalGridView.setItemSpacing(spacing); 1933 horizontalGridView.setNumRows(mNumRows); 1934 horizontalGridView.setRowHeight(rowHeight); 1935 } 1936 }); 1937 waitForLayout(); 1938 View view; 1939 // first row should be aligned to top padding 1940 setSelectedPosition(0); 1941 assertEquals(topPadding, mGridView.findViewHolderForAdapterPosition(0).itemView.getTop()); 1942 // middle row should be aligned to keyline (1/2 of screen height) 1943 setSelectedPosition(4); 1944 view = mGridView.findViewHolderForAdapterPosition(4).itemView; 1945 assertEquals(height / 2, (view.getTop() + view.getBottom()) / 2); 1946 // last row should be aligned to bottom padding. 1947 setSelectedPosition(8); 1948 view = mGridView.findViewHolderForAdapterPosition(8).itemView; 1949 assertEquals(height, view.getTop() + rowHeight + bottomPadding); 1950 setSelectedPositionSmooth(4); 1951 waitForScrollIdle(); 1952 // middle row should be aligned to keyline (1/2 of screen height) 1953 setSelectedPosition(4); 1954 view = mGridView.findViewHolderForAdapterPosition(4).itemView; 1955 assertEquals(height / 2, (view.getTop() + view.getBottom()) / 2); 1956 // first row should be aligned to top padding 1957 setSelectedPositionSmooth(0); 1958 waitForScrollIdle(); 1959 assertEquals(topPadding, mGridView.findViewHolderForAdapterPosition(0).itemView.getTop()); 1960 } 1961 1962 @Test 1963 public void testItemMovedVertical() throws Throwable { 1964 1965 Intent intent = new Intent(); 1966 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1967 R.layout.vertical_grid); 1968 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200); 1969 initActivity(intent); 1970 mOrientation = BaseGridView.VERTICAL; 1971 mNumRows = 3; 1972 1973 mGridView.setSelectedPositionSmooth(150); 1974 waitForScrollIdle(mVerifyLayout); 1975 performAndWaitForAnimation(new Runnable() { 1976 @Override 1977 public void run() { 1978 mActivity.swap(150, 152); 1979 } 1980 }); 1981 mActivityTestRule.runOnUiThread(mVerifyLayout); 1982 1983 scrollToEnd(mVerifyLayout); 1984 scrollToBegin(mVerifyLayout); 1985 1986 verifyBeginAligned(); 1987 } 1988 1989 @Test 1990 public void testAddLastItemHorizontal() throws Throwable { 1991 1992 Intent intent = new Intent(); 1993 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 1994 R.layout.horizontal_linear); 1995 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50); 1996 initActivity(intent); 1997 mOrientation = BaseGridView.HORIZONTAL; 1998 mNumRows = 1; 1999 2000 mActivityTestRule.runOnUiThread( 2001 new Runnable() { 2002 @Override 2003 public void run() { 2004 mGridView.setSelectedPositionSmooth(49); 2005 } 2006 } 2007 ); 2008 waitForScrollIdle(mVerifyLayout); 2009 performAndWaitForAnimation(new Runnable() { 2010 @Override 2011 public void run() { 2012 mActivity.addItems(50, new int[]{150}); 2013 } 2014 }); 2015 2016 // assert new added item aligned to right edge 2017 assertEquals(mGridView.getWidth() - mGridView.getPaddingRight(), 2018 mGridView.getLayoutManager().findViewByPosition(50).getRight()); 2019 } 2020 2021 @Test 2022 public void testAddMultipleLastItemsHorizontal() throws Throwable { 2023 2024 Intent intent = new Intent(); 2025 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2026 R.layout.horizontal_linear); 2027 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50); 2028 initActivity(intent); 2029 mOrientation = BaseGridView.HORIZONTAL; 2030 mNumRows = 1; 2031 2032 mActivityTestRule.runOnUiThread( 2033 new Runnable() { 2034 @Override 2035 public void run() { 2036 mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_BOTH_EDGE); 2037 mGridView.setWindowAlignmentOffsetPercent(50); 2038 mGridView.setSelectedPositionSmooth(49); 2039 } 2040 } 2041 ); 2042 waitForScrollIdle(mVerifyLayout); 2043 performAndWaitForAnimation(new Runnable() { 2044 @Override 2045 public void run() { 2046 mActivity.addItems(50, new int[]{150, 150, 150, 150, 150, 150, 150, 150, 150, 2047 150, 150, 150, 150, 150}); 2048 } 2049 }); 2050 2051 // The focused item will be at center of window 2052 View view = mGridView.getLayoutManager().findViewByPosition(49); 2053 assertEquals(mGridView.getWidth() / 2, (view.getLeft() + view.getRight()) / 2); 2054 } 2055 2056 @Test 2057 public void testItemAddRemoveHorizontal() throws Throwable { 2058 2059 Intent intent = new Intent(); 2060 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2061 R.layout.horizontal_grid); 2062 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200); 2063 initActivity(intent); 2064 mOrientation = BaseGridView.HORIZONTAL; 2065 mNumRows = 3; 2066 2067 scrollToEnd(mVerifyLayout); 2068 int[] endEdges = getEndEdges(); 2069 2070 mGridView.setSelectedPositionSmooth(150); 2071 waitForScrollIdle(mVerifyLayout); 2072 performAndWaitForAnimation(new Runnable() { 2073 @Override 2074 public void run() { 2075 mRemovedItems = mActivity.removeItems(151, 4); 2076 } 2077 }); 2078 2079 scrollToEnd(mVerifyLayout); 2080 mGridView.setSelectedPositionSmooth(150); 2081 waitForScrollIdle(mVerifyLayout); 2082 2083 performAndWaitForAnimation(new Runnable() { 2084 @Override 2085 public void run() { 2086 mActivity.addItems(151, mRemovedItems); 2087 } 2088 }); 2089 scrollToEnd(mVerifyLayout); 2090 2091 // we should get same aligned end edges 2092 int[] endEdges2 = getEndEdges(); 2093 verifyEdgesSame(endEdges, endEdges2); 2094 2095 scrollToBegin(mVerifyLayout); 2096 verifyBeginAligned(); 2097 } 2098 2099 @Test 2100 public void testSetSelectedPositionDetached() throws Throwable { 2101 2102 Intent intent = new Intent(); 2103 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2104 R.layout.horizontal_linear); 2105 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50); 2106 initActivity(intent); 2107 mOrientation = BaseGridView.HORIZONTAL; 2108 mNumRows = 1; 2109 2110 final int focusToIndex = 49; 2111 final ViewGroup parent = (ViewGroup) mGridView.getParent(); 2112 mActivityTestRule.runOnUiThread(new Runnable() { 2113 @Override 2114 public void run() { 2115 parent.removeView(mGridView); 2116 } 2117 }); 2118 mActivityTestRule.runOnUiThread(new Runnable() { 2119 @Override 2120 public void run() { 2121 mGridView.setSelectedPositionSmooth(focusToIndex); 2122 } 2123 }); 2124 mActivityTestRule.runOnUiThread(new Runnable() { 2125 @Override 2126 public void run() { 2127 parent.addView(mGridView); 2128 mGridView.requestFocus(); 2129 } 2130 }); 2131 waitForScrollIdle(); 2132 assertEquals(mGridView.getSelectedPosition(), focusToIndex); 2133 assertTrue(mGridView.getLayoutManager().findViewByPosition(focusToIndex).hasFocus()); 2134 2135 final int focusToIndex2 = 0; 2136 mActivityTestRule.runOnUiThread(new Runnable() { 2137 @Override 2138 public void run() { 2139 parent.removeView(mGridView); 2140 } 2141 }); 2142 mActivityTestRule.runOnUiThread(new Runnable() { 2143 @Override 2144 public void run() { 2145 mGridView.setSelectedPosition(focusToIndex2); 2146 } 2147 }); 2148 mActivityTestRule.runOnUiThread(new Runnable() { 2149 @Override 2150 public void run() { 2151 parent.addView(mGridView); 2152 mGridView.requestFocus(); 2153 } 2154 }); 2155 assertEquals(mGridView.getSelectedPosition(), focusToIndex2); 2156 waitForScrollIdle(); 2157 assertTrue(mGridView.getLayoutManager().findViewByPosition(focusToIndex2).hasFocus()); 2158 } 2159 2160 @Test 2161 public void testBug22209986() throws Throwable { 2162 2163 Intent intent = new Intent(); 2164 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2165 R.layout.horizontal_linear); 2166 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50); 2167 initActivity(intent); 2168 mOrientation = BaseGridView.HORIZONTAL; 2169 mNumRows = 1; 2170 2171 final int focusToIndex = mGridView.getChildCount() - 1; 2172 mActivityTestRule.runOnUiThread(new Runnable() { 2173 @Override 2174 public void run() { 2175 mGridView.setSelectedPositionSmooth(focusToIndex); 2176 } 2177 }); 2178 2179 waitForScrollIdle(); 2180 mActivityTestRule.runOnUiThread(new Runnable() { 2181 @Override 2182 public void run() { 2183 mGridView.setSelectedPositionSmooth(focusToIndex + 1); 2184 } 2185 }); 2186 // let the scroll running for a while and requestLayout during scroll 2187 Thread.sleep(80); 2188 mActivityTestRule.runOnUiThread(new Runnable() { 2189 @Override 2190 public void run() { 2191 assertEquals(mGridView.getScrollState(), BaseGridView.SCROLL_STATE_SETTLING); 2192 mGridView.requestLayout(); 2193 } 2194 }); 2195 waitForScrollIdle(); 2196 2197 int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft(); 2198 2199 mActivityTestRule.runOnUiThread(new Runnable() { 2200 @Override 2201 public void run() { 2202 mGridView.requestLayout(); 2203 } 2204 }); 2205 waitForScrollIdle(); 2206 assertEquals(leftEdge, 2207 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft()); 2208 } 2209 2210 void testScrollAndRemove(int[] itemsLength, int numItems) throws Throwable { 2211 2212 Intent intent = new Intent(); 2213 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2214 R.layout.horizontal_linear); 2215 if (itemsLength != null) { 2216 intent.putExtra(GridActivity.EXTRA_ITEMS, itemsLength); 2217 } else { 2218 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2219 } 2220 initActivity(intent); 2221 mOrientation = BaseGridView.HORIZONTAL; 2222 mNumRows = 1; 2223 2224 final int focusToIndex = mGridView.getChildCount() - 1; 2225 mActivityTestRule.runOnUiThread(new Runnable() { 2226 @Override 2227 public void run() { 2228 mGridView.setSelectedPositionSmooth(focusToIndex); 2229 } 2230 }); 2231 2232 performAndWaitForAnimation(new Runnable() { 2233 @Override 2234 public void run() { 2235 mActivity.removeItems(focusToIndex, 1); 2236 } 2237 }); 2238 2239 waitOneUiCycle(); 2240 waitForScrollIdle(); 2241 int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft(); 2242 2243 mActivityTestRule.runOnUiThread(new Runnable() { 2244 @Override 2245 public void run() { 2246 mGridView.requestLayout(); 2247 } 2248 }); 2249 waitForScrollIdle(); 2250 assertEquals(leftEdge, 2251 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft(), DELTA); 2252 } 2253 2254 @Test 2255 public void testScrollAndRemove() throws Throwable { 2256 // test random lengths for 50 items 2257 testScrollAndRemove(null, 50); 2258 } 2259 2260 /** 2261 * This test verifies if scroll limits are ignored when onLayoutChildren compensate remaining 2262 * scroll distance. b/64931938 2263 * In the test, second child is long, other children are short. 2264 * Test scrolls to the long child, and when scrolling, remove the long child. We made it long 2265 * to have enough remaining scroll distance when the layout pass kicks in. 2266 * The onLayoutChildren() would compensate the remaining scroll distance, moving all items 2267 * toward right, which will make the first item's left edge bigger than left padding, 2268 * which would violate the "scroll limit of left" in a regular scroll case, but 2269 * in layout pass, we still honor that scroll request, ignoring the scroll limit. 2270 */ 2271 @Test 2272 public void testScrollAndRemoveSample1() throws Throwable { 2273 DisplayMetrics dm = InstrumentationRegistry.getInstrumentation().getTargetContext() 2274 .getResources().getDisplayMetrics(); 2275 // screen width for long item and 4DP for other items 2276 int longItemLength = dm.widthPixels; 2277 int shortItemLength = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, dm); 2278 int[] items = new int[1000]; 2279 for (int i = 0; i < items.length; i++) { 2280 items[i] = shortItemLength; 2281 } 2282 items[1] = longItemLength; 2283 testScrollAndRemove(items, 0); 2284 } 2285 2286 @Test 2287 public void testScrollAndInsert() throws Throwable { 2288 2289 Intent intent = new Intent(); 2290 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2291 R.layout.vertical_grid); 2292 int[] items = new int[1000]; 2293 for (int i = 0; i < items.length; i++) { 2294 items[i] = 300 + (int)(Math.random() * 100); 2295 } 2296 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 2297 intent.putExtra(GridActivity.EXTRA_STAGGERED, true); 2298 mOrientation = BaseGridView.VERTICAL; 2299 mNumRows = 3; 2300 2301 initActivity(intent); 2302 2303 mActivityTestRule.runOnUiThread(new Runnable() { 2304 @Override 2305 public void run() { 2306 mGridView.setSelectedPositionSmooth(150); 2307 } 2308 }); 2309 waitForScrollIdle(mVerifyLayout); 2310 2311 View view = mGridView.getChildAt(mGridView.getChildCount() - 1); 2312 final int focusToIndex = mGridView.getChildAdapterPosition(view); 2313 mActivityTestRule.runOnUiThread(new Runnable() { 2314 @Override 2315 public void run() { 2316 mGridView.setSelectedPositionSmooth(focusToIndex); 2317 } 2318 }); 2319 2320 mActivityTestRule.runOnUiThread(new Runnable() { 2321 @Override 2322 public void run() { 2323 int[] newItems = new int[]{300, 300, 300}; 2324 mActivity.addItems(0, newItems); 2325 } 2326 }); 2327 waitForScrollIdle(); 2328 int topEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getTop(); 2329 mActivityTestRule.runOnUiThread(new Runnable() { 2330 @Override 2331 public void run() { 2332 mGridView.requestLayout(); 2333 } 2334 }); 2335 waitForScrollIdle(); 2336 assertEquals(topEdge, 2337 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getTop()); 2338 } 2339 2340 @Test 2341 public void testScrollAndInsertBeforeVisibleItem() throws Throwable { 2342 2343 Intent intent = new Intent(); 2344 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2345 R.layout.vertical_grid); 2346 int[] items = new int[1000]; 2347 for (int i = 0; i < items.length; i++) { 2348 items[i] = 300 + (int)(Math.random() * 100); 2349 } 2350 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 2351 intent.putExtra(GridActivity.EXTRA_STAGGERED, true); 2352 mOrientation = BaseGridView.VERTICAL; 2353 mNumRows = 3; 2354 2355 initActivity(intent); 2356 2357 mActivityTestRule.runOnUiThread(new Runnable() { 2358 @Override 2359 public void run() { 2360 mGridView.setSelectedPositionSmooth(150); 2361 } 2362 }); 2363 waitForScrollIdle(mVerifyLayout); 2364 2365 View view = mGridView.getChildAt(mGridView.getChildCount() - 1); 2366 final int focusToIndex = mGridView.getChildAdapterPosition(view); 2367 mActivityTestRule.runOnUiThread(new Runnable() { 2368 @Override 2369 public void run() { 2370 mGridView.setSelectedPositionSmooth(focusToIndex); 2371 } 2372 }); 2373 2374 performAndWaitForAnimation(new Runnable() { 2375 @Override 2376 public void run() { 2377 int[] newItems = new int[]{300, 300, 300}; 2378 mActivity.addItems(focusToIndex, newItems); 2379 } 2380 }); 2381 } 2382 2383 @Test 2384 public void testSmoothScrollAndRemove() throws Throwable { 2385 2386 Intent intent = new Intent(); 2387 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2388 R.layout.horizontal_linear); 2389 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 300); 2390 initActivity(intent); 2391 mOrientation = BaseGridView.HORIZONTAL; 2392 mNumRows = 1; 2393 2394 final int focusToIndex = 200; 2395 mActivityTestRule.runOnUiThread(new Runnable() { 2396 @Override 2397 public void run() { 2398 mGridView.setSelectedPositionSmooth(focusToIndex); 2399 } 2400 }); 2401 2402 mActivityTestRule.runOnUiThread(new Runnable() { 2403 @Override 2404 public void run() { 2405 mActivity.removeItems(focusToIndex, 1); 2406 } 2407 }); 2408 2409 assertTrue("removing the index of not attached child should not affect smooth scroller", 2410 mGridView.getLayoutManager().isSmoothScrolling()); 2411 waitForScrollIdle(); 2412 int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft(); 2413 2414 mActivityTestRule.runOnUiThread(new Runnable() { 2415 @Override 2416 public void run() { 2417 mGridView.requestLayout(); 2418 } 2419 }); 2420 waitForScrollIdle(); 2421 assertEquals(leftEdge, 2422 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft()); 2423 } 2424 2425 @Test 2426 public void testSmoothScrollAndRemove2() throws Throwable { 2427 2428 Intent intent = new Intent(); 2429 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2430 R.layout.horizontal_linear); 2431 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 300); 2432 initActivity(intent); 2433 mOrientation = BaseGridView.HORIZONTAL; 2434 mNumRows = 1; 2435 2436 final int focusToIndex = 200; 2437 mActivityTestRule.runOnUiThread(new Runnable() { 2438 @Override 2439 public void run() { 2440 mGridView.setSelectedPositionSmooth(focusToIndex); 2441 } 2442 }); 2443 2444 startWaitLayout(); 2445 mActivityTestRule.runOnUiThread(new Runnable() { 2446 @Override 2447 public void run() { 2448 final int removeIndex = mGridView.getChildViewHolder( 2449 mGridView.getChildAt(mGridView.getChildCount() - 1)).getAdapterPosition(); 2450 mActivity.removeItems(removeIndex, 1); 2451 } 2452 }); 2453 waitForLayout(); 2454 2455 assertTrue("removing the index of attached child should not kill smooth scroller", 2456 mGridView.getLayoutManager().isSmoothScrolling()); 2457 waitForItemAnimation(); 2458 waitForScrollIdle(); 2459 int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft(); 2460 2461 mActivityTestRule.runOnUiThread(new Runnable() { 2462 @Override 2463 public void run() { 2464 mGridView.requestLayout(); 2465 } 2466 }); 2467 waitForScrollIdle(); 2468 assertEquals(leftEdge, 2469 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft()); 2470 } 2471 2472 @Test 2473 public void testPendingSmoothScrollAndRemove() throws Throwable { 2474 Intent intent = new Intent(); 2475 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2476 R.layout.vertical_linear); 2477 intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true); 2478 int[] items = new int[100]; 2479 for (int i = 0; i < items.length; i++) { 2480 items[i] = 630 + (int)(Math.random() * 100); 2481 } 2482 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 2483 intent.putExtra(GridActivity.EXTRA_STAGGERED, true); 2484 mOrientation = BaseGridView.VERTICAL; 2485 mNumRows = 1; 2486 2487 initActivity(intent); 2488 2489 mGridView.setSelectedPositionSmooth(0); 2490 waitForScrollIdle(mVerifyLayout); 2491 assertTrue(mGridView.getChildAt(0).hasFocus()); 2492 2493 // Pressing lots of key to make sure smooth scroller is running 2494 mGridView.mLayoutManager.mMaxPendingMoves = 100; 2495 for (int i = 0; i < 100; i++) { 2496 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 2497 } 2498 2499 assertTrue(mGridView.getLayoutManager().isSmoothScrolling()); 2500 startWaitLayout(); 2501 mActivityTestRule.runOnUiThread(new Runnable() { 2502 @Override 2503 public void run() { 2504 final int removeIndex = mGridView.getChildViewHolder( 2505 mGridView.getChildAt(mGridView.getChildCount() - 1)).getAdapterPosition(); 2506 mActivity.removeItems(removeIndex, 1); 2507 } 2508 }); 2509 waitForLayout(); 2510 2511 assertTrue("removing the index of attached child should not kill smooth scroller", 2512 mGridView.getLayoutManager().isSmoothScrolling()); 2513 2514 waitForItemAnimation(); 2515 waitForScrollIdle(); 2516 int focusIndex = mGridView.getSelectedPosition(); 2517 int topEdge = mGridView.getLayoutManager().findViewByPosition(focusIndex).getTop(); 2518 2519 mActivityTestRule.runOnUiThread(new Runnable() { 2520 @Override 2521 public void run() { 2522 mGridView.requestLayout(); 2523 } 2524 }); 2525 waitForScrollIdle(); 2526 assertEquals(topEdge, 2527 mGridView.getLayoutManager().findViewByPosition(focusIndex).getTop()); 2528 } 2529 2530 @Test 2531 public void testFocusToFirstItem() throws Throwable { 2532 2533 Intent intent = new Intent(); 2534 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2535 R.layout.horizontal_grid); 2536 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200); 2537 initActivity(intent); 2538 mOrientation = BaseGridView.HORIZONTAL; 2539 mNumRows = 3; 2540 2541 performAndWaitForAnimation(new Runnable() { 2542 @Override 2543 public void run() { 2544 mRemovedItems = mActivity.removeItems(0, 200); 2545 } 2546 }); 2547 2548 humanDelay(500); 2549 performAndWaitForAnimation(new Runnable() { 2550 @Override 2551 public void run() { 2552 mActivity.addItems(0, mRemovedItems); 2553 } 2554 }); 2555 2556 humanDelay(500); 2557 assertTrue(mGridView.getLayoutManager().findViewByPosition(0).hasFocus()); 2558 2559 changeArraySize(0); 2560 2561 changeArraySize(200); 2562 assertTrue(mGridView.getLayoutManager().findViewByPosition(0).hasFocus()); 2563 } 2564 2565 @Test 2566 public void testNonFocusableHorizontal() throws Throwable { 2567 final int numItems = 200; 2568 final int startPos = 45; 2569 final int skips = 20; 2570 final int numColumns = 3; 2571 final int endPos = startPos + numColumns * (skips + 1); 2572 2573 Intent intent = new Intent(); 2574 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2575 R.layout.horizontal_grid); 2576 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2577 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2578 mOrientation = BaseGridView.HORIZONTAL; 2579 mNumRows = numColumns; 2580 boolean[] focusable = new boolean[numItems]; 2581 for (int i = 0; i < focusable.length; i++) { 2582 focusable[i] = true; 2583 } 2584 for (int i = startPos + mNumRows, j = 0; j < skips; i += mNumRows, j++) { 2585 focusable[i] = false; 2586 } 2587 intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable); 2588 initActivity(intent); 2589 2590 mGridView.setSelectedPositionSmooth(startPos); 2591 waitForScrollIdle(mVerifyLayout); 2592 2593 if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) { 2594 sendKey(KeyEvent.KEYCODE_DPAD_LEFT); 2595 } else { 2596 sendKey(KeyEvent.KEYCODE_DPAD_RIGHT); 2597 } 2598 waitForScrollIdle(mVerifyLayout); 2599 assertEquals(endPos, mGridView.getSelectedPosition()); 2600 2601 if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) { 2602 sendKey(KeyEvent.KEYCODE_DPAD_RIGHT); 2603 } else { 2604 sendKey(KeyEvent.KEYCODE_DPAD_LEFT); 2605 } 2606 waitForScrollIdle(mVerifyLayout); 2607 assertEquals(startPos, mGridView.getSelectedPosition()); 2608 2609 } 2610 2611 @Test 2612 public void testNoInitialFocusable() throws Throwable { 2613 2614 Intent intent = new Intent(); 2615 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2616 R.layout.horizontal_linear); 2617 final int numItems = 100; 2618 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2619 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2620 mOrientation = BaseGridView.HORIZONTAL; 2621 mNumRows = 1; 2622 boolean[] focusable = new boolean[numItems]; 2623 final int firstFocusableIndex = 10; 2624 for (int i = 0; i < firstFocusableIndex; i++) { 2625 focusable[i] = false; 2626 } 2627 for (int i = firstFocusableIndex; i < focusable.length; i++) { 2628 focusable[i] = true; 2629 } 2630 intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable); 2631 initActivity(intent); 2632 assertTrue(mGridView.isFocused()); 2633 2634 if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) { 2635 sendKey(KeyEvent.KEYCODE_DPAD_LEFT); 2636 } else { 2637 sendKey(KeyEvent.KEYCODE_DPAD_RIGHT); 2638 } 2639 waitForScrollIdle(mVerifyLayout); 2640 assertEquals(firstFocusableIndex, mGridView.getSelectedPosition()); 2641 assertTrue(mGridView.getLayoutManager().findViewByPosition(firstFocusableIndex).hasFocus()); 2642 } 2643 2644 @Test 2645 public void testFocusOutOfEmptyListView() throws Throwable { 2646 2647 Intent intent = new Intent(); 2648 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2649 R.layout.horizontal_linear); 2650 final int numItems = 100; 2651 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2652 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2653 mOrientation = BaseGridView.HORIZONTAL; 2654 mNumRows = 1; 2655 initActivity(intent); 2656 2657 final View horizontalGridView = new HorizontalGridViewEx(mGridView.getContext()); 2658 mActivityTestRule.runOnUiThread(new Runnable() { 2659 @Override 2660 public void run() { 2661 horizontalGridView.setFocusable(true); 2662 horizontalGridView.setFocusableInTouchMode(true); 2663 horizontalGridView.setLayoutParams(new ViewGroup.LayoutParams(100, 100)); 2664 ((ViewGroup) mGridView.getParent()).addView(horizontalGridView, 0); 2665 horizontalGridView.requestFocus(); 2666 } 2667 }); 2668 2669 assertTrue(horizontalGridView.isFocused()); 2670 2671 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 2672 2673 assertTrue(mGridView.hasFocus()); 2674 } 2675 2676 @Test 2677 public void testTransferFocusToChildWhenGainFocus() throws Throwable { 2678 2679 Intent intent = new Intent(); 2680 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2681 R.layout.horizontal_linear); 2682 final int numItems = 100; 2683 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2684 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2685 mOrientation = BaseGridView.HORIZONTAL; 2686 mNumRows = 1; 2687 boolean[] focusable = new boolean[numItems]; 2688 final int firstFocusableIndex = 1; 2689 for (int i = 0; i < firstFocusableIndex; i++) { 2690 focusable[i] = false; 2691 } 2692 for (int i = firstFocusableIndex; i < focusable.length; i++) { 2693 focusable[i] = true; 2694 } 2695 intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable); 2696 initActivity(intent); 2697 2698 assertEquals(firstFocusableIndex, mGridView.getSelectedPosition()); 2699 assertTrue(mGridView.getLayoutManager().findViewByPosition(firstFocusableIndex).hasFocus()); 2700 } 2701 2702 @Test 2703 public void testFocusFromSecondChild() throws Throwable { 2704 2705 Intent intent = new Intent(); 2706 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2707 R.layout.horizontal_linear); 2708 final int numItems = 100; 2709 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2710 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2711 mOrientation = BaseGridView.HORIZONTAL; 2712 mNumRows = 1; 2713 boolean[] focusable = new boolean[numItems]; 2714 for (int i = 0; i < focusable.length; i++) { 2715 focusable[i] = false; 2716 } 2717 intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable); 2718 initActivity(intent); 2719 2720 // switching Adapter to cause a full rebind, test if it will focus to second item. 2721 performAndWaitForAnimation(new Runnable() { 2722 @Override 2723 public void run() { 2724 mActivity.mNumItems = numItems; 2725 mActivity.mItemFocusables[1] = true; 2726 mActivity.rebindToNewAdapter(); 2727 } 2728 }); 2729 assertTrue(mGridView.findViewHolderForAdapterPosition(1).itemView.hasFocus()); 2730 } 2731 2732 @Test 2733 public void removeFocusableItemAndFocusableRecyclerViewGetsFocus() throws Throwable { 2734 final int numItems = 100; 2735 final int numColumns = 3; 2736 final int focusableIndex = 2; 2737 2738 Intent intent = new Intent(); 2739 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2740 R.layout.vertical_grid); 2741 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2742 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2743 mOrientation = BaseGridView.VERTICAL; 2744 mNumRows = numColumns; 2745 boolean[] focusable = new boolean[numItems]; 2746 for (int i = 0; i < focusable.length; i++) { 2747 focusable[i] = false; 2748 } 2749 focusable[focusableIndex] = true; 2750 intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable); 2751 initActivity(intent); 2752 2753 mActivityTestRule.runOnUiThread(new Runnable() { 2754 @Override 2755 public void run() { 2756 mGridView.setSelectedPositionSmooth(focusableIndex); 2757 } 2758 }); 2759 waitForScrollIdle(mVerifyLayout); 2760 assertEquals(focusableIndex, mGridView.getSelectedPosition()); 2761 2762 performAndWaitForAnimation(new Runnable() { 2763 @Override 2764 public void run() { 2765 mActivity.removeItems(focusableIndex, 1); 2766 } 2767 }); 2768 assertTrue(dumpGridView(mGridView), mGridView.isFocused()); 2769 } 2770 2771 @Test 2772 public void removeFocusableItemAndUnFocusableRecyclerViewLosesFocus() throws Throwable { 2773 final int numItems = 100; 2774 final int numColumns = 3; 2775 final int focusableIndex = 2; 2776 2777 Intent intent = new Intent(); 2778 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2779 R.layout.vertical_grid); 2780 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2781 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2782 mOrientation = BaseGridView.VERTICAL; 2783 mNumRows = numColumns; 2784 boolean[] focusable = new boolean[numItems]; 2785 for (int i = 0; i < focusable.length; i++) { 2786 focusable[i] = false; 2787 } 2788 focusable[focusableIndex] = true; 2789 intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable); 2790 initActivity(intent); 2791 2792 mActivityTestRule.runOnUiThread(new Runnable() { 2793 @Override 2794 public void run() { 2795 mGridView.setFocusableInTouchMode(false); 2796 mGridView.setFocusable(false); 2797 mGridView.setSelectedPositionSmooth(focusableIndex); 2798 } 2799 }); 2800 waitForScrollIdle(mVerifyLayout); 2801 assertEquals(focusableIndex, mGridView.getSelectedPosition()); 2802 2803 performAndWaitForAnimation(new Runnable() { 2804 @Override 2805 public void run() { 2806 mActivity.removeItems(focusableIndex, 1); 2807 } 2808 }); 2809 assertFalse(dumpGridView(mGridView), mGridView.hasFocus()); 2810 } 2811 2812 @Test 2813 public void testNonFocusableVertical() throws Throwable { 2814 final int numItems = 200; 2815 final int startPos = 44; 2816 final int skips = 20; 2817 final int numColumns = 3; 2818 final int endPos = startPos + numColumns * (skips + 1); 2819 2820 Intent intent = new Intent(); 2821 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2822 R.layout.vertical_grid); 2823 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2824 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2825 mOrientation = BaseGridView.VERTICAL; 2826 mNumRows = numColumns; 2827 boolean[] focusable = new boolean[numItems]; 2828 for (int i = 0; i < focusable.length; i++) { 2829 focusable[i] = true; 2830 } 2831 for (int i = startPos + mNumRows, j = 0; j < skips; i += mNumRows, j++) { 2832 focusable[i] = false; 2833 } 2834 intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable); 2835 initActivity(intent); 2836 2837 mGridView.setSelectedPositionSmooth(startPos); 2838 waitForScrollIdle(mVerifyLayout); 2839 2840 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 2841 waitForScrollIdle(mVerifyLayout); 2842 assertEquals(endPos, mGridView.getSelectedPosition()); 2843 2844 sendKey(KeyEvent.KEYCODE_DPAD_UP); 2845 waitForScrollIdle(mVerifyLayout); 2846 assertEquals(startPos, mGridView.getSelectedPosition()); 2847 2848 } 2849 2850 @Test 2851 public void testLtrFocusOutStartDisabled() throws Throwable { 2852 final int numItems = 200; 2853 2854 Intent intent = new Intent(); 2855 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid_ltr); 2856 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2857 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2858 mOrientation = BaseGridView.VERTICAL; 2859 mNumRows = 2; 2860 initActivity(intent); 2861 2862 mActivityTestRule.runOnUiThread(new Runnable() { 2863 @Override 2864 public void run() { 2865 mGridView.requestFocus(); 2866 mGridView.setSelectedPositionSmooth(0); 2867 } 2868 }); 2869 waitForScrollIdle(mVerifyLayout); 2870 2871 sendKey(KeyEvent.KEYCODE_DPAD_LEFT); 2872 waitForScrollIdle(mVerifyLayout); 2873 assertTrue(mGridView.hasFocus()); 2874 } 2875 2876 @Test 2877 public void testVerticalGridRtl() throws Throwable { 2878 final int numItems = 200; 2879 2880 Intent intent = new Intent(); 2881 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid_rtl); 2882 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2883 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2884 mOrientation = BaseGridView.VERTICAL; 2885 mNumRows = 2; 2886 initActivity(intent); 2887 2888 waitForScrollIdle(mVerifyLayout); 2889 2890 View item0 = mGridView.findViewHolderForAdapterPosition(0).itemView; 2891 View item1 = mGridView.findViewHolderForAdapterPosition(1).itemView; 2892 assertEquals(mGridView.getWidth() - mGridView.getPaddingRight(), item0.getRight()); 2893 assertEquals(item0.getLeft(), item1.getRight() + mGridView.getHorizontalSpacing()); 2894 } 2895 2896 @Test 2897 public void testRtlFocusOutStartDisabled() throws Throwable { 2898 final int numItems = 200; 2899 2900 Intent intent = new Intent(); 2901 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid_rtl); 2902 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2903 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2904 mOrientation = BaseGridView.VERTICAL; 2905 mNumRows = 1; 2906 initActivity(intent); 2907 2908 mActivityTestRule.runOnUiThread(new Runnable() { 2909 @Override 2910 public void run() { 2911 mGridView.requestFocus(); 2912 mGridView.setSelectedPositionSmooth(0); 2913 } 2914 }); 2915 waitForScrollIdle(mVerifyLayout); 2916 2917 sendKey(KeyEvent.KEYCODE_DPAD_RIGHT); 2918 waitForScrollIdle(mVerifyLayout); 2919 assertTrue(mGridView.hasFocus()); 2920 } 2921 2922 @Test 2923 public void testTransferFocusable() throws Throwable { 2924 final int numItems = 200; 2925 final int numColumns = 3; 2926 final int startPos = 1; 2927 2928 Intent intent = new Intent(); 2929 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2930 R.layout.horizontal_grid); 2931 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2932 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2933 mOrientation = BaseGridView.HORIZONTAL; 2934 mNumRows = numColumns; 2935 boolean[] focusable = new boolean[numItems]; 2936 for (int i = 0; i < focusable.length; i++) { 2937 focusable[i] = true; 2938 } 2939 for (int i = 0; i < startPos; i++) { 2940 focusable[i] = false; 2941 } 2942 intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable); 2943 initActivity(intent); 2944 2945 changeArraySize(0); 2946 assertTrue(mGridView.isFocused()); 2947 2948 changeArraySize(numItems); 2949 assertTrue(mGridView.getLayoutManager().findViewByPosition(startPos).hasFocus()); 2950 } 2951 2952 @Test 2953 public void testTransferFocusable2() throws Throwable { 2954 final int numItems = 200; 2955 final int numColumns = 3; 2956 final int startPos = 3; // make sure view at startPos is in visible area. 2957 2958 Intent intent = new Intent(); 2959 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2960 R.layout.horizontal_grid); 2961 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems); 2962 intent.putExtra(GridActivity.EXTRA_STAGGERED, true); 2963 mOrientation = BaseGridView.HORIZONTAL; 2964 mNumRows = numColumns; 2965 boolean[] focusable = new boolean[numItems]; 2966 for (int i = 0; i < focusable.length; i++) { 2967 focusable[i] = true; 2968 } 2969 for (int i = 0; i < startPos; i++) { 2970 focusable[i] = false; 2971 } 2972 intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable); 2973 initActivity(intent); 2974 2975 assertTrue(mGridView.getLayoutManager().findViewByPosition(startPos).hasFocus()); 2976 2977 changeArraySize(0); 2978 assertTrue(mGridView.isFocused()); 2979 2980 changeArraySize(numItems); 2981 assertTrue(mGridView.getLayoutManager().findViewByPosition(startPos).hasFocus()); 2982 } 2983 2984 @Test 2985 public void testNonFocusableLoseInFastLayout() throws Throwable { 2986 Intent intent = new Intent(); 2987 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 2988 R.layout.vertical_linear); 2989 int[] items = new int[300]; 2990 for (int i = 0; i < items.length; i++) { 2991 items[i] = 480; 2992 } 2993 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 2994 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 2995 intent.putExtra(GridActivity.EXTRA_REQUEST_LAYOUT_ONFOCUS, true); 2996 mOrientation = BaseGridView.VERTICAL; 2997 mNumRows = 1; 2998 int pressDown = 15; 2999 3000 initActivity(intent); 3001 3002 mGridView.setSelectedPositionSmooth(0); 3003 waitForScrollIdle(mVerifyLayout); 3004 3005 for (int i = 0; i < pressDown; i++) { 3006 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 3007 } 3008 waitForScrollIdle(mVerifyLayout); 3009 assertFalse(mGridView.isFocused()); 3010 3011 } 3012 3013 @Test 3014 public void testFocusableViewAvailable() throws Throwable { 3015 Intent intent = new Intent(); 3016 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3017 R.layout.vertical_linear); 3018 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 0); 3019 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3020 intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, 3021 new boolean[]{false, false, true, false, false}); 3022 mOrientation = BaseGridView.VERTICAL; 3023 mNumRows = 1; 3024 3025 initActivity(intent); 3026 3027 mActivityTestRule.runOnUiThread(new Runnable() { 3028 @Override 3029 public void run() { 3030 // RecyclerView does not respect focusable and focusableInTouchMode flag, so 3031 // set flags in code. 3032 mGridView.setFocusableInTouchMode(false); 3033 mGridView.setFocusable(false); 3034 } 3035 }); 3036 3037 assertFalse(mGridView.isFocused()); 3038 3039 final boolean[] scrolled = new boolean[]{false}; 3040 mGridView.addOnScrollListener(new RecyclerView.OnScrollListener() { 3041 @Override 3042 public void onScrolled(RecyclerView recyclerView, int dx, int dy){ 3043 if (dy > 0) { 3044 scrolled[0] = true; 3045 } 3046 } 3047 }); 3048 performAndWaitForAnimation(new Runnable() { 3049 @Override 3050 public void run() { 3051 mActivity.addItems(0, new int[]{200, 300, 500, 500, 200}); 3052 } 3053 }); 3054 waitForScrollIdle(mVerifyLayout); 3055 3056 assertFalse("GridView should not be scrolled", scrolled[0]); 3057 assertTrue(mGridView.getLayoutManager().findViewByPosition(2).hasFocus()); 3058 3059 } 3060 3061 @Test 3062 public void testSetSelectionWithDelta() throws Throwable { 3063 Intent intent = new Intent(); 3064 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3065 R.layout.vertical_linear); 3066 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 300); 3067 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3068 mOrientation = BaseGridView.VERTICAL; 3069 mNumRows = 1; 3070 3071 initActivity(intent); 3072 3073 mActivityTestRule.runOnUiThread(new Runnable() { 3074 @Override 3075 public void run() { 3076 mGridView.setSelectedPositionSmooth(3); 3077 } 3078 }); 3079 waitForScrollIdle(mVerifyLayout); 3080 int top1 = mGridView.getLayoutManager().findViewByPosition(3).getTop(); 3081 3082 humanDelay(1000); 3083 3084 // scroll to position with delta 3085 setSelectedPosition(3, 100); 3086 int top2 = mGridView.getLayoutManager().findViewByPosition(3).getTop(); 3087 assertEquals(top1 - 100, top2); 3088 3089 // scroll to same position without delta, it will be reset 3090 setSelectedPosition(3, 0); 3091 int top3 = mGridView.getLayoutManager().findViewByPosition(3).getTop(); 3092 assertEquals(top1, top3); 3093 3094 // scroll invisible item after last visible item 3095 final int lastVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager()) 3096 .mGrid.getLastVisibleIndex(); 3097 setSelectedPosition(lastVisiblePos + 1, 100); 3098 int top4 = mGridView.getLayoutManager().findViewByPosition(lastVisiblePos + 1).getTop(); 3099 assertEquals(top1 - 100, top4); 3100 3101 // scroll invisible item before first visible item 3102 final int firstVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager()) 3103 .mGrid.getFirstVisibleIndex(); 3104 setSelectedPosition(firstVisiblePos - 1, 100); 3105 int top5 = mGridView.getLayoutManager().findViewByPosition(firstVisiblePos - 1).getTop(); 3106 assertEquals(top1 - 100, top5); 3107 3108 // scroll to invisible item that is far away. 3109 setSelectedPosition(50, 100); 3110 int top6 = mGridView.getLayoutManager().findViewByPosition(50).getTop(); 3111 assertEquals(top1 - 100, top6); 3112 3113 // scroll to invisible item that is far away. 3114 mActivityTestRule.runOnUiThread(new Runnable() { 3115 @Override 3116 public void run() { 3117 mGridView.setSelectedPositionSmooth(100); 3118 } 3119 }); 3120 waitForScrollIdle(mVerifyLayout); 3121 int top7 = mGridView.getLayoutManager().findViewByPosition(100).getTop(); 3122 assertEquals(top1, top7); 3123 3124 // scroll to invisible item that is far away. 3125 setSelectedPosition(10, 50); 3126 int top8 = mGridView.getLayoutManager().findViewByPosition(10).getTop(); 3127 assertEquals(top1 - 50, top8); 3128 } 3129 3130 @Test 3131 public void testSetSelectionWithDeltaInGrid() throws Throwable { 3132 Intent intent = new Intent(); 3133 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3134 R.layout.vertical_grid); 3135 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 500); 3136 intent.putExtra(GridActivity.EXTRA_STAGGERED, true); 3137 mOrientation = BaseGridView.VERTICAL; 3138 mNumRows = 3; 3139 3140 initActivity(intent); 3141 3142 mActivityTestRule.runOnUiThread(new Runnable() { 3143 @Override 3144 public void run() { 3145 mGridView.setSelectedPositionSmooth(10); 3146 } 3147 }); 3148 waitForScrollIdle(mVerifyLayout); 3149 int top1 = getCenterY(mGridView.getLayoutManager().findViewByPosition(10)); 3150 3151 humanDelay(500); 3152 3153 // scroll to position with delta 3154 setSelectedPosition(20, 100); 3155 int top2 = getCenterY(mGridView.getLayoutManager().findViewByPosition(20)); 3156 assertEquals(top1 - 100, top2); 3157 3158 // scroll to same position without delta, it will be reset 3159 setSelectedPosition(20, 0); 3160 int top3 = getCenterY(mGridView.getLayoutManager().findViewByPosition(20)); 3161 assertEquals(top1, top3); 3162 3163 // scroll invisible item after last visible item 3164 final int lastVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager()) 3165 .mGrid.getLastVisibleIndex(); 3166 setSelectedPosition(lastVisiblePos + 1, 100); 3167 int top4 = getCenterY(mGridView.getLayoutManager().findViewByPosition(lastVisiblePos + 1)); 3168 verifyMargin(); 3169 assertEquals(top1 - 100, top4); 3170 3171 // scroll invisible item before first visible item 3172 final int firstVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager()) 3173 .mGrid.getFirstVisibleIndex(); 3174 setSelectedPosition(firstVisiblePos - 1, 100); 3175 int top5 = getCenterY(mGridView.getLayoutManager().findViewByPosition(firstVisiblePos - 1)); 3176 assertEquals(top1 - 100, top5); 3177 3178 // scroll to invisible item that is far away. 3179 setSelectedPosition(100, 100); 3180 int top6 = getCenterY(mGridView.getLayoutManager().findViewByPosition(100)); 3181 assertEquals(top1 - 100, top6); 3182 3183 // scroll to invisible item that is far away. 3184 mActivityTestRule.runOnUiThread(new Runnable() { 3185 @Override 3186 public void run() { 3187 mGridView.setSelectedPositionSmooth(200); 3188 } 3189 }); 3190 waitForScrollIdle(mVerifyLayout); 3191 Thread.sleep(500); 3192 int top7 = getCenterY(mGridView.getLayoutManager().findViewByPosition(200)); 3193 assertEquals(top1, top7); 3194 3195 // scroll to invisible item that is far away. 3196 setSelectedPosition(10, 50); 3197 int top8 = getCenterY(mGridView.getLayoutManager().findViewByPosition(10)); 3198 assertEquals(top1 - 50, top8); 3199 } 3200 3201 3202 @Test 3203 public void testSetSelectionWithDeltaInGrid1() throws Throwable { 3204 Intent intent = new Intent(); 3205 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3206 R.layout.vertical_grid); 3207 intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{ 3208 193,176,153,141,203,184,232,139,177,206,222,136,132,237,172,137, 3209 188,172,163,213,158,219,209,147,133,229,170,197,138,215,188,205, 3210 223,192,225,170,195,127,229,229,210,195,134,142,160,139,130,222, 3211 150,163,180,176,157,137,234,169,159,167,182,150,224,231,202,236, 3212 123,140,181,223,120,185,183,221,123,210,134,158,166,208,149,128, 3213 192,214,212,198,133,140,158,133,229,173,226,141,180,128,127,218, 3214 192,235,183,213,216,150,143,193,125,141,219,210,195,195,192,191, 3215 212,236,157,189,160,220,147,158,220,199,233,231,201,180,168,141, 3216 156,204,191,183,190,153,123,210,238,151,139,221,223,200,175,191, 3217 132,184,197,204,236,157,230,151,195,219,212,143,172,149,219,184, 3218 164,211,132,187,172,142,174,146,127,147,206,238,188,129,199,226, 3219 132,220,210,159,235,153,208,182,196,123,180,159,131,135,175,226, 3220 127,134,237,211,133,225,132,124,160,226,224,200,173,137,217,169, 3221 182,183,176,185,122,168,195,159,172,129,126,129,166,136,149,220, 3222 178,191,192,238,180,208,234,154,222,206,239,228,129,140,203,125, 3223 214,175,125,169,196,132,234,138,192,142,234,190,215,232,239,122, 3224 188,158,128,221,159,237,207,157,232,138,132,214,122,199,121,191, 3225 199,209,126,164,175,187,173,186,194,224,191,196,146,208,213,210, 3226 164,176,202,213,123,157,179,138,217,129,186,166,237,211,157,130, 3227 137,132,171,232,216,239,180,151,137,132,190,133,218,155,171,227, 3228 193,147,197,164,120,218,193,154,170,196,138,222,161,235,143,154, 3229 192,178,228,195,178,133,203,178,173,206,178,212,136,157,169,124, 3230 172,121,128,223,238,125,217,187,184,156,169,215,231,124,210,174, 3231 146,226,185,134,223,228,183,182,136,133,199,146,180,233,226,225, 3232 174,233,145,235,216,170,192,171,132,132,134,223,233,148,154,162, 3233 192,179,197,203,139,197,174,187,135,132,180,136,192,195,124,221, 3234 120,189,233,233,146,225,234,163,215,143,132,198,156,205,151,190, 3235 204,239,221,229,123,138,134,217,219,136,218,215,167,139,195,125, 3236 202,225,178,226,145,208,130,194,228,197,157,215,124,147,174,123, 3237 237,140,172,181,161,151,229,216,199,199,179,213,146,122,222,162, 3238 139,173,165,150,160,217,207,137,165,175,129,158,134,133,178,199, 3239 215,213,122,197 3240 }); 3241 intent.putExtra(GridActivity.EXTRA_STAGGERED, true); 3242 mOrientation = BaseGridView.VERTICAL; 3243 mNumRows = 3; 3244 3245 initActivity(intent); 3246 3247 mActivityTestRule.runOnUiThread(new Runnable() { 3248 @Override 3249 public void run() { 3250 mGridView.setSelectedPositionSmooth(10); 3251 } 3252 }); 3253 waitForScrollIdle(mVerifyLayout); 3254 int top1 = getCenterY(mGridView.getLayoutManager().findViewByPosition(10)); 3255 3256 humanDelay(500); 3257 3258 // scroll to position with delta 3259 setSelectedPosition(20, 100); 3260 int top2 = getCenterY(mGridView.getLayoutManager().findViewByPosition(20)); 3261 assertEquals(top1 - 100, top2); 3262 3263 // scroll to same position without delta, it will be reset 3264 setSelectedPosition(20, 0); 3265 int top3 = getCenterY(mGridView.getLayoutManager().findViewByPosition(20)); 3266 assertEquals(top1, top3); 3267 3268 // scroll invisible item after last visible item 3269 final int lastVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager()) 3270 .mGrid.getLastVisibleIndex(); 3271 setSelectedPosition(lastVisiblePos + 1, 100); 3272 int top4 = getCenterY(mGridView.getLayoutManager().findViewByPosition(lastVisiblePos + 1)); 3273 verifyMargin(); 3274 assertEquals(top1 - 100, top4); 3275 3276 // scroll invisible item before first visible item 3277 final int firstVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager()) 3278 .mGrid.getFirstVisibleIndex(); 3279 setSelectedPosition(firstVisiblePos - 1, 100); 3280 int top5 = getCenterY(mGridView.getLayoutManager().findViewByPosition(firstVisiblePos - 1)); 3281 assertEquals(top1 - 100, top5); 3282 3283 // scroll to invisible item that is far away. 3284 setSelectedPosition(100, 100); 3285 int top6 = getCenterY(mGridView.getLayoutManager().findViewByPosition(100)); 3286 assertEquals(top1 - 100, top6); 3287 3288 // scroll to invisible item that is far away. 3289 mActivityTestRule.runOnUiThread(new Runnable() { 3290 @Override 3291 public void run() { 3292 mGridView.setSelectedPositionSmooth(200); 3293 } 3294 }); 3295 waitForScrollIdle(mVerifyLayout); 3296 Thread.sleep(500); 3297 int top7 = getCenterY(mGridView.getLayoutManager().findViewByPosition(200)); 3298 assertEquals(top1, top7); 3299 3300 // scroll to invisible item that is far away. 3301 setSelectedPosition(10, 50); 3302 int top8 = getCenterY(mGridView.getLayoutManager().findViewByPosition(10)); 3303 assertEquals(top1 - 50, top8); 3304 } 3305 3306 @Test 3307 public void testSmoothScrollSelectionEvents() throws Throwable { 3308 Intent intent = new Intent(); 3309 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3310 R.layout.vertical_grid); 3311 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 500); 3312 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3313 mOrientation = BaseGridView.VERTICAL; 3314 mNumRows = 3; 3315 initActivity(intent); 3316 3317 mActivityTestRule.runOnUiThread(new Runnable() { 3318 @Override 3319 public void run() { 3320 mGridView.setSelectedPositionSmooth(30); 3321 } 3322 }); 3323 waitForScrollIdle(mVerifyLayout); 3324 humanDelay(500); 3325 3326 final ArrayList<Integer> selectedPositions = new ArrayList<Integer>(); 3327 mGridView.setOnChildSelectedListener(new OnChildSelectedListener() { 3328 @Override 3329 public void onChildSelected(ViewGroup parent, View view, int position, long id) { 3330 selectedPositions.add(position); 3331 } 3332 }); 3333 3334 sendRepeatedKeys(10, KeyEvent.KEYCODE_DPAD_UP); 3335 humanDelay(500); 3336 waitForScrollIdle(mVerifyLayout); 3337 // should only get childselected event for item 0 once 3338 assertTrue(selectedPositions.size() > 0); 3339 assertEquals(0, selectedPositions.get(selectedPositions.size() - 1).intValue()); 3340 for (int i = selectedPositions.size() - 2; i >= 0; i--) { 3341 assertFalse(0 == selectedPositions.get(i).intValue()); 3342 } 3343 3344 } 3345 3346 @Test 3347 public void testSmoothScrollSelectionEventsLinear() throws Throwable { 3348 Intent intent = new Intent(); 3349 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3350 R.layout.vertical_linear); 3351 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 500); 3352 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3353 mOrientation = BaseGridView.VERTICAL; 3354 mNumRows = 1; 3355 initActivity(intent); 3356 3357 mActivityTestRule.runOnUiThread(new Runnable() { 3358 @Override 3359 public void run() { 3360 mGridView.setSelectedPositionSmooth(10); 3361 } 3362 }); 3363 waitForScrollIdle(mVerifyLayout); 3364 humanDelay(500); 3365 3366 final ArrayList<Integer> selectedPositions = new ArrayList<Integer>(); 3367 mGridView.setOnChildSelectedListener(new OnChildSelectedListener() { 3368 @Override 3369 public void onChildSelected(ViewGroup parent, View view, int position, long id) { 3370 selectedPositions.add(position); 3371 } 3372 }); 3373 3374 sendRepeatedKeys(10, KeyEvent.KEYCODE_DPAD_UP); 3375 humanDelay(500); 3376 waitForScrollIdle(mVerifyLayout); 3377 // should only get childselected event for item 0 once 3378 assertTrue(selectedPositions.size() > 0); 3379 assertEquals(0, selectedPositions.get(selectedPositions.size() - 1).intValue()); 3380 for (int i = selectedPositions.size() - 2; i >= 0; i--) { 3381 assertFalse(0 == selectedPositions.get(i).intValue()); 3382 } 3383 3384 } 3385 3386 @Test 3387 public void testScrollToNoneExisting() throws Throwable { 3388 Intent intent = new Intent(); 3389 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3390 R.layout.vertical_grid); 3391 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100); 3392 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3393 mOrientation = BaseGridView.VERTICAL; 3394 mNumRows = 3; 3395 initActivity(intent); 3396 3397 mActivityTestRule.runOnUiThread(new Runnable() { 3398 @Override 3399 public void run() { 3400 mGridView.setSelectedPositionSmooth(99); 3401 } 3402 }); 3403 waitForScrollIdle(mVerifyLayout); 3404 humanDelay(500); 3405 3406 3407 mActivityTestRule.runOnUiThread(new Runnable() { 3408 @Override 3409 public void run() { 3410 mGridView.setSelectedPositionSmooth(50); 3411 } 3412 }); 3413 Thread.sleep(100); 3414 mActivityTestRule.runOnUiThread(new Runnable() { 3415 @Override 3416 public void run() { 3417 mGridView.requestLayout(); 3418 mGridView.setSelectedPositionSmooth(0); 3419 } 3420 }); 3421 waitForScrollIdle(mVerifyLayout); 3422 humanDelay(500); 3423 3424 } 3425 3426 @Test 3427 public void testSmoothscrollerInterrupted() throws Throwable { 3428 Intent intent = new Intent(); 3429 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3430 R.layout.vertical_linear); 3431 intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true); 3432 int[] items = new int[100]; 3433 for (int i = 0; i < items.length; i++) { 3434 items[i] = 680; 3435 } 3436 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 3437 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3438 mOrientation = BaseGridView.VERTICAL; 3439 mNumRows = 1; 3440 3441 initActivity(intent); 3442 3443 mGridView.setSelectedPositionSmooth(0); 3444 waitForScrollIdle(mVerifyLayout); 3445 assertTrue(mGridView.getChildAt(0).hasFocus()); 3446 3447 // Pressing lots of key to make sure smooth scroller is running 3448 for (int i = 0; i < 20; i++) { 3449 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 3450 } 3451 while (mGridView.getLayoutManager().isSmoothScrolling() 3452 || mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE) { 3453 // Repeatedly pressing to make sure pending keys does not drop to zero. 3454 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 3455 } 3456 } 3457 3458 @Test 3459 public void testSmoothscrollerCancelled() throws Throwable { 3460 Intent intent = new Intent(); 3461 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3462 R.layout.vertical_linear); 3463 intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true); 3464 int[] items = new int[100]; 3465 for (int i = 0; i < items.length; i++) { 3466 items[i] = 680; 3467 } 3468 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 3469 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3470 mOrientation = BaseGridView.VERTICAL; 3471 mNumRows = 1; 3472 3473 initActivity(intent); 3474 3475 mGridView.setSelectedPositionSmooth(0); 3476 waitForScrollIdle(mVerifyLayout); 3477 assertTrue(mGridView.getChildAt(0).hasFocus()); 3478 3479 int targetPosition = items.length - 1; 3480 mGridView.setSelectedPositionSmooth(targetPosition); 3481 mActivityTestRule.runOnUiThread(new Runnable() { 3482 @Override 3483 public void run() { 3484 mGridView.stopScroll(); 3485 } 3486 }); 3487 waitForScrollIdle(); 3488 waitForItemAnimation(); 3489 assertEquals(mGridView.getSelectedPosition(), targetPosition); 3490 assertSame(mGridView.getLayoutManager().findViewByPosition(targetPosition), 3491 mGridView.findFocus()); 3492 } 3493 3494 @Test 3495 public void testSetNumRowsAndAddItem() throws Throwable { 3496 Intent intent = new Intent(); 3497 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3498 R.layout.vertical_linear); 3499 intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true); 3500 int[] items = new int[2]; 3501 for (int i = 0; i < items.length; i++) { 3502 items[i] = 300; 3503 } 3504 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 3505 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3506 mOrientation = BaseGridView.VERTICAL; 3507 mNumRows = 1; 3508 3509 initActivity(intent); 3510 3511 mGridView.setSelectedPositionSmooth(0); 3512 waitForScrollIdle(mVerifyLayout); 3513 3514 mActivity.addItems(items.length, new int[]{300}); 3515 3516 mActivityTestRule.runOnUiThread(new Runnable() { 3517 @Override 3518 public void run() { 3519 ((VerticalGridView) mGridView).setNumColumns(2); 3520 } 3521 }); 3522 Thread.sleep(1000); 3523 assertTrue(mGridView.getChildAt(2).getLeft() != mGridView.getChildAt(1).getLeft()); 3524 } 3525 3526 3527 @Test 3528 public void testRequestLayoutBugInLayout() throws Throwable { 3529 Intent intent = new Intent(); 3530 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3531 R.layout.vertical_linear); 3532 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout); 3533 intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true); 3534 int[] items = new int[100]; 3535 for (int i = 0; i < items.length; i++) { 3536 items[i] = 300; 3537 } 3538 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 3539 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3540 mOrientation = BaseGridView.VERTICAL; 3541 mNumRows = 1; 3542 3543 initActivity(intent); 3544 3545 mActivityTestRule.runOnUiThread(new Runnable() { 3546 @Override 3547 public void run() { 3548 mGridView.setSelectedPositionSmooth(1); 3549 } 3550 }); 3551 waitForScrollIdle(mVerifyLayout); 3552 3553 sendKey(KeyEvent.KEYCODE_DPAD_UP); 3554 waitForScrollIdle(mVerifyLayout); 3555 3556 assertEquals("Line 2", ((TextView) mGridView.findFocus()).getText().toString()); 3557 } 3558 3559 3560 @Test 3561 public void testChangeLayoutInChild() throws Throwable { 3562 Intent intent = new Intent(); 3563 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3564 R.layout.vertical_linear_wrap_content); 3565 intent.putExtra(GridActivity.EXTRA_REQUEST_LAYOUT_ONFOCUS, true); 3566 int[] items = new int[2]; 3567 for (int i = 0; i < items.length; i++) { 3568 items[i] = 300; 3569 } 3570 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 3571 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3572 mOrientation = BaseGridView.VERTICAL; 3573 mNumRows = 1; 3574 3575 initActivity(intent); 3576 3577 mActivityTestRule.runOnUiThread(new Runnable() { 3578 @Override 3579 public void run() { 3580 mGridView.setSelectedPositionSmooth(0); 3581 } 3582 }); 3583 waitForScrollIdle(mVerifyLayout); 3584 verifyMargin(); 3585 3586 mActivityTestRule.runOnUiThread(new Runnable() { 3587 @Override 3588 public void run() { 3589 mGridView.setSelectedPositionSmooth(1); 3590 } 3591 }); 3592 waitForScrollIdle(mVerifyLayout); 3593 verifyMargin(); 3594 } 3595 3596 @Test 3597 public void testWrapContent() throws Throwable { 3598 Intent intent = new Intent(); 3599 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3600 R.layout.horizontal_grid_wrap); 3601 int[] items = new int[200]; 3602 for (int i = 0; i < items.length; i++) { 3603 items[i] = 300; 3604 } 3605 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 3606 mOrientation = BaseGridView.HORIZONTAL; 3607 mNumRows = 1; 3608 3609 initActivity(intent); 3610 3611 mActivityTestRule.runOnUiThread(new Runnable() { 3612 @Override 3613 public void run() { 3614 mActivity.attachToNewAdapter(new int[0]); 3615 } 3616 }); 3617 3618 } 3619 3620 @Test 3621 public void testZeroFixedSecondarySize() throws Throwable { 3622 Intent intent = new Intent(); 3623 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3624 R.layout.vertical_linear_measured_with_zero); 3625 intent.putExtra(GridActivity.EXTRA_SECONDARY_SIZE_ZERO, true); 3626 int[] items = new int[2]; 3627 for (int i = 0; i < items.length; i++) { 3628 items[i] = 0; 3629 } 3630 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 3631 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3632 mOrientation = BaseGridView.VERTICAL; 3633 mNumRows = 1; 3634 3635 initActivity(intent); 3636 3637 } 3638 3639 @Test 3640 public void testChildStates() throws Throwable { 3641 Intent intent = new Intent(); 3642 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 3643 int[] items = new int[100]; 3644 for (int i = 0; i < items.length; i++) { 3645 items[i] = 200; 3646 } 3647 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 3648 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3649 intent.putExtra(GridActivity.EXTRA_REQUEST_LAYOUT_ONFOCUS, true); 3650 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.selectable_text_view); 3651 mOrientation = BaseGridView.VERTICAL; 3652 mNumRows = 1; 3653 3654 initActivity(intent); 3655 mGridView.setSaveChildrenPolicy(VerticalGridView.SAVE_ALL_CHILD); 3656 3657 final SparseArray<Parcelable> container = new SparseArray<Parcelable>(); 3658 3659 // 1 Save view states 3660 mActivityTestRule.runOnUiThread(new Runnable() { 3661 @Override 3662 public void run() { 3663 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(0)) 3664 .getText()), 0, 1); 3665 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(1)) 3666 .getText()), 0, 1); 3667 mGridView.saveHierarchyState(container); 3668 } 3669 }); 3670 3671 // 2 Change view states 3672 mActivityTestRule.runOnUiThread(new Runnable() { 3673 @Override 3674 public void run() { 3675 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(0)) 3676 .getText()), 1, 2); 3677 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(1)) 3678 .getText()), 1, 2); 3679 } 3680 }); 3681 3682 // 3 Detached and re-attached, should still maintain state of (2) 3683 mActivityTestRule.runOnUiThread(new Runnable() { 3684 @Override 3685 public void run() { 3686 mGridView.setSelectedPositionSmooth(1); 3687 } 3688 }); 3689 waitForScrollIdle(mVerifyLayout); 3690 assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionStart(), 1); 3691 assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionEnd(), 2); 3692 assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionStart(), 1); 3693 assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionEnd(), 2); 3694 3695 // 4 Recycled and rebound, should load state from (2) 3696 mActivityTestRule.runOnUiThread(new Runnable() { 3697 @Override 3698 public void run() { 3699 mGridView.setSelectedPositionSmooth(20); 3700 } 3701 }); 3702 waitForScrollIdle(mVerifyLayout); 3703 mActivityTestRule.runOnUiThread(new Runnable() { 3704 @Override 3705 public void run() { 3706 mGridView.setSelectedPositionSmooth(0); 3707 } 3708 }); 3709 waitForScrollIdle(mVerifyLayout); 3710 assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionStart(), 1); 3711 assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionEnd(), 2); 3712 assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionStart(), 1); 3713 assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionEnd(), 2); 3714 } 3715 3716 3717 @Test 3718 public void testNoDispatchSaveChildState() throws Throwable { 3719 Intent intent = new Intent(); 3720 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 3721 int[] items = new int[100]; 3722 for (int i = 0; i < items.length; i++) { 3723 items[i] = 200; 3724 } 3725 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 3726 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3727 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.selectable_text_view); 3728 mOrientation = BaseGridView.VERTICAL; 3729 mNumRows = 1; 3730 3731 initActivity(intent); 3732 mGridView.setSaveChildrenPolicy(VerticalGridView.SAVE_NO_CHILD); 3733 3734 final SparseArray<Parcelable> container = new SparseArray<Parcelable>(); 3735 3736 // 1. Set text selection, save view states should do nothing on child 3737 mActivityTestRule.runOnUiThread(new Runnable() { 3738 @Override 3739 public void run() { 3740 for (int i = 0; i < mGridView.getChildCount(); i++) { 3741 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(i)) 3742 .getText()), 0, 1); 3743 } 3744 mGridView.saveHierarchyState(container); 3745 } 3746 }); 3747 3748 // 2. clear the text selection 3749 mActivityTestRule.runOnUiThread(new Runnable() { 3750 @Override 3751 public void run() { 3752 for (int i = 0; i < mGridView.getChildCount(); i++) { 3753 Selection.removeSelection((Spannable)(((TextView) mGridView.getChildAt(i)) 3754 .getText())); 3755 } 3756 } 3757 }); 3758 3759 // 3. Restore view states should be a no-op for child 3760 mActivityTestRule.runOnUiThread(new Runnable() { 3761 @Override 3762 public void run() { 3763 mGridView.restoreHierarchyState(container); 3764 for (int i = 0; i < mGridView.getChildCount(); i++) { 3765 assertEquals(-1, ((TextView) mGridView.getChildAt(i)).getSelectionStart()); 3766 assertEquals(-1, ((TextView) mGridView.getChildAt(i)).getSelectionEnd()); 3767 } 3768 } 3769 }); 3770 } 3771 3772 3773 static interface ViewTypeProvider { 3774 public int getViewType(int position); 3775 } 3776 3777 static interface ItemAlignmentFacetProvider { 3778 public ItemAlignmentFacet getItemAlignmentFacet(int viewType); 3779 } 3780 3781 static class TwoViewTypesProvider implements ViewTypeProvider { 3782 static int VIEW_TYPE_FIRST = 1; 3783 static int VIEW_TYPE_DEFAULT = 0; 3784 @Override 3785 public int getViewType(int position) { 3786 if (position == 0) { 3787 return VIEW_TYPE_FIRST; 3788 } else { 3789 return VIEW_TYPE_DEFAULT; 3790 } 3791 } 3792 } 3793 3794 static class ChangeableViewTypesProvider implements ViewTypeProvider { 3795 static SparseIntArray sViewTypes = new SparseIntArray(); 3796 @Override 3797 public int getViewType(int position) { 3798 return sViewTypes.get(position); 3799 } 3800 public static void clear() { 3801 sViewTypes.clear(); 3802 } 3803 public static void setViewType(int position, int type) { 3804 sViewTypes.put(position, type); 3805 } 3806 } 3807 3808 static class PositionItemAlignmentFacetProviderForRelativeLayout1 3809 implements ItemAlignmentFacetProvider { 3810 ItemAlignmentFacet mMultipleFacet; 3811 3812 PositionItemAlignmentFacetProviderForRelativeLayout1() { 3813 mMultipleFacet = new ItemAlignmentFacet(); 3814 ItemAlignmentFacet.ItemAlignmentDef[] defs = 3815 new ItemAlignmentFacet.ItemAlignmentDef[2]; 3816 defs[0] = new ItemAlignmentFacet.ItemAlignmentDef(); 3817 defs[0].setItemAlignmentViewId(R.id.t1); 3818 defs[1] = new ItemAlignmentFacet.ItemAlignmentDef(); 3819 defs[1].setItemAlignmentViewId(R.id.t2); 3820 defs[1].setItemAlignmentOffsetPercent(100); 3821 defs[1].setItemAlignmentOffset(-10); 3822 mMultipleFacet.setAlignmentDefs(defs); 3823 } 3824 3825 @Override 3826 public ItemAlignmentFacet getItemAlignmentFacet(int position) { 3827 if (position == 0) { 3828 return mMultipleFacet; 3829 } else { 3830 return null; 3831 } 3832 } 3833 } 3834 3835 @Test 3836 public void testMultipleScrollPosition1() throws Throwable { 3837 Intent intent = new Intent(); 3838 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 3839 R.layout.vertical_linear); 3840 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout); 3841 intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true); 3842 int[] items = new int[100]; 3843 for (int i = 0; i < items.length; i++) { 3844 items[i] = 300; 3845 } 3846 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 3847 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3848 intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS, 3849 TwoViewTypesProvider.class.getName()); 3850 // Set ItemAlignment for each ViewHolder and view type, ViewHolder should 3851 // override the view type settings. 3852 intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_CLASS, 3853 PositionItemAlignmentFacetProviderForRelativeLayout1.class.getName()); 3854 intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_VIEWTYPE_CLASS, 3855 ViewTypePositionItemAlignmentFacetProviderForRelativeLayout2.class.getName()); 3856 mOrientation = BaseGridView.VERTICAL; 3857 mNumRows = 1; 3858 3859 initActivity(intent); 3860 3861 assertEquals("First view is aligned with padding top", 3862 mGridView.getPaddingTop(), mGridView.getChildAt(0).getTop()); 3863 3864 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 3865 waitForScrollIdle(mVerifyLayout); 3866 3867 final View v = mGridView.getChildAt(0); 3868 View t1 = v.findViewById(R.id.t1); 3869 int t1align = (t1.getTop() + t1.getBottom()) / 2; 3870 View t2 = v.findViewById(R.id.t2); 3871 int t2align = t2.getBottom() - 10; 3872 assertEquals("Expected alignment for 2nd textview", 3873 mGridView.getPaddingTop() - (t2align - t1align), 3874 v.getTop()); 3875 } 3876 3877 static class PositionItemAlignmentFacetProviderForRelativeLayout2 implements 3878 ItemAlignmentFacetProvider { 3879 ItemAlignmentFacet mMultipleFacet; 3880 3881 PositionItemAlignmentFacetProviderForRelativeLayout2() { 3882 mMultipleFacet = new ItemAlignmentFacet(); 3883 ItemAlignmentFacet.ItemAlignmentDef[] defs = new ItemAlignmentFacet.ItemAlignmentDef[2]; 3884 defs[0] = new ItemAlignmentFacet.ItemAlignmentDef(); 3885 defs[0].setItemAlignmentViewId(R.id.t1); 3886 defs[0].setItemAlignmentOffsetPercent(0); 3887 defs[1] = new ItemAlignmentFacet.ItemAlignmentDef(); 3888 defs[1].setItemAlignmentViewId(R.id.t2); 3889 defs[1].setItemAlignmentOffsetPercent(ItemAlignmentFacet.ITEM_ALIGN_OFFSET_PERCENT_DISABLED); 3890 defs[1].setItemAlignmentOffset(-10); 3891 mMultipleFacet.setAlignmentDefs(defs); 3892 } 3893 3894 @Override 3895 public ItemAlignmentFacet getItemAlignmentFacet(int position) { 3896 if (position == 0) { 3897 return mMultipleFacet; 3898 } else { 3899 return null; 3900 } 3901 } 3902 } 3903 3904 @Test 3905 public void testMultipleScrollPosition2() throws Throwable { 3906 Intent intent = new Intent(); 3907 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 3908 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout); 3909 intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true); 3910 int[] items = new int[100]; 3911 for (int i = 0; i < items.length; i++) { 3912 items[i] = 300; 3913 } 3914 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 3915 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3916 intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS, 3917 TwoViewTypesProvider.class.getName()); 3918 intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_CLASS, 3919 PositionItemAlignmentFacetProviderForRelativeLayout2.class.getName()); 3920 mOrientation = BaseGridView.VERTICAL; 3921 mNumRows = 1; 3922 3923 initActivity(intent); 3924 3925 assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(), 3926 mGridView.getChildAt(0).getTop()); 3927 3928 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 3929 waitForScrollIdle(mVerifyLayout); 3930 3931 final View v = mGridView.getChildAt(0); 3932 View t1 = v.findViewById(R.id.t1); 3933 int t1align = t1.getTop(); 3934 View t2 = v.findViewById(R.id.t2); 3935 int t2align = t2.getTop() - 10; 3936 assertEquals("Expected alignment for 2nd textview", 3937 mGridView.getPaddingTop() - (t2align - t1align), v.getTop()); 3938 } 3939 3940 static class ViewTypePositionItemAlignmentFacetProviderForRelativeLayout2 implements 3941 ItemAlignmentFacetProvider { 3942 ItemAlignmentFacet mMultipleFacet; 3943 3944 ViewTypePositionItemAlignmentFacetProviderForRelativeLayout2() { 3945 mMultipleFacet = new ItemAlignmentFacet(); 3946 ItemAlignmentFacet.ItemAlignmentDef[] defs = new ItemAlignmentFacet.ItemAlignmentDef[2]; 3947 defs[0] = new ItemAlignmentFacet.ItemAlignmentDef(); 3948 defs[0].setItemAlignmentViewId(R.id.t1); 3949 defs[0].setItemAlignmentOffsetPercent(0); 3950 defs[1] = new ItemAlignmentFacet.ItemAlignmentDef(); 3951 defs[1].setItemAlignmentViewId(R.id.t2); 3952 defs[1].setItemAlignmentOffsetPercent(100); 3953 defs[1].setItemAlignmentOffset(-10); 3954 mMultipleFacet.setAlignmentDefs(defs); 3955 } 3956 3957 @Override 3958 public ItemAlignmentFacet getItemAlignmentFacet(int viewType) { 3959 if (viewType == TwoViewTypesProvider.VIEW_TYPE_FIRST) { 3960 return mMultipleFacet; 3961 } else { 3962 return null; 3963 } 3964 } 3965 } 3966 3967 @Test 3968 public void testMultipleScrollPosition3() throws Throwable { 3969 Intent intent = new Intent(); 3970 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 3971 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout); 3972 intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true); 3973 int[] items = new int[100]; 3974 for (int i = 0; i < items.length; i++) { 3975 items[i] = 300; 3976 } 3977 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 3978 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 3979 intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS, 3980 TwoViewTypesProvider.class.getName()); 3981 intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_VIEWTYPE_CLASS, 3982 ViewTypePositionItemAlignmentFacetProviderForRelativeLayout2.class.getName()); 3983 mOrientation = BaseGridView.VERTICAL; 3984 mNumRows = 1; 3985 3986 initActivity(intent); 3987 3988 assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(), 3989 mGridView.getChildAt(0).getTop()); 3990 3991 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 3992 waitForScrollIdle(mVerifyLayout); 3993 3994 final View v = mGridView.getChildAt(0); 3995 View t1 = v.findViewById(R.id.t1); 3996 int t1align = t1.getTop(); 3997 View t2 = v.findViewById(R.id.t2); 3998 int t2align = t2.getBottom() - 10; 3999 assertEquals("Expected alignment for 2nd textview", 4000 mGridView.getPaddingTop() - (t2align - t1align), v.getTop()); 4001 } 4002 4003 @Test 4004 public void testSelectionAndAddItemInOneCycle() throws Throwable { 4005 Intent intent = new Intent(); 4006 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4007 R.layout.vertical_linear); 4008 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 0); 4009 initActivity(intent); 4010 mOrientation = BaseGridView.HORIZONTAL; 4011 mNumRows = 1; 4012 4013 performAndWaitForAnimation(new Runnable() { 4014 @Override 4015 public void run() { 4016 mActivity.addItems(0, new int[]{300, 300}); 4017 mGridView.setSelectedPosition(0); 4018 } 4019 }); 4020 assertEquals(0, mGridView.getSelectedPosition()); 4021 } 4022 4023 @Test 4024 public void testSelectViewTaskSmoothWithAdapterChange() throws Throwable { 4025 testSelectViewTaskWithAdapterChange(true /*smooth*/); 4026 } 4027 4028 @Test 4029 public void testSelectViewTaskWithAdapterChange() throws Throwable { 4030 testSelectViewTaskWithAdapterChange(false /*smooth*/); 4031 } 4032 4033 private void testSelectViewTaskWithAdapterChange(final boolean smooth) throws Throwable { 4034 Intent intent = new Intent(); 4035 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4036 R.layout.vertical_linear); 4037 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2); 4038 initActivity(intent); 4039 mOrientation = BaseGridView.HORIZONTAL; 4040 mNumRows = 1; 4041 4042 final View firstView = mGridView.getLayoutManager().findViewByPosition(0); 4043 final View[] selectedViewByTask = new View[1]; 4044 final ViewHolderTask task = new ViewHolderTask() { 4045 @Override 4046 public void run(RecyclerView.ViewHolder viewHolder) { 4047 selectedViewByTask[0] = viewHolder.itemView; 4048 } 4049 }; 4050 performAndWaitForAnimation(new Runnable() { 4051 @Override 4052 public void run() { 4053 mActivity.removeItems(0, 1); 4054 if (smooth) { 4055 mGridView.setSelectedPositionSmooth(0, task); 4056 } else { 4057 mGridView.setSelectedPosition(0, task); 4058 } 4059 } 4060 }); 4061 assertEquals(0, mGridView.getSelectedPosition()); 4062 assertNotNull(selectedViewByTask[0]); 4063 assertNotSame(firstView, selectedViewByTask[0]); 4064 assertSame(mGridView.getLayoutManager().findViewByPosition(0), selectedViewByTask[0]); 4065 } 4066 4067 @Test 4068 public void testNotifyItemTypeChangedSelectionEvent() throws Throwable { 4069 Intent intent = new Intent(); 4070 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4071 R.layout.vertical_linear); 4072 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 10); 4073 intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS, 4074 ChangeableViewTypesProvider.class.getName()); 4075 ChangeableViewTypesProvider.clear(); 4076 initActivity(intent); 4077 mOrientation = BaseGridView.HORIZONTAL; 4078 mNumRows = 1; 4079 4080 final ArrayList<Integer> selectedLog = new ArrayList<Integer>(); 4081 mGridView.setOnChildSelectedListener(new OnChildSelectedListener() { 4082 @Override 4083 public void onChildSelected(ViewGroup parent, View view, int position, long id) { 4084 selectedLog.add(position); 4085 } 4086 }); 4087 4088 performAndWaitForAnimation(new Runnable() { 4089 @Override 4090 public void run() { 4091 ChangeableViewTypesProvider.setViewType(0, 1); 4092 mGridView.getAdapter().notifyItemChanged(0, 1); 4093 } 4094 }); 4095 assertEquals(0, mGridView.getSelectedPosition()); 4096 assertEquals(selectedLog.size(), 1); 4097 assertEquals((int) selectedLog.get(0), 0); 4098 } 4099 4100 @Test 4101 public void testNotifyItemChangedSelectionEvent() throws Throwable { 4102 Intent intent = new Intent(); 4103 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4104 R.layout.vertical_linear); 4105 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 10); 4106 initActivity(intent); 4107 mOrientation = BaseGridView.HORIZONTAL; 4108 mNumRows = 1; 4109 4110 OnChildViewHolderSelectedListener listener = 4111 Mockito.mock(OnChildViewHolderSelectedListener.class); 4112 mGridView.setOnChildViewHolderSelectedListener(listener); 4113 4114 performAndWaitForAnimation(new Runnable() { 4115 @Override 4116 public void run() { 4117 mGridView.getAdapter().notifyItemChanged(0, 1); 4118 } 4119 }); 4120 Mockito.verify(listener, times(1)).onChildViewHolderSelected(any(RecyclerView.class), 4121 any(RecyclerView.ViewHolder.class), anyInt(), anyInt()); 4122 assertEquals(0, mGridView.getSelectedPosition()); 4123 } 4124 4125 @Test 4126 public void testSelectionSmoothAndAddItemInOneCycle() throws Throwable { 4127 Intent intent = new Intent(); 4128 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4129 R.layout.vertical_linear); 4130 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 0); 4131 initActivity(intent); 4132 mOrientation = BaseGridView.HORIZONTAL; 4133 mNumRows = 1; 4134 4135 performAndWaitForAnimation(new Runnable() { 4136 @Override 4137 public void run() { 4138 mActivity.addItems(0, new int[]{300, 300}); 4139 mGridView.setSelectedPositionSmooth(0); 4140 } 4141 }); 4142 assertEquals(0, mGridView.getSelectedPosition()); 4143 } 4144 4145 @Test 4146 public void testExtraLayoutSpace() throws Throwable { 4147 Intent intent = new Intent(); 4148 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4149 R.layout.vertical_linear); 4150 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000); 4151 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4152 initActivity(intent); 4153 4154 final int windowSize = mGridView.getHeight(); 4155 final int extraLayoutSize = windowSize; 4156 mOrientation = BaseGridView.VERTICAL; 4157 mNumRows = 1; 4158 4159 // add extra layout space 4160 startWaitLayout(); 4161 mActivityTestRule.runOnUiThread(new Runnable() { 4162 @Override 4163 public void run() { 4164 mGridView.setExtraLayoutSpace(extraLayoutSize); 4165 } 4166 }); 4167 waitForLayout(); 4168 View v; 4169 v = mGridView.getChildAt(mGridView.getChildCount() - 1); 4170 assertTrue(v.getTop() < windowSize + extraLayoutSize); 4171 assertTrue(v.getBottom() >= windowSize + extraLayoutSize - mGridView.getVerticalMargin()); 4172 4173 mGridView.setSelectedPositionSmooth(150); 4174 waitForScrollIdle(mVerifyLayout); 4175 v = mGridView.getChildAt(0); 4176 assertTrue(v.getBottom() > - extraLayoutSize); 4177 assertTrue(v.getTop() <= -extraLayoutSize + mGridView.getVerticalMargin()); 4178 4179 // clear extra layout space 4180 mActivityTestRule.runOnUiThread(new Runnable() { 4181 @Override 4182 public void run() { 4183 mGridView.setExtraLayoutSpace(0); 4184 verifyMargin(); 4185 } 4186 }); 4187 Thread.sleep(50); 4188 v = mGridView.getChildAt(mGridView.getChildCount() - 1); 4189 assertTrue(v.getTop() < windowSize); 4190 assertTrue(v.getBottom() >= windowSize - mGridView.getVerticalMargin()); 4191 } 4192 4193 @Test 4194 public void testFocusFinder() throws Throwable { 4195 Intent intent = new Intent(); 4196 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4197 R.layout.vertical_linear_with_button); 4198 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 3); 4199 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4200 initActivity(intent); 4201 mOrientation = BaseGridView.VERTICAL; 4202 mNumRows = 1; 4203 4204 // test focus from button to vertical grid view 4205 final View button = mActivity.findViewById(R.id.button); 4206 assertTrue(button.isFocused()); 4207 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 4208 assertFalse(mGridView.isFocused()); 4209 assertTrue(mGridView.hasFocus()); 4210 4211 // FocusFinder should find last focused(2nd) item on DPAD_DOWN 4212 final View secondChild = mGridView.getChildAt(1); 4213 mActivityTestRule.runOnUiThread(new Runnable() { 4214 @Override 4215 public void run() { 4216 secondChild.requestFocus(); 4217 button.requestFocus(); 4218 } 4219 }); 4220 assertTrue(button.isFocused()); 4221 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 4222 assertTrue(secondChild.isFocused()); 4223 4224 // Bug 26918143 Even VerticalGridView is not focusable, FocusFinder should find last focused 4225 // (2nd) item on DPAD_DOWN. 4226 mActivityTestRule.runOnUiThread(new Runnable() { 4227 @Override 4228 public void run() { 4229 button.requestFocus(); 4230 } 4231 }); 4232 mGridView.setFocusable(false); 4233 mGridView.setFocusableInTouchMode(false); 4234 assertTrue(button.isFocused()); 4235 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 4236 assertTrue(secondChild.isFocused()); 4237 } 4238 4239 @Test 4240 public void testRestoreIndexAndAddItems() throws Throwable { 4241 Intent intent = new Intent(); 4242 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4243 R.layout.vertical_linear); 4244 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.horizontal_item); 4245 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 4); 4246 initActivity(intent); 4247 mOrientation = BaseGridView.VERTICAL; 4248 mNumRows = 1; 4249 4250 assertEquals(mGridView.getSelectedPosition(), 0); 4251 final SparseArray<Parcelable> states = new SparseArray<>(); 4252 mActivityTestRule.runOnUiThread(new Runnable() { 4253 @Override 4254 public void run() { 4255 mGridView.saveHierarchyState(states); 4256 mGridView.setAdapter(null); 4257 } 4258 4259 }); 4260 performAndWaitForAnimation(new Runnable() { 4261 @Override 4262 public void run() { 4263 mGridView.restoreHierarchyState(states); 4264 mActivity.attachToNewAdapter(new int[0]); 4265 mActivity.addItems(0, new int[]{100, 100, 100, 100}); 4266 } 4267 4268 }); 4269 assertEquals(mGridView.getSelectedPosition(), 0); 4270 } 4271 4272 @Test 4273 public void testRestoreIndexAndAddItemsSelect1() throws Throwable { 4274 Intent intent = new Intent(); 4275 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4276 R.layout.vertical_linear); 4277 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.horizontal_item); 4278 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 4); 4279 initActivity(intent); 4280 mOrientation = BaseGridView.VERTICAL; 4281 mNumRows = 1; 4282 4283 mActivityTestRule.runOnUiThread(new Runnable() { 4284 @Override 4285 public void run() { 4286 mGridView.setSelectedPosition(1); 4287 } 4288 4289 }); 4290 assertEquals(mGridView.getSelectedPosition(), 1); 4291 final SparseArray<Parcelable> states = new SparseArray<>(); 4292 mActivityTestRule.runOnUiThread(new Runnable() { 4293 @Override 4294 public void run() { 4295 mGridView.saveHierarchyState(states); 4296 mGridView.setAdapter(null); 4297 } 4298 4299 }); 4300 performAndWaitForAnimation(new Runnable() { 4301 @Override 4302 public void run() { 4303 mGridView.restoreHierarchyState(states); 4304 mActivity.attachToNewAdapter(new int[0]); 4305 mActivity.addItems(0, new int[]{100, 100, 100, 100}); 4306 } 4307 4308 }); 4309 assertEquals(mGridView.getSelectedPosition(), 1); 4310 } 4311 4312 @Test 4313 public void testRestoreStateAfterAdapterChange() throws Throwable { 4314 Intent intent = new Intent(); 4315 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4316 R.layout.vertical_linear); 4317 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.selectable_text_view); 4318 intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{50, 50, 50, 50}); 4319 initActivity(intent); 4320 mOrientation = BaseGridView.VERTICAL; 4321 mNumRows = 1; 4322 4323 mActivityTestRule.runOnUiThread(new Runnable() { 4324 @Override 4325 public void run() { 4326 mGridView.setSelectedPosition(1); 4327 mGridView.setSaveChildrenPolicy(VerticalGridView.SAVE_ALL_CHILD); 4328 } 4329 4330 }); 4331 assertEquals(mGridView.getSelectedPosition(), 1); 4332 final SparseArray<Parcelable> states = new SparseArray<>(); 4333 mActivityTestRule.runOnUiThread(new Runnable() { 4334 @Override 4335 public void run() { 4336 Selection.setSelection((Spannable) (((TextView) mGridView.getChildAt(0)) 4337 .getText()), 1, 2); 4338 Selection.setSelection((Spannable) (((TextView) mGridView.getChildAt(1)) 4339 .getText()), 0, 1); 4340 mGridView.saveHierarchyState(states); 4341 mGridView.setAdapter(null); 4342 } 4343 4344 }); 4345 performAndWaitForAnimation(new Runnable() { 4346 @Override 4347 public void run() { 4348 mGridView.restoreHierarchyState(states); 4349 mActivity.attachToNewAdapter(new int[]{50, 50, 50, 50}); 4350 } 4351 4352 }); 4353 assertEquals(mGridView.getSelectedPosition(), 1); 4354 assertEquals(1, ((TextView) mGridView.getChildAt(0)).getSelectionStart()); 4355 assertEquals(2, ((TextView) mGridView.getChildAt(0)).getSelectionEnd()); 4356 assertEquals(0, ((TextView) mGridView.getChildAt(1)).getSelectionStart()); 4357 assertEquals(1, ((TextView) mGridView.getChildAt(1)).getSelectionEnd()); 4358 } 4359 4360 @Test 4361 public void test27766012() throws Throwable { 4362 Intent intent = new Intent(); 4363 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4364 R.layout.vertical_linear_with_button_onleft); 4365 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.horizontal_item); 4366 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2); 4367 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4368 intent.putExtra(GridActivity.EXTRA_UPDATE_SIZE, false); 4369 initActivity(intent); 4370 mOrientation = BaseGridView.VERTICAL; 4371 mNumRows = 1; 4372 4373 // set remove animator two seconds 4374 mGridView.getItemAnimator().setRemoveDuration(2000); 4375 final View view = mGridView.getChildAt(1); 4376 mActivityTestRule.runOnUiThread(new Runnable() { 4377 @Override 4378 public void run() { 4379 view.requestFocus(); 4380 } 4381 }); 4382 assertTrue(view.hasFocus()); 4383 mActivityTestRule.runOnUiThread(new Runnable() { 4384 @Override 4385 public void run() { 4386 mActivity.removeItems(0, 2); 4387 } 4388 4389 }); 4390 // wait one second, removing second view is still attached to parent 4391 Thread.sleep(1000); 4392 assertSame(view.getParent(), mGridView); 4393 mActivityTestRule.runOnUiThread(new Runnable() { 4394 @Override 4395 public void run() { 4396 // refocus to the removed item and do a focus search. 4397 view.requestFocus(); 4398 view.focusSearch(View.FOCUS_UP); 4399 } 4400 4401 }); 4402 } 4403 4404 @Test 4405 public void testBug27258366() throws Throwable { 4406 Intent intent = new Intent(); 4407 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4408 R.layout.vertical_linear_with_button_onleft); 4409 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.horizontal_item); 4410 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 10); 4411 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4412 intent.putExtra(GridActivity.EXTRA_UPDATE_SIZE, false); 4413 initActivity(intent); 4414 mOrientation = BaseGridView.VERTICAL; 4415 mNumRows = 1; 4416 4417 // move item1 500 pixels right, when focus is on item1, default focus finder will pick 4418 // item0 and item2 for the best match of focusSearch(FOCUS_LEFT). The grid widget 4419 // must override default addFocusables(), not to add item0 or item2. 4420 mActivity.mAdapterListener = new GridActivity.AdapterListener() { 4421 @Override 4422 public void onBind(RecyclerView.ViewHolder vh, int position) { 4423 if (position == 1) { 4424 vh.itemView.setPaddingRelative(500, 0, 0, 0); 4425 } else { 4426 vh.itemView.setPaddingRelative(0, 0, 0, 0); 4427 } 4428 } 4429 }; 4430 mActivityTestRule.runOnUiThread(new Runnable() { 4431 @Override 4432 public void run() { 4433 mGridView.getAdapter().notifyDataSetChanged(); 4434 } 4435 }); 4436 Thread.sleep(100); 4437 4438 final ViewGroup secondChild = (ViewGroup) mGridView.getChildAt(1); 4439 mActivityTestRule.runOnUiThread(new Runnable() { 4440 @Override 4441 public void run() { 4442 secondChild.requestFocus(); 4443 } 4444 }); 4445 sendKey(KeyEvent.KEYCODE_DPAD_LEFT); 4446 Thread.sleep(100); 4447 final View button = mActivity.findViewById(R.id.button); 4448 assertTrue(button.isFocused()); 4449 } 4450 4451 @Test 4452 public void testUpdateHeightScrollHorizontal() throws Throwable { 4453 Intent intent = new Intent(); 4454 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4455 R.layout.horizontal_linear); 4456 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 30); 4457 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4458 intent.putExtra(GridActivity.EXTRA_REQUEST_LAYOUT_ONFOCUS, true); 4459 intent.putExtra(GridActivity.EXTRA_UPDATE_SIZE, false); 4460 intent.putExtra(GridActivity.EXTRA_UPDATE_SIZE_SECONDARY, true); 4461 initActivity(intent); 4462 mOrientation = BaseGridView.HORIZONTAL; 4463 mNumRows = 1; 4464 4465 final int childTop = mGridView.getChildAt(0).getTop(); 4466 // scroll to end, all children's top should not change. 4467 scrollToEnd(new Runnable() { 4468 @Override 4469 public void run() { 4470 for (int i = 0; i < mGridView.getChildCount(); i++) { 4471 assertEquals(childTop, mGridView.getChildAt(i).getTop()); 4472 } 4473 } 4474 }); 4475 // sanity check last child has focus with a larger height. 4476 assertTrue(mGridView.getChildAt(0).getHeight() 4477 < mGridView.getChildAt(mGridView.getChildCount() - 1).getHeight()); 4478 } 4479 4480 @Test 4481 public void testUpdateWidthScrollHorizontal() throws Throwable { 4482 Intent intent = new Intent(); 4483 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4484 R.layout.horizontal_linear); 4485 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 30); 4486 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4487 intent.putExtra(GridActivity.EXTRA_REQUEST_LAYOUT_ONFOCUS, true); 4488 intent.putExtra(GridActivity.EXTRA_UPDATE_SIZE, true); 4489 intent.putExtra(GridActivity.EXTRA_UPDATE_SIZE_SECONDARY, false); 4490 initActivity(intent); 4491 mOrientation = BaseGridView.HORIZONTAL; 4492 mNumRows = 1; 4493 4494 final int childTop = mGridView.getChildAt(0).getTop(); 4495 // scroll to end, all children's top should not change. 4496 scrollToEnd(new Runnable() { 4497 @Override 4498 public void run() { 4499 for (int i = 0; i < mGridView.getChildCount(); i++) { 4500 assertEquals(childTop, mGridView.getChildAt(i).getTop()); 4501 } 4502 } 4503 }); 4504 // sanity check last child has focus with a larger width. 4505 assertTrue(mGridView.getChildAt(0).getWidth() 4506 < mGridView.getChildAt(mGridView.getChildCount() - 1).getWidth()); 4507 if (mGridView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { 4508 assertEquals(mGridView.getPaddingLeft(), 4509 mGridView.getChildAt(mGridView.getChildCount() - 1).getLeft()); 4510 } else { 4511 assertEquals(mGridView.getWidth() - mGridView.getPaddingRight(), 4512 mGridView.getChildAt(mGridView.getChildCount() - 1).getRight()); 4513 } 4514 } 4515 4516 @Test 4517 public void testUpdateWidthScrollHorizontalRtl() throws Throwable { 4518 Intent intent = new Intent(); 4519 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4520 R.layout.horizontal_linear_rtl); 4521 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 30); 4522 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4523 intent.putExtra(GridActivity.EXTRA_REQUEST_LAYOUT_ONFOCUS, true); 4524 intent.putExtra(GridActivity.EXTRA_UPDATE_SIZE, true); 4525 intent.putExtra(GridActivity.EXTRA_UPDATE_SIZE_SECONDARY, false); 4526 initActivity(intent); 4527 mOrientation = BaseGridView.HORIZONTAL; 4528 mNumRows = 1; 4529 4530 final int childTop = mGridView.getChildAt(0).getTop(); 4531 // scroll to end, all children's top should not change. 4532 scrollToEnd(new Runnable() { 4533 @Override 4534 public void run() { 4535 for (int i = 0; i < mGridView.getChildCount(); i++) { 4536 assertEquals(childTop, mGridView.getChildAt(i).getTop()); 4537 } 4538 } 4539 }); 4540 // sanity check last child has focus with a larger width. 4541 assertTrue(mGridView.getChildAt(0).getWidth() 4542 < mGridView.getChildAt(mGridView.getChildCount() - 1).getWidth()); 4543 assertEquals(mGridView.getPaddingLeft(), 4544 mGridView.getChildAt(mGridView.getChildCount() - 1).getLeft()); 4545 } 4546 4547 @Test 4548 public void testAccessibility() throws Throwable { 4549 Intent intent = new Intent(); 4550 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 4551 R.layout.vertical_linear); 4552 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000); 4553 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4554 initActivity(intent); 4555 mOrientation = BaseGridView.VERTICAL; 4556 mNumRows = 1; 4557 4558 assertTrue(0 == mGridView.getSelectedPosition()); 4559 4560 final RecyclerViewAccessibilityDelegate delegateCompat = mGridView 4561 .getCompatAccessibilityDelegate(); 4562 final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(); 4563 mActivityTestRule.runOnUiThread(new Runnable() { 4564 @Override 4565 public void run() { 4566 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info); 4567 } 4568 }); 4569 assertTrue("test sanity", info.isScrollable()); 4570 mActivityTestRule.runOnUiThread(new Runnable() { 4571 @Override 4572 public void run() { 4573 delegateCompat.performAccessibilityAction(mGridView, 4574 AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null); 4575 } 4576 }); 4577 waitForScrollIdle(mVerifyLayout); 4578 int selectedPosition1 = mGridView.getSelectedPosition(); 4579 assertTrue(0 < selectedPosition1); 4580 4581 mActivityTestRule.runOnUiThread(new Runnable() { 4582 @Override 4583 public void run() { 4584 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info); 4585 } 4586 }); 4587 assertTrue("test sanity", info.isScrollable()); 4588 mActivityTestRule.runOnUiThread(new Runnable() { 4589 @Override 4590 public void run() { 4591 delegateCompat.performAccessibilityAction(mGridView, 4592 AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, null); 4593 } 4594 }); 4595 waitForScrollIdle(mVerifyLayout); 4596 int selectedPosition2 = mGridView.getSelectedPosition(); 4597 assertTrue(selectedPosition2 < selectedPosition1); 4598 } 4599 4600 @Test 4601 public void testAccessibilityScrollForwardHalfVisible() throws Throwable { 4602 Intent intent = new Intent(); 4603 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 4604 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.item_button_at_bottom); 4605 intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{}); 4606 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4607 initActivity(intent); 4608 mOrientation = BaseGridView.VERTICAL; 4609 mNumRows = 1; 4610 4611 int height = mGridView.getHeight() - mGridView.getPaddingTop() 4612 - mGridView.getPaddingBottom(); 4613 final int childHeight = height - mGridView.getVerticalSpacing() - 100; 4614 mActivityTestRule.runOnUiThread(new Runnable() { 4615 @Override 4616 public void run() { 4617 mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE); 4618 mGridView.setWindowAlignmentOffset(100); 4619 mGridView.setWindowAlignmentOffsetPercent(BaseGridView 4620 .WINDOW_ALIGN_OFFSET_PERCENT_DISABLED); 4621 mGridView.setItemAlignmentOffset(0); 4622 mGridView.setItemAlignmentOffsetPercent(BaseGridView 4623 .ITEM_ALIGN_OFFSET_PERCENT_DISABLED); 4624 } 4625 }); 4626 mActivity.addItems(0, new int[]{childHeight, childHeight}); 4627 waitForItemAnimation(); 4628 setSelectedPosition(0); 4629 4630 final RecyclerViewAccessibilityDelegate delegateCompat = mGridView 4631 .getCompatAccessibilityDelegate(); 4632 final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(); 4633 mActivityTestRule.runOnUiThread(new Runnable() { 4634 @Override 4635 public void run() { 4636 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info); 4637 } 4638 }); 4639 assertTrue("test sanity", info.isScrollable()); 4640 mActivityTestRule.runOnUiThread(new Runnable() { 4641 @Override 4642 public void run() { 4643 delegateCompat.performAccessibilityAction(mGridView, 4644 AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null); 4645 } 4646 }); 4647 waitForScrollIdle(mVerifyLayout); 4648 assertEquals(1, mGridView.getSelectedPosition()); 4649 } 4650 4651 @Test 4652 public void testAccessibilityBug77292190() throws Throwable { 4653 Intent intent = new Intent(); 4654 final int numItems = 1000; 4655 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 4656 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.item_full_width); 4657 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000); 4658 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4659 initActivity(intent); 4660 mOrientation = BaseGridView.VERTICAL; 4661 mNumRows = 1; 4662 4663 setSelectedPosition(0); 4664 4665 final RecyclerViewAccessibilityDelegate delegateCompat = mGridView 4666 .getCompatAccessibilityDelegate(); 4667 final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(); 4668 mActivityTestRule.runOnUiThread(new Runnable() { 4669 @Override 4670 public void run() { 4671 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info); 4672 } 4673 }); 4674 if (Build.VERSION.SDK_INT >= 21) { 4675 assertFalse(hasAction(info, 4676 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP)); 4677 assertTrue(hasAction(info, 4678 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN)); 4679 } else { 4680 assertFalse(hasAction(info, AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD)); 4681 assertTrue(hasAction(info, AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD)); 4682 } 4683 4684 setSelectedPosition(numItems - 1); 4685 final AccessibilityNodeInfoCompat info2 = AccessibilityNodeInfoCompat.obtain(); 4686 mActivityTestRule.runOnUiThread(new Runnable() { 4687 @Override 4688 public void run() { 4689 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info2); 4690 } 4691 }); 4692 if (Build.VERSION.SDK_INT >= 21) { 4693 assertTrue(hasAction(info2, 4694 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP)); 4695 assertFalse(hasAction(info2, 4696 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN)); 4697 } else { 4698 assertTrue(hasAction(info2, AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD)); 4699 assertFalse(hasAction(info2, AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD)); 4700 } 4701 } 4702 4703 @Test 4704 public void testAccessibilityWhenScrollDisabled() throws Throwable { 4705 Intent intent = new Intent(); 4706 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 4707 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.item_button_at_bottom); 4708 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000); 4709 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4710 initActivity(intent); 4711 mOrientation = BaseGridView.VERTICAL; 4712 mNumRows = 1; 4713 4714 setSelectedPosition(0); 4715 4716 final RecyclerViewAccessibilityDelegate delegateCompat = mGridView 4717 .getCompatAccessibilityDelegate(); 4718 final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(); 4719 mActivityTestRule.runOnUiThread(new Runnable() { 4720 @Override 4721 public void run() { 4722 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info); 4723 } 4724 }); 4725 mGridView.setScrollEnabled(false); 4726 mActivityTestRule.runOnUiThread(new Runnable() { 4727 @Override 4728 public void run() { 4729 for (int i = 0; i < 100; i++) { 4730 assertTrue(delegateCompat.performAccessibilityAction(mGridView, 4731 AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null)); 4732 } 4733 } 4734 }); 4735 assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState()); 4736 } 4737 private boolean hasAction(AccessibilityNodeInfoCompat info, Object action) { 4738 if (Build.VERSION.SDK_INT >= 21) { 4739 AccessibilityNodeInfoCompat.AccessibilityActionCompat convertedAction = 4740 (AccessibilityNodeInfoCompat.AccessibilityActionCompat) action; 4741 List<AccessibilityNodeInfoCompat.AccessibilityActionCompat> actions = 4742 info.getActionList(); 4743 for (int i = 0; i < actions.size(); i++) { 4744 if (actions.get(i).getId() == convertedAction.getId()) { 4745 return true; 4746 } 4747 } 4748 return false; 4749 } else { 4750 int convertedAction = (int) action; 4751 return ((info.getActions() & convertedAction) != 0); 4752 } 4753 } 4754 4755 private void setUpActivityForScrollingTest(final boolean isRTL, boolean isHorizontal, 4756 int numChildViews, boolean isSiblingViewVisible) throws Throwable { 4757 Intent intent = new Intent(); 4758 int layout; 4759 if (isHorizontal) { 4760 layout = isRTL ? R.layout.horizontal_linear_rtl : R.layout.horizontal_linear; 4761 } else { 4762 layout = R.layout.vertical_linear; 4763 } 4764 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, layout); 4765 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.item_button_at_bottom); 4766 intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{}); 4767 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4768 initActivity(intent); 4769 mOrientation = isHorizontal ? BaseGridView.HORIZONTAL : BaseGridView.VERTICAL; 4770 mNumRows = 1; 4771 4772 final int offset = (isSiblingViewVisible ? 2 : 1) * (isHorizontal 4773 ? mGridView.getHorizontalSpacing() : mGridView.getVerticalSpacing()); 4774 final int childSize = (isHorizontal ? mGridView.getWidth() : mGridView.getHeight()) 4775 - offset - (isHorizontal ? 2 * mGridView.getHorizontalSpacing() : 4776 mGridView.getVerticalSpacing()); 4777 mActivityTestRule.runOnUiThread(new Runnable() { 4778 @Override 4779 public void run() { 4780 if (isRTL) { 4781 mGridView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL); 4782 } 4783 mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE); 4784 mGridView.setWindowAlignmentOffset(offset); 4785 mGridView.setWindowAlignmentOffsetPercent(BaseGridView 4786 .WINDOW_ALIGN_OFFSET_PERCENT_DISABLED); 4787 mGridView.setItemAlignmentOffset(0); 4788 mGridView.setItemAlignmentOffsetPercent(BaseGridView 4789 .ITEM_ALIGN_OFFSET_PERCENT_DISABLED); 4790 } 4791 }); 4792 int[] widthArrays = new int[numChildViews]; 4793 Arrays.fill(widthArrays, childSize); 4794 mActivity.addItems(0, widthArrays); 4795 } 4796 4797 private void testScrollingAction(boolean isRTL, boolean isHorizontal) throws Throwable { 4798 waitForItemAnimation(); 4799 setSelectedPosition(1); 4800 final RecyclerViewAccessibilityDelegate delegateCompat = mGridView 4801 .getCompatAccessibilityDelegate(); 4802 final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(); 4803 mActivityTestRule.runOnUiThread(new Runnable() { 4804 @Override 4805 public void run() { 4806 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info); 4807 } 4808 }); 4809 // We are currently focusing on item 1, calculating the direction to get me to item 0 4810 final AccessibilityNodeInfoCompat.AccessibilityActionCompat itemZeroDirection; 4811 if (isHorizontal) { 4812 itemZeroDirection = isRTL 4813 ? AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_RIGHT : 4814 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_LEFT; 4815 } else { 4816 itemZeroDirection = 4817 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP; 4818 } 4819 final int translatedItemZeroDirection = AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD; 4820 4821 assertTrue("test sanity", info.isScrollable()); 4822 if (Build.VERSION.SDK_INT >= 23) { 4823 assertTrue("test sanity", hasAction(info, itemZeroDirection)); 4824 } else { 4825 assertTrue("test sanity", hasAction(info, translatedItemZeroDirection)); 4826 } 4827 4828 mActivityTestRule.runOnUiThread(new Runnable() { 4829 @Override 4830 public void run() { 4831 if (Build.VERSION.SDK_INT >= 23) { 4832 delegateCompat.performAccessibilityAction(mGridView, itemZeroDirection.getId(), 4833 null); 4834 } else { 4835 delegateCompat.performAccessibilityAction(mGridView, 4836 translatedItemZeroDirection, null); 4837 } 4838 } 4839 }); 4840 waitForScrollIdle(mVerifyLayout); 4841 assertEquals(0, mGridView.getSelectedPosition()); 4842 setSelectedPosition(0); 4843 // We are at item 0, calculate the direction that lead us to the item 1 4844 final AccessibilityNodeInfoCompat.AccessibilityActionCompat itemOneDirection; 4845 if (isHorizontal) { 4846 itemOneDirection = isRTL 4847 ? AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_LEFT 4848 : AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_RIGHT; 4849 } else { 4850 itemOneDirection = 4851 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN; 4852 } 4853 final int translatedItemOneDirection = AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD; 4854 4855 mActivityTestRule.runOnUiThread(new Runnable() { 4856 @Override 4857 public void run() { 4858 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info); 4859 } 4860 }); 4861 if (Build.VERSION.SDK_INT >= 23) { 4862 assertTrue("test sanity", hasAction(info, itemOneDirection)); 4863 } else { 4864 assertTrue("test sanity", hasAction(info, translatedItemOneDirection)); 4865 } 4866 mActivityTestRule.runOnUiThread(new Runnable() { 4867 @Override 4868 public void run() { 4869 if (Build.VERSION.SDK_INT >= 23) { 4870 delegateCompat.performAccessibilityAction(mGridView, itemOneDirection.getId(), 4871 null); 4872 } else { 4873 delegateCompat.performAccessibilityAction(mGridView, translatedItemOneDirection, 4874 null); 4875 } 4876 } 4877 }); 4878 waitForScrollIdle(mVerifyLayout); 4879 assertEquals(1, mGridView.getSelectedPosition()); 4880 } 4881 4882 @Test 4883 public void testAccessibilityRespondToLeftRightInvisible() throws Throwable { 4884 boolean isRTL = false; 4885 boolean isHorizontal = true; 4886 setUpActivityForScrollingTest(isRTL, isHorizontal, 2 /* numChild */, 4887 false /* next child partially visible */); 4888 testScrollingAction(isRTL, isHorizontal); 4889 } 4890 4891 @Test 4892 public void testAccessibilityRespondToLeftRightPartiallyVisible() throws Throwable { 4893 boolean isRTL = false; 4894 boolean isHorizontal = true; 4895 setUpActivityForScrollingTest(isRTL, isHorizontal, 2 /* numChild */, 4896 true /* next child partially visible */); 4897 testScrollingAction(isRTL, isHorizontal); 4898 } 4899 4900 @Test 4901 public void testAccessibilityRespondToLeftRightRtlInvisible() 4902 throws Throwable { 4903 boolean isRTL = true; 4904 boolean isHorizontal = true; 4905 setUpActivityForScrollingTest(isRTL, isHorizontal, 2 /* numChild */, 4906 false /* next child partially visible */); 4907 testScrollingAction(isRTL, isHorizontal); 4908 } 4909 4910 @Test 4911 public void testAccessibilityRespondToLeftRightRtlPartiallyVisible() throws Throwable { 4912 boolean isRTL = true; 4913 boolean isHorizontal = true; 4914 setUpActivityForScrollingTest(isRTL, isHorizontal, 2 /* numChild */, 4915 true /* next child partially visible */); 4916 testScrollingAction(isRTL, isHorizontal); 4917 } 4918 4919 @Test 4920 public void testAccessibilityRespondToScrollUpDownActionInvisible() throws Throwable { 4921 boolean isRTL = false; 4922 boolean isHorizontal = false; 4923 setUpActivityForScrollingTest(isRTL, isHorizontal, 2 /* numChild */, 4924 false /* next child partially visible */); 4925 testScrollingAction(isRTL, isHorizontal); 4926 } 4927 4928 @Test 4929 public void testAccessibilityRespondToScrollUpDownActionPartiallyVisible() throws Throwable { 4930 boolean isRTL = false; 4931 boolean isHorizontal = false; 4932 setUpActivityForScrollingTest(isRTL, isHorizontal, 2 /* numChild */, 4933 true /* next child partially visible */); 4934 testScrollingAction(isRTL, isHorizontal); 4935 } 4936 4937 @Test 4938 public void testAccessibilityScrollBackwardHalfVisible() throws Throwable { 4939 Intent intent = new Intent(); 4940 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 4941 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.item_button_at_top); 4942 intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{}); 4943 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 4944 initActivity(intent); 4945 mOrientation = BaseGridView.VERTICAL; 4946 mNumRows = 1; 4947 4948 int height = mGridView.getHeight() - mGridView.getPaddingTop() 4949 - mGridView.getPaddingBottom(); 4950 final int childHeight = height - mGridView.getVerticalSpacing() - 100; 4951 mActivityTestRule.runOnUiThread(new Runnable() { 4952 @Override 4953 public void run() { 4954 mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE); 4955 mGridView.setWindowAlignmentOffset(100); 4956 mGridView.setWindowAlignmentOffsetPercent(BaseGridView 4957 .WINDOW_ALIGN_OFFSET_PERCENT_DISABLED); 4958 mGridView.setItemAlignmentOffset(0); 4959 mGridView.setItemAlignmentOffsetPercent(BaseGridView 4960 .ITEM_ALIGN_OFFSET_PERCENT_DISABLED); 4961 } 4962 }); 4963 mActivity.addItems(0, new int[]{childHeight, childHeight}); 4964 waitForItemAnimation(); 4965 setSelectedPosition(1); 4966 4967 final RecyclerViewAccessibilityDelegate delegateCompat = mGridView 4968 .getCompatAccessibilityDelegate(); 4969 final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(); 4970 mActivityTestRule.runOnUiThread(new Runnable() { 4971 @Override 4972 public void run() { 4973 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info); 4974 } 4975 }); 4976 assertTrue("test sanity", info.isScrollable()); 4977 mActivityTestRule.runOnUiThread(new Runnable() { 4978 @Override 4979 public void run() { 4980 delegateCompat.performAccessibilityAction(mGridView, 4981 AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, null); 4982 } 4983 }); 4984 waitForScrollIdle(mVerifyLayout); 4985 assertEquals(0, mGridView.getSelectedPosition()); 4986 } 4987 4988 void slideInAndWaitIdle() throws Throwable { 4989 slideInAndWaitIdle(5000); 4990 } 4991 4992 void slideInAndWaitIdle(long timeout) throws Throwable { 4993 // animateIn() would reset position 4994 mActivityTestRule.runOnUiThread(new Runnable() { 4995 @Override 4996 public void run() { 4997 mGridView.animateIn(); 4998 } 4999 }); 5000 PollingCheck.waitFor(timeout, new PollingCheck.PollingCheckCondition() { 5001 @Override 5002 public boolean canProceed() { 5003 return !mGridView.getLayoutManager().isSmoothScrolling() 5004 && mGridView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE; 5005 } 5006 }); 5007 } 5008 5009 @Test 5010 public void testAnimateOutBlockScrollTo() throws Throwable { 5011 Intent intent = new Intent(); 5012 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 5013 R.layout.vertical_linear_with_button_onleft); 5014 int[] items = new int[100]; 5015 for (int i = 0; i < items.length; i++) { 5016 items[i] = 300; 5017 } 5018 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5019 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5020 mOrientation = BaseGridView.VERTICAL; 5021 mNumRows = 1; 5022 5023 initActivity(intent); 5024 5025 assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(), 5026 mGridView.getChildAt(0).getTop()); 5027 5028 mActivityTestRule.runOnUiThread(new Runnable() { 5029 @Override 5030 public void run() { 5031 mGridView.animateOut(); 5032 } 5033 }); 5034 // wait until sliding out. 5035 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 5036 @Override 5037 public boolean canProceed() { 5038 return mGridView.getChildAt(0).getTop() > mGridView.getPaddingTop(); 5039 } 5040 }); 5041 // scrollToPosition() should not affect slideOut status 5042 mActivityTestRule.runOnUiThread(new Runnable() { 5043 @Override 5044 public void run() { 5045 mGridView.scrollToPosition(0); 5046 } 5047 }); 5048 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 5049 @Override 5050 public boolean canProceed() { 5051 return mGridView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE; 5052 } 5053 }); 5054 assertTrue("First view slided Out", mGridView.getChildAt(0).getTop() 5055 >= mGridView.getHeight()); 5056 5057 slideInAndWaitIdle(); 5058 assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(), 5059 mGridView.getChildAt(0).getTop()); 5060 } 5061 5062 @Test 5063 public void testAnimateOutBlockSmoothScrolling() throws Throwable { 5064 Intent intent = new Intent(); 5065 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 5066 R.layout.vertical_linear_with_button_onleft); 5067 int[] items = new int[30]; 5068 for (int i = 0; i < items.length; i++) { 5069 items[i] = 300; 5070 } 5071 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5072 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5073 mOrientation = BaseGridView.VERTICAL; 5074 mNumRows = 1; 5075 5076 initActivity(intent); 5077 5078 assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(), 5079 mGridView.getChildAt(0).getTop()); 5080 5081 mActivityTestRule.runOnUiThread(new Runnable() { 5082 @Override 5083 public void run() { 5084 mGridView.animateOut(); 5085 } 5086 }); 5087 // wait until sliding out. 5088 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 5089 @Override 5090 public boolean canProceed() { 5091 return mGridView.getChildAt(0).getTop() > mGridView.getPaddingTop(); 5092 } 5093 }); 5094 // smoothScrollToPosition() should not affect slideOut status 5095 mActivityTestRule.runOnUiThread(new Runnable() { 5096 @Override 5097 public void run() { 5098 mGridView.smoothScrollToPosition(29); 5099 } 5100 }); 5101 PollingCheck.waitFor(10000, new PollingCheck.PollingCheckCondition() { 5102 @Override 5103 public boolean canProceed() { 5104 return mGridView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE; 5105 } 5106 }); 5107 assertTrue("First view slided Out", mGridView.getChildAt(0).getTop() 5108 >= mGridView.getHeight()); 5109 5110 slideInAndWaitIdle(); 5111 View lastChild = mGridView.getChildAt(mGridView.getChildCount() - 1); 5112 assertSame("Scrolled to last child", 5113 mGridView.findViewHolderForAdapterPosition(29).itemView, lastChild); 5114 } 5115 5116 @Test 5117 public void testAnimateOutBlockLongScrollTo() throws Throwable { 5118 Intent intent = new Intent(); 5119 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 5120 R.layout.vertical_linear_with_button_onleft); 5121 int[] items = new int[30]; 5122 for (int i = 0; i < items.length; i++) { 5123 items[i] = 300; 5124 } 5125 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5126 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5127 mOrientation = BaseGridView.VERTICAL; 5128 mNumRows = 1; 5129 5130 initActivity(intent); 5131 5132 assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(), 5133 mGridView.getChildAt(0).getTop()); 5134 5135 mActivityTestRule.runOnUiThread(new Runnable() { 5136 @Override 5137 public void run() { 5138 mGridView.animateOut(); 5139 } 5140 }); 5141 // wait until sliding out. 5142 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 5143 @Override 5144 public boolean canProceed() { 5145 return mGridView.getChildAt(0).getTop() > mGridView.getPaddingTop(); 5146 } 5147 }); 5148 // smoothScrollToPosition() should not affect slideOut status 5149 mActivityTestRule.runOnUiThread(new Runnable() { 5150 @Override 5151 public void run() { 5152 mGridView.scrollToPosition(29); 5153 } 5154 }); 5155 PollingCheck.waitFor(10000, new PollingCheck.PollingCheckCondition() { 5156 @Override 5157 public boolean canProceed() { 5158 return mGridView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE; 5159 } 5160 }); 5161 assertTrue("First view slided Out", mGridView.getChildAt(0).getTop() 5162 >= mGridView.getHeight()); 5163 5164 slideInAndWaitIdle(); 5165 View lastChild = mGridView.getChildAt(mGridView.getChildCount() - 1); 5166 assertSame("Scrolled to last child", 5167 mGridView.findViewHolderForAdapterPosition(29).itemView, lastChild); 5168 } 5169 5170 @Test 5171 public void testAnimateOutBlockLayout() throws Throwable { 5172 Intent intent = new Intent(); 5173 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 5174 R.layout.vertical_linear_with_button_onleft); 5175 int[] items = new int[100]; 5176 for (int i = 0; i < items.length; i++) { 5177 items[i] = 300; 5178 } 5179 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5180 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5181 mOrientation = BaseGridView.VERTICAL; 5182 mNumRows = 1; 5183 5184 initActivity(intent); 5185 5186 assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(), 5187 mGridView.getChildAt(0).getTop()); 5188 5189 mActivityTestRule.runOnUiThread(new Runnable() { 5190 @Override 5191 public void run() { 5192 mGridView.animateOut(); 5193 } 5194 }); 5195 // wait until sliding out. 5196 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 5197 @Override 5198 public boolean canProceed() { 5199 return mGridView.getChildAt(0).getTop() > mGridView.getPaddingTop(); 5200 } 5201 }); 5202 // change adapter should not affect slideOut status 5203 mActivityTestRule.runOnUiThread(new Runnable() { 5204 @Override 5205 public void run() { 5206 mActivity.changeItem(0, 200); 5207 } 5208 }); 5209 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 5210 @Override 5211 public boolean canProceed() { 5212 return mGridView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE; 5213 } 5214 }); 5215 assertTrue("First view slided Out", mGridView.getChildAt(0).getTop() 5216 >= mGridView.getHeight()); 5217 assertEquals("onLayout suppressed during slide out", 300, 5218 mGridView.getChildAt(0).getHeight()); 5219 5220 slideInAndWaitIdle(); 5221 assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(), 5222 mGridView.getChildAt(0).getTop()); 5223 // size of item should be updated immediately after slide in animation finishes: 5224 PollingCheck.waitFor(1000, new PollingCheck.PollingCheckCondition() { 5225 @Override 5226 public boolean canProceed() { 5227 return 200 == mGridView.getChildAt(0).getHeight(); 5228 } 5229 }); 5230 } 5231 5232 @Test 5233 public void testAnimateOutBlockFocusChange() throws Throwable { 5234 Intent intent = new Intent(); 5235 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 5236 R.layout.vertical_linear_with_button_onleft); 5237 int[] items = new int[100]; 5238 for (int i = 0; i < items.length; i++) { 5239 items[i] = 300; 5240 } 5241 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5242 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5243 mOrientation = BaseGridView.VERTICAL; 5244 mNumRows = 1; 5245 5246 initActivity(intent); 5247 5248 assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(), 5249 mGridView.getChildAt(0).getTop()); 5250 5251 mActivityTestRule.runOnUiThread(new Runnable() { 5252 @Override 5253 public void run() { 5254 mGridView.animateOut(); 5255 mActivity.findViewById(R.id.button).requestFocus(); 5256 } 5257 }); 5258 assertTrue(mActivity.findViewById(R.id.button).hasFocus()); 5259 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 5260 @Override 5261 public boolean canProceed() { 5262 return mGridView.getChildAt(0).getTop() > mGridView.getPaddingTop(); 5263 } 5264 }); 5265 mActivityTestRule.runOnUiThread(new Runnable() { 5266 @Override 5267 public void run() { 5268 mGridView.requestFocus(); 5269 } 5270 }); 5271 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 5272 @Override 5273 public boolean canProceed() { 5274 return mGridView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE; 5275 } 5276 }); 5277 assertTrue("First view slided Out", mGridView.getChildAt(0).getTop() 5278 >= mGridView.getHeight()); 5279 5280 slideInAndWaitIdle(); 5281 assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(), 5282 mGridView.getChildAt(0).getTop()); 5283 } 5284 5285 @Test 5286 public void testHorizontalAnimateOutBlockScrollTo() throws Throwable { 5287 Intent intent = new Intent(); 5288 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 5289 R.layout.horizontal_linear); 5290 int[] items = new int[100]; 5291 for (int i = 0; i < items.length; i++) { 5292 items[i] = 300; 5293 } 5294 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5295 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5296 mOrientation = BaseGridView.HORIZONTAL; 5297 mNumRows = 1; 5298 5299 initActivity(intent); 5300 5301 assertEquals("First view is aligned with padding left", mGridView.getPaddingLeft(), 5302 mGridView.getChildAt(0).getLeft()); 5303 5304 mActivityTestRule.runOnUiThread(new Runnable() { 5305 @Override 5306 public void run() { 5307 mGridView.animateOut(); 5308 } 5309 }); 5310 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 5311 @Override 5312 public boolean canProceed() { 5313 return mGridView.getChildAt(0).getLeft() > mGridView.getPaddingLeft(); 5314 } 5315 }); 5316 mActivityTestRule.runOnUiThread(new Runnable() { 5317 @Override 5318 public void run() { 5319 mGridView.scrollToPosition(0); 5320 } 5321 }); 5322 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 5323 @Override 5324 public boolean canProceed() { 5325 return mGridView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE; 5326 } 5327 }); 5328 5329 assertTrue("First view is slided out", mGridView.getChildAt(0).getLeft() 5330 > mGridView.getWidth()); 5331 5332 slideInAndWaitIdle(); 5333 assertEquals("First view is aligned with padding left", mGridView.getPaddingLeft(), 5334 mGridView.getChildAt(0).getLeft()); 5335 5336 } 5337 5338 @Test 5339 public void testHorizontalAnimateOutRtl() throws Throwable { 5340 Intent intent = new Intent(); 5341 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 5342 R.layout.horizontal_linear_rtl); 5343 int[] items = new int[100]; 5344 for (int i = 0; i < items.length; i++) { 5345 items[i] = 300; 5346 } 5347 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5348 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5349 mOrientation = BaseGridView.HORIZONTAL; 5350 mNumRows = 1; 5351 5352 initActivity(intent); 5353 5354 assertEquals("First view is aligned with padding right", 5355 mGridView.getWidth() - mGridView.getPaddingRight(), 5356 mGridView.getChildAt(0).getRight()); 5357 5358 mActivityTestRule.runOnUiThread(new Runnable() { 5359 @Override 5360 public void run() { 5361 mGridView.animateOut(); 5362 } 5363 }); 5364 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 5365 @Override 5366 public boolean canProceed() { 5367 return mGridView.getChildAt(0).getRight() 5368 < mGridView.getWidth() - mGridView.getPaddingRight(); 5369 } 5370 }); 5371 mActivityTestRule.runOnUiThread(new Runnable() { 5372 @Override 5373 public void run() { 5374 mGridView.smoothScrollToPosition(0); 5375 } 5376 }); 5377 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 5378 @Override 5379 public boolean canProceed() { 5380 return mGridView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE; 5381 } 5382 }); 5383 5384 assertTrue("First view is slided out", mGridView.getChildAt(0).getRight() < 0); 5385 5386 slideInAndWaitIdle(); 5387 assertEquals("First view is aligned with padding right", 5388 mGridView.getWidth() - mGridView.getPaddingRight(), 5389 mGridView.getChildAt(0).getRight()); 5390 } 5391 5392 @Test 5393 public void testSmoothScrollerOutRange() throws Throwable { 5394 Intent intent = new Intent(); 5395 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, 5396 R.layout.vertical_linear_with_button_onleft); 5397 intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true); 5398 int[] items = new int[30]; 5399 for (int i = 0; i < items.length; i++) { 5400 items[i] = 680; 5401 } 5402 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5403 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5404 mOrientation = BaseGridView.VERTICAL; 5405 mNumRows = 1; 5406 5407 initActivity(intent); 5408 5409 final View button = mActivity.findViewById(R.id.button); 5410 mActivityTestRule.runOnUiThread(new Runnable() { 5411 public void run() { 5412 button.requestFocus(); 5413 } 5414 }); 5415 5416 mGridView.setSelectedPositionSmooth(0); 5417 waitForScrollIdle(mVerifyLayout); 5418 5419 mActivityTestRule.runOnUiThread(new Runnable() { 5420 public void run() { 5421 mGridView.setSelectedPositionSmooth(120); 5422 } 5423 }); 5424 waitForScrollIdle(mVerifyLayout); 5425 assertTrue(button.hasFocus()); 5426 int key; 5427 if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) { 5428 key = KeyEvent.KEYCODE_DPAD_LEFT; 5429 } else { 5430 key = KeyEvent.KEYCODE_DPAD_RIGHT; 5431 } 5432 sendKey(key); 5433 // the GridView should has focus in its children 5434 assertTrue(mGridView.hasFocus()); 5435 assertFalse(mGridView.isFocused()); 5436 assertEquals(29, mGridView.getSelectedPosition()); 5437 } 5438 5439 @Test 5440 public void testRemoveLastItemWithStableId() throws Throwable { 5441 Intent intent = new Intent(); 5442 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 5443 intent.putExtra(GridActivity.EXTRA_HAS_STABLE_IDS, true); 5444 int[] items = new int[1]; 5445 for (int i = 0; i < items.length; i++) { 5446 items[i] = 680; 5447 } 5448 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5449 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5450 mOrientation = BaseGridView.VERTICAL; 5451 mNumRows = 1; 5452 5453 initActivity(intent); 5454 5455 mActivityTestRule.runOnUiThread(new Runnable() { 5456 @Override 5457 public void run() { 5458 mGridView.getItemAnimator().setRemoveDuration(2000); 5459 mActivity.removeItems(0, 1, false); 5460 mGridView.getAdapter().notifyDataSetChanged(); 5461 } 5462 }); 5463 Thread.sleep(500); 5464 assertEquals(-1, mGridView.getSelectedPosition()); 5465 } 5466 5467 @Test 5468 public void testUpdateAndSelect1() throws Throwable { 5469 Intent intent = new Intent(); 5470 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 5471 intent.putExtra(GridActivity.EXTRA_HAS_STABLE_IDS, false); 5472 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 10); 5473 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5474 mOrientation = BaseGridView.VERTICAL; 5475 mNumRows = 1; 5476 5477 initActivity(intent); 5478 5479 mActivityTestRule.runOnUiThread(new Runnable() { 5480 @Override 5481 public void run() { 5482 mGridView.getAdapter().notifyDataSetChanged(); 5483 mGridView.setSelectedPosition(1); 5484 } 5485 }); 5486 waitOneUiCycle(); 5487 assertEquals(1, mGridView.getSelectedPosition()); 5488 } 5489 5490 @Test 5491 public void testUpdateAndSelect2() throws Throwable { 5492 Intent intent = new Intent(); 5493 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 5494 intent.putExtra(GridActivity.EXTRA_HAS_STABLE_IDS, false); 5495 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100); 5496 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5497 mOrientation = BaseGridView.VERTICAL; 5498 mNumRows = 1; 5499 5500 initActivity(intent); 5501 5502 mActivityTestRule.runOnUiThread(new Runnable() { 5503 @Override 5504 public void run() { 5505 mGridView.getAdapter().notifyDataSetChanged(); 5506 mGridView.setSelectedPosition(50); 5507 } 5508 }); 5509 waitOneUiCycle(); 5510 assertEquals(50, mGridView.getSelectedPosition()); 5511 } 5512 5513 @Test 5514 public void testUpdateAndSelect3() throws Throwable { 5515 Intent intent = new Intent(); 5516 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 5517 intent.putExtra(GridActivity.EXTRA_HAS_STABLE_IDS, false); 5518 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 10); 5519 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5520 mOrientation = BaseGridView.VERTICAL; 5521 mNumRows = 1; 5522 5523 initActivity(intent); 5524 5525 mActivityTestRule.runOnUiThread(new Runnable() { 5526 @Override 5527 public void run() { 5528 int[] newItems = new int[100]; 5529 for (int i = 0; i < newItems.length; i++) { 5530 newItems[i] = mActivity.mItemLengths[0]; 5531 } 5532 mActivity.addItems(0, newItems, false); 5533 mGridView.getAdapter().notifyDataSetChanged(); 5534 mGridView.setSelectedPosition(50); 5535 } 5536 }); 5537 waitOneUiCycle(); 5538 assertEquals(50, mGridView.getSelectedPosition()); 5539 } 5540 5541 @Test 5542 public void testFocusedPositonAfterRemoved1() throws Throwable { 5543 Intent intent = new Intent(); 5544 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 5545 final int[] items = new int[2]; 5546 for (int i = 0; i < items.length; i++) { 5547 items[i] = 300; 5548 } 5549 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5550 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5551 mOrientation = BaseGridView.VERTICAL; 5552 mNumRows = 1; 5553 5554 initActivity(intent); 5555 setSelectedPosition(1); 5556 assertEquals(1, mGridView.getSelectedPosition()); 5557 5558 final int[] newItems = new int[3]; 5559 for (int i = 0; i < newItems.length; i++) { 5560 newItems[i] = 300; 5561 } 5562 performAndWaitForAnimation(new Runnable() { 5563 @Override 5564 public void run() { 5565 mActivity.removeItems(0, 2, true); 5566 mActivity.addItems(0, newItems, true); 5567 } 5568 }); 5569 assertEquals(0, mGridView.getSelectedPosition()); 5570 } 5571 5572 @Test 5573 public void testFocusedPositonAfterRemoved2() throws Throwable { 5574 Intent intent = new Intent(); 5575 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 5576 final int[] items = new int[2]; 5577 for (int i = 0; i < items.length; i++) { 5578 items[i] = 300; 5579 } 5580 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5581 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5582 mOrientation = BaseGridView.VERTICAL; 5583 mNumRows = 1; 5584 5585 initActivity(intent); 5586 setSelectedPosition(1); 5587 assertEquals(1, mGridView.getSelectedPosition()); 5588 5589 final int[] newItems = new int[3]; 5590 for (int i = 0; i < newItems.length; i++) { 5591 newItems[i] = 300; 5592 } 5593 performAndWaitForAnimation(new Runnable() { 5594 @Override 5595 public void run() { 5596 mActivity.removeItems(1, 1, true); 5597 mActivity.addItems(1, newItems, true); 5598 } 5599 }); 5600 assertEquals(1, mGridView.getSelectedPosition()); 5601 } 5602 5603 static void assertNoCollectionItemInfo(AccessibilityNodeInfoCompat info) { 5604 AccessibilityNodeInfoCompat.CollectionItemInfoCompat nodeInfoCompat = 5605 info.getCollectionItemInfo(); 5606 if (nodeInfoCompat == null) { 5607 return; 5608 } 5609 assertTrue(nodeInfoCompat.getRowIndex() < 0); 5610 assertTrue(nodeInfoCompat.getColumnIndex() < 0); 5611 } 5612 5613 /** 5614 * This test would need talkback on. 5615 */ 5616 @Test 5617 public void testAccessibilityOfItemsBeingPushedOut() throws Throwable { 5618 Intent intent = new Intent(); 5619 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid); 5620 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100); 5621 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5622 mOrientation = BaseGridView.HORIZONTAL; 5623 mNumRows = 3; 5624 5625 initActivity(intent); 5626 5627 final int lastPos = mGridView.getChildAdapterPosition( 5628 mGridView.getChildAt(mGridView.getChildCount() - 1)); 5629 mActivityTestRule.runOnUiThread(new Runnable() { 5630 @Override 5631 public void run() { 5632 mGridView.getLayoutManager().setItemPrefetchEnabled(false); 5633 } 5634 }); 5635 final int numItemsToPushOut = mNumRows; 5636 mActivityTestRule.runOnUiThread(new Runnable() { 5637 @Override 5638 public void run() { 5639 // set longer enough so that accessibility service will initialize node 5640 // within setImportantForAccessibility(). 5641 mGridView.getItemAnimator().setRemoveDuration(2000); 5642 mGridView.getItemAnimator().setAddDuration(2000); 5643 final int[] newItems = new int[numItemsToPushOut]; 5644 final int newItemValue = mActivity.mItemLengths[0]; 5645 for (int i = 0; i < newItems.length; i++) { 5646 newItems[i] = newItemValue; 5647 } 5648 mActivity.addItems(lastPos - numItemsToPushOut + 1, newItems); 5649 } 5650 }); 5651 waitForItemAnimation(); 5652 } 5653 5654 /** 5655 * This test simulates talkback by calling setImportanceForAccessibility at end of animation 5656 */ 5657 @Test 5658 public void simulatesAccessibilityOfItemsBeingPushedOut() throws Throwable { 5659 Intent intent = new Intent(); 5660 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid); 5661 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100); 5662 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5663 mOrientation = BaseGridView.HORIZONTAL; 5664 mNumRows = 3; 5665 5666 initActivity(intent); 5667 5668 final HashSet<View> moveAnimationViews = new HashSet(); 5669 mActivity.mImportantForAccessibilityListener = 5670 new GridActivity.ImportantForAccessibilityListener() { 5671 RecyclerView.LayoutManager mLM = mGridView.getLayoutManager(); 5672 @Override 5673 public void onImportantForAccessibilityChanged(View view, int newValue) { 5674 // simulates talkack, having setImportantForAccessibility to call 5675 // onInitializeAccessibilityNodeInfoForItem() for the DISAPPEARING items. 5676 if (moveAnimationViews.contains(view)) { 5677 AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(); 5678 mLM.onInitializeAccessibilityNodeInfoForItem( 5679 null, null, view, info); 5680 } 5681 } 5682 }; 5683 final int lastPos = mGridView.getChildAdapterPosition( 5684 mGridView.getChildAt(mGridView.getChildCount() - 1)); 5685 final int numItemsToPushOut = mNumRows; 5686 for (int i = 0; i < numItemsToPushOut; i++) { 5687 moveAnimationViews.add( 5688 mGridView.getChildAt(mGridView.getChildCount() - 1 - i)); 5689 } 5690 mActivityTestRule.runOnUiThread(new Runnable() { 5691 @Override 5692 public void run() { 5693 mGridView.setItemAnimator(new DefaultItemAnimator() { 5694 @Override 5695 public void onMoveFinished(RecyclerView.ViewHolder item) { 5696 moveAnimationViews.remove(item.itemView); 5697 } 5698 }); 5699 mGridView.getLayoutManager().setItemPrefetchEnabled(false); 5700 } 5701 }); 5702 mActivityTestRule.runOnUiThread(new Runnable() { 5703 @Override 5704 public void run() { 5705 final int[] newItems = new int[numItemsToPushOut]; 5706 final int newItemValue = mActivity.mItemLengths[0] + 1; 5707 for (int i = 0; i < newItems.length; i++) { 5708 newItems[i] = newItemValue; 5709 } 5710 mActivity.addItems(lastPos - numItemsToPushOut + 1, newItems); 5711 } 5712 }); 5713 while (moveAnimationViews.size() != 0) { 5714 Thread.sleep(100); 5715 } 5716 } 5717 5718 @Test 5719 public void testAccessibilityNodeInfoOnRemovedFirstItem() throws Throwable { 5720 Intent intent = new Intent(); 5721 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid); 5722 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 6); 5723 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5724 mOrientation = BaseGridView.HORIZONTAL; 5725 mNumRows = 3; 5726 5727 initActivity(intent); 5728 5729 final View lastView = mGridView.findViewHolderForAdapterPosition(0).itemView; 5730 mActivityTestRule.runOnUiThread(new Runnable() { 5731 @Override 5732 public void run() { 5733 mGridView.getItemAnimator().setRemoveDuration(20000); 5734 mActivity.removeItems(0, 1); 5735 } 5736 }); 5737 waitForItemAnimationStart(); 5738 AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(lastView); 5739 mGridView.getLayoutManager().onInitializeAccessibilityNodeInfoForItem(null, null, 5740 lastView, info); 5741 assertNoCollectionItemInfo(info); 5742 } 5743 5744 @Test 5745 public void testAccessibilityNodeInfoOnRemovedLastItem() throws Throwable { 5746 Intent intent = new Intent(); 5747 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid); 5748 intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 6); 5749 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5750 mOrientation = BaseGridView.HORIZONTAL; 5751 mNumRows = 3; 5752 5753 initActivity(intent); 5754 5755 final View lastView = mGridView.findViewHolderForAdapterPosition(5).itemView; 5756 mActivityTestRule.runOnUiThread(new Runnable() { 5757 @Override 5758 public void run() { 5759 mGridView.getItemAnimator().setRemoveDuration(20000); 5760 mActivity.removeItems(5, 1); 5761 } 5762 }); 5763 waitForItemAnimationStart(); 5764 AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(lastView); 5765 mGridView.getLayoutManager().onInitializeAccessibilityNodeInfoForItem(null, null, 5766 lastView, info); 5767 assertNoCollectionItemInfo(info); 5768 } 5769 5770 static class FiveViewTypesProvider implements ViewTypeProvider { 5771 5772 @Override 5773 public int getViewType(int position) { 5774 switch (position) { 5775 case 0: 5776 return 0; 5777 case 1: 5778 return 1; 5779 case 2: 5780 return 2; 5781 case 3: 5782 return 3; 5783 case 4: 5784 return 4; 5785 } 5786 return 199; 5787 } 5788 } 5789 5790 // Used by testItemAlignmentVertical() testItemAlignmentHorizontal() 5791 static class ItemAlignmentWithPaddingFacetProvider implements 5792 ItemAlignmentFacetProvider { 5793 final ItemAlignmentFacet mFacet0; 5794 final ItemAlignmentFacet mFacet1; 5795 final ItemAlignmentFacet mFacet2; 5796 final ItemAlignmentFacet mFacet3; 5797 final ItemAlignmentFacet mFacet4; 5798 5799 ItemAlignmentWithPaddingFacetProvider() { 5800 ItemAlignmentFacet.ItemAlignmentDef[] defs; 5801 mFacet0 = new ItemAlignmentFacet(); 5802 defs = new ItemAlignmentFacet.ItemAlignmentDef[1]; 5803 defs[0] = new ItemAlignmentFacet.ItemAlignmentDef(); 5804 defs[0].setItemAlignmentViewId(R.id.t1); 5805 defs[0].setItemAlignmentOffsetPercent(0); 5806 defs[0].setItemAlignmentOffsetWithPadding(false); 5807 mFacet0.setAlignmentDefs(defs); 5808 mFacet1 = new ItemAlignmentFacet(); 5809 defs = new ItemAlignmentFacet.ItemAlignmentDef[1]; 5810 defs[0] = new ItemAlignmentFacet.ItemAlignmentDef(); 5811 defs[0].setItemAlignmentViewId(R.id.t1); 5812 defs[0].setItemAlignmentOffsetPercent(0); 5813 defs[0].setItemAlignmentOffsetWithPadding(true); 5814 mFacet1.setAlignmentDefs(defs); 5815 mFacet2 = new ItemAlignmentFacet(); 5816 defs = new ItemAlignmentFacet.ItemAlignmentDef[1]; 5817 defs[0] = new ItemAlignmentFacet.ItemAlignmentDef(); 5818 defs[0].setItemAlignmentViewId(R.id.t2); 5819 defs[0].setItemAlignmentOffsetPercent(100); 5820 defs[0].setItemAlignmentOffsetWithPadding(true); 5821 mFacet2.setAlignmentDefs(defs); 5822 mFacet3 = new ItemAlignmentFacet(); 5823 defs = new ItemAlignmentFacet.ItemAlignmentDef[1]; 5824 defs[0] = new ItemAlignmentFacet.ItemAlignmentDef(); 5825 defs[0].setItemAlignmentViewId(R.id.t2); 5826 defs[0].setItemAlignmentOffsetPercent(50); 5827 defs[0].setItemAlignmentOffsetWithPadding(true); 5828 mFacet3.setAlignmentDefs(defs); 5829 mFacet4 = new ItemAlignmentFacet(); 5830 defs = new ItemAlignmentFacet.ItemAlignmentDef[1]; 5831 defs[0] = new ItemAlignmentFacet.ItemAlignmentDef(); 5832 defs[0].setItemAlignmentViewId(R.id.t2); 5833 defs[0].setItemAlignmentOffsetPercent(50); 5834 defs[0].setItemAlignmentOffsetWithPadding(false); 5835 mFacet4.setAlignmentDefs(defs); 5836 } 5837 5838 @Override 5839 public ItemAlignmentFacet getItemAlignmentFacet(int viewType) { 5840 switch (viewType) { 5841 case 0: 5842 return mFacet0; 5843 case 1: 5844 return mFacet1; 5845 case 2: 5846 return mFacet2; 5847 case 3: 5848 return mFacet3; 5849 case 4: 5850 return mFacet4; 5851 } 5852 return null; 5853 } 5854 } 5855 5856 @Test 5857 public void testItemAlignmentVertical() throws Throwable { 5858 Intent intent = new Intent(); 5859 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear); 5860 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout2); 5861 int[] items = new int[5]; 5862 for (int i = 0; i < items.length; i++) { 5863 items[i] = 300; 5864 } 5865 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5866 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5867 intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS, 5868 FiveViewTypesProvider.class.getName()); 5869 intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_CLASS, 5870 ItemAlignmentWithPaddingFacetProvider.class.getName()); 5871 mOrientation = BaseGridView.VERTICAL; 5872 mNumRows = 1; 5873 5874 initActivity(intent); 5875 startWaitLayout(); 5876 mActivityTestRule.runOnUiThread(new Runnable() { 5877 @Override 5878 public void run() { 5879 mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE); 5880 mGridView.setWindowAlignmentOffsetPercent(50); 5881 mGridView.setWindowAlignmentOffset(0); 5882 } 5883 }); 5884 waitForLayout(); 5885 5886 final float windowAlignCenter = mGridView.getHeight() / 2f; 5887 Rect rect = new Rect(); 5888 View textView; 5889 5890 // test 1: does not include padding 5891 textView = mGridView.findViewHolderForAdapterPosition(0).itemView.findViewById(R.id.t1); 5892 rect.set(0, 0, textView.getWidth(), textView.getHeight()); 5893 mGridView.offsetDescendantRectToMyCoords(textView, rect); 5894 assertEquals(windowAlignCenter, rect.top, DELTA); 5895 5896 // test 2: including low padding 5897 setSelectedPosition(1); 5898 textView = mGridView.findViewHolderForAdapterPosition(1).itemView.findViewById(R.id.t1); 5899 assertTrue(textView.getPaddingTop() > 0); 5900 rect.set(0, textView.getPaddingTop(), textView.getWidth(), textView.getHeight()); 5901 mGridView.offsetDescendantRectToMyCoords(textView, rect); 5902 assertEquals(windowAlignCenter, rect.top, DELTA); 5903 5904 // test 3: including high padding 5905 setSelectedPosition(2); 5906 textView = mGridView.findViewHolderForAdapterPosition(2).itemView.findViewById(R.id.t2); 5907 assertTrue(textView.getPaddingBottom() > 0); 5908 rect.set(0, 0, textView.getWidth(), 5909 textView.getHeight() - textView.getPaddingBottom()); 5910 mGridView.offsetDescendantRectToMyCoords(textView, rect); 5911 assertEquals(windowAlignCenter, rect.bottom, DELTA); 5912 5913 // test 4: including padding will be ignored if offsetPercent is not 0 or 100 5914 setSelectedPosition(3); 5915 textView = mGridView.findViewHolderForAdapterPosition(3).itemView.findViewById(R.id.t2); 5916 assertTrue(textView.getPaddingTop() != textView.getPaddingBottom()); 5917 rect.set(0, 0, textView.getWidth(), textView.getHeight() / 2); 5918 mGridView.offsetDescendantRectToMyCoords(textView, rect); 5919 assertEquals(windowAlignCenter, rect.bottom, DELTA); 5920 5921 // test 5: does not include padding 5922 setSelectedPosition(4); 5923 textView = mGridView.findViewHolderForAdapterPosition(4).itemView.findViewById(R.id.t2); 5924 assertTrue(textView.getPaddingTop() != textView.getPaddingBottom()); 5925 rect.set(0, 0, textView.getWidth(), textView.getHeight() / 2); 5926 mGridView.offsetDescendantRectToMyCoords(textView, rect); 5927 assertEquals(windowAlignCenter, rect.bottom, DELTA); 5928 } 5929 5930 @Test 5931 public void testItemAlignmentHorizontal() throws Throwable { 5932 Intent intent = new Intent(); 5933 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear); 5934 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout3); 5935 int[] items = new int[5]; 5936 for (int i = 0; i < items.length; i++) { 5937 items[i] = 300; 5938 } 5939 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 5940 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 5941 intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS, 5942 FiveViewTypesProvider.class.getName()); 5943 intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_CLASS, 5944 ItemAlignmentWithPaddingFacetProvider.class.getName()); 5945 mOrientation = BaseGridView.VERTICAL; 5946 mNumRows = 1; 5947 5948 initActivity(intent); 5949 startWaitLayout(); 5950 mActivityTestRule.runOnUiThread(new Runnable() { 5951 @Override 5952 public void run() { 5953 mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE); 5954 mGridView.setWindowAlignmentOffsetPercent(50); 5955 mGridView.setWindowAlignmentOffset(0); 5956 } 5957 }); 5958 waitForLayout(); 5959 5960 final float windowAlignCenter = mGridView.getWidth() / 2f; 5961 Rect rect = new Rect(); 5962 View textView; 5963 5964 // test 1: does not include padding 5965 textView = mGridView.findViewHolderForAdapterPosition(0).itemView.findViewById(R.id.t1); 5966 rect.set(0, 0, textView.getWidth(), textView.getHeight()); 5967 mGridView.offsetDescendantRectToMyCoords(textView, rect); 5968 assertEquals(windowAlignCenter, rect.left, DELTA); 5969 5970 // test 2: including low padding 5971 setSelectedPosition(1); 5972 textView = mGridView.findViewHolderForAdapterPosition(1).itemView.findViewById(R.id.t1); 5973 assertTrue(textView.getPaddingLeft() > 0); 5974 rect.set(textView.getPaddingLeft(), 0, textView.getWidth(), textView.getHeight()); 5975 mGridView.offsetDescendantRectToMyCoords(textView, rect); 5976 assertEquals(windowAlignCenter, rect.left, DELTA); 5977 5978 // test 3: including high padding 5979 setSelectedPosition(2); 5980 textView = mGridView.findViewHolderForAdapterPosition(2).itemView.findViewById(R.id.t2); 5981 assertTrue(textView.getPaddingRight() > 0); 5982 rect.set(0, 0, textView.getWidth() - textView.getPaddingRight(), 5983 textView.getHeight()); 5984 mGridView.offsetDescendantRectToMyCoords(textView, rect); 5985 assertEquals(windowAlignCenter, rect.right, DELTA); 5986 5987 // test 4: including padding will be ignored if offsetPercent is not 0 or 100 5988 setSelectedPosition(3); 5989 textView = mGridView.findViewHolderForAdapterPosition(3).itemView.findViewById(R.id.t2); 5990 assertTrue(textView.getPaddingLeft() != textView.getPaddingRight()); 5991 rect.set(0, 0, textView.getWidth() / 2, textView.getHeight()); 5992 mGridView.offsetDescendantRectToMyCoords(textView, rect); 5993 assertEquals(windowAlignCenter, rect.right, DELTA); 5994 5995 // test 5: does not include padding 5996 setSelectedPosition(4); 5997 textView = mGridView.findViewHolderForAdapterPosition(4).itemView.findViewById(R.id.t2); 5998 assertTrue(textView.getPaddingLeft() != textView.getPaddingRight()); 5999 rect.set(0, 0, textView.getWidth() / 2, textView.getHeight()); 6000 mGridView.offsetDescendantRectToMyCoords(textView, rect); 6001 assertEquals(windowAlignCenter, rect.right, DELTA); 6002 } 6003 6004 @Test 6005 public void testItemAlignmentHorizontalRtl() throws Throwable { 6006 Intent intent = new Intent(); 6007 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear); 6008 intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout3); 6009 int[] items = new int[5]; 6010 for (int i = 0; i < items.length; i++) { 6011 items[i] = 300; 6012 } 6013 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 6014 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 6015 intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS, 6016 FiveViewTypesProvider.class.getName()); 6017 intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_CLASS, 6018 ItemAlignmentWithPaddingFacetProvider.class.getName()); 6019 mOrientation = BaseGridView.VERTICAL; 6020 mNumRows = 1; 6021 6022 initActivity(intent); 6023 startWaitLayout(); 6024 mActivityTestRule.runOnUiThread(new Runnable() { 6025 @Override 6026 public void run() { 6027 mGridView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL); 6028 mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE); 6029 mGridView.setWindowAlignmentOffsetPercent(50); 6030 mGridView.setWindowAlignmentOffset(0); 6031 } 6032 }); 6033 waitForLayout(); 6034 6035 final float windowAlignCenter = mGridView.getWidth() / 2f; 6036 Rect rect = new Rect(); 6037 View textView; 6038 6039 // test 1: does not include padding 6040 textView = mGridView.findViewHolderForAdapterPosition(0).itemView.findViewById(R.id.t1); 6041 rect.set(0, 0, textView.getWidth(), textView.getHeight()); 6042 mGridView.offsetDescendantRectToMyCoords(textView, rect); 6043 assertEquals(windowAlignCenter, rect.right, DELTA); 6044 6045 // test 2: including low padding 6046 setSelectedPosition(1); 6047 textView = mGridView.findViewHolderForAdapterPosition(1).itemView.findViewById(R.id.t1); 6048 assertTrue(textView.getPaddingRight() > 0); 6049 rect.set(0, 0, textView.getWidth() - textView.getPaddingRight(), 6050 textView.getHeight()); 6051 mGridView.offsetDescendantRectToMyCoords(textView, rect); 6052 assertEquals(windowAlignCenter, rect.right, DELTA); 6053 6054 // test 3: including high padding 6055 setSelectedPosition(2); 6056 textView = mGridView.findViewHolderForAdapterPosition(2).itemView.findViewById(R.id.t2); 6057 assertTrue(textView.getPaddingLeft() > 0); 6058 rect.set(textView.getPaddingLeft(), 0, textView.getWidth(), 6059 textView.getHeight()); 6060 mGridView.offsetDescendantRectToMyCoords(textView, rect); 6061 assertEquals(windowAlignCenter, rect.left, DELTA); 6062 6063 // test 4: including padding will be ignored if offsetPercent is not 0 or 100 6064 setSelectedPosition(3); 6065 textView = mGridView.findViewHolderForAdapterPosition(3).itemView.findViewById(R.id.t2); 6066 assertTrue(textView.getPaddingLeft() != textView.getPaddingRight()); 6067 rect.set(0, 0, textView.getWidth() / 2, textView.getHeight()); 6068 mGridView.offsetDescendantRectToMyCoords(textView, rect); 6069 assertEquals(windowAlignCenter, rect.right, DELTA); 6070 6071 // test 5: does not include padding 6072 setSelectedPosition(4); 6073 textView = mGridView.findViewHolderForAdapterPosition(4).itemView.findViewById(R.id.t2); 6074 assertTrue(textView.getPaddingLeft() != textView.getPaddingRight()); 6075 rect.set(0, 0, textView.getWidth() / 2, textView.getHeight()); 6076 mGridView.offsetDescendantRectToMyCoords(textView, rect); 6077 assertEquals(windowAlignCenter, rect.right, DELTA); 6078 } 6079 6080 enum ItemLocation { 6081 ITEM_AT_LOW, 6082 ITEM_AT_KEY_LINE, 6083 ITEM_AT_HIGH 6084 }; 6085 6086 static class ItemAt { 6087 final int mScrollPosition; 6088 final int mPosition; 6089 final ItemLocation mLocation; 6090 6091 ItemAt(int scrollPosition, int position, ItemLocation loc) { 6092 mScrollPosition = scrollPosition; 6093 mPosition = position; 6094 mLocation = loc; 6095 } 6096 6097 ItemAt(int position, ItemLocation loc) { 6098 mScrollPosition = position; 6099 mPosition = position; 6100 mLocation = loc; 6101 } 6102 } 6103 6104 /** 6105 * When scroll to position, item at position is expected at given location. 6106 */ 6107 static ItemAt itemAt(int position, ItemLocation location) { 6108 return new ItemAt(position, location); 6109 } 6110 6111 /** 6112 * When scroll to scrollPosition, item at position is expected at given location. 6113 */ 6114 static ItemAt itemAt(int scrollPosition, int position, ItemLocation location) { 6115 return new ItemAt(scrollPosition, position, location); 6116 } 6117 6118 void prepareKeyLineTest(int numItems) throws Throwable { 6119 Intent intent = new Intent(); 6120 intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear); 6121 int[] items = new int[numItems]; 6122 for (int i = 0; i < items.length; i++) { 6123 items[i] = 32; 6124 } 6125 intent.putExtra(GridActivity.EXTRA_ITEMS, items); 6126 intent.putExtra(GridActivity.EXTRA_STAGGERED, false); 6127 mOrientation = BaseGridView.HORIZONTAL; 6128 mNumRows = 1; 6129 6130 initActivity(intent); 6131 } 6132 6133 public void testPreferKeyLine(final int windowAlignment, 6134 final boolean preferKeyLineOverLow, 6135 final boolean preferKeyLineOverHigh, 6136 ItemLocation assertFirstItemLocation, 6137 ItemLocation assertLastItemLocation) throws Throwable { 6138 testPreferKeyLine(windowAlignment, preferKeyLineOverLow, preferKeyLineOverHigh, 6139 itemAt(0, assertFirstItemLocation), 6140 itemAt(mActivity.mNumItems - 1, assertLastItemLocation)); 6141 } 6142 6143 public void testPreferKeyLine(final int windowAlignment, 6144 final boolean preferKeyLineOverLow, 6145 final boolean preferKeyLineOverHigh, 6146 ItemLocation assertFirstItemLocation, 6147 ItemAt assertLastItemLocation) throws Throwable { 6148 testPreferKeyLine(windowAlignment, preferKeyLineOverLow, preferKeyLineOverHigh, 6149 itemAt(0, assertFirstItemLocation), 6150 assertLastItemLocation); 6151 } 6152 6153 public void testPreferKeyLine(final int windowAlignment, 6154 final boolean preferKeyLineOverLow, 6155 final boolean preferKeyLineOverHigh, 6156 ItemAt assertFirstItemLocation, 6157 ItemLocation assertLastItemLocation) throws Throwable { 6158 testPreferKeyLine(windowAlignment, preferKeyLineOverLow, preferKeyLineOverHigh, 6159 assertFirstItemLocation, 6160 itemAt(mActivity.mNumItems - 1, assertLastItemLocation)); 6161 } 6162 6163 public void testPreferKeyLine(final int windowAlignment, 6164 final boolean preferKeyLineOverLow, 6165 final boolean preferKeyLineOverHigh, 6166 ItemAt assertFirstItemLocation, 6167 ItemAt assertLastItemLocation) throws Throwable { 6168 TestPreferKeyLineOptions options = new TestPreferKeyLineOptions(); 6169 options.mAssertItemLocations = new ItemAt[] {assertFirstItemLocation, 6170 assertLastItemLocation}; 6171 options.mPreferKeyLineOverLow = preferKeyLineOverLow; 6172 options.mPreferKeyLineOverHigh = preferKeyLineOverHigh; 6173 options.mWindowAlignment = windowAlignment; 6174 6175 options.mRtl = false; 6176 testPreferKeyLine(options); 6177 6178 options.mRtl = true; 6179 testPreferKeyLine(options); 6180 } 6181 6182 static class TestPreferKeyLineOptions { 6183 int mWindowAlignment; 6184 boolean mPreferKeyLineOverLow; 6185 boolean mPreferKeyLineOverHigh; 6186 ItemAt[] mAssertItemLocations; 6187 boolean mRtl; 6188 } 6189 6190 public void testPreferKeyLine(final TestPreferKeyLineOptions options) throws Throwable { 6191 startWaitLayout(); 6192 mActivityTestRule.runOnUiThread(new Runnable() { 6193 @Override 6194 public void run() { 6195 if (options.mRtl) { 6196 mGridView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL); 6197 } else { 6198 mGridView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); 6199 } 6200 mGridView.setWindowAlignment(options.mWindowAlignment); 6201 mGridView.setWindowAlignmentOffsetPercent(50); 6202 mGridView.setWindowAlignmentOffset(0); 6203 mGridView.setWindowAlignmentPreferKeyLineOverLowEdge(options.mPreferKeyLineOverLow); 6204 mGridView.setWindowAlignmentPreferKeyLineOverHighEdge( 6205 options.mPreferKeyLineOverHigh); 6206 } 6207 }); 6208 waitForLayout(); 6209 6210 final int paddingStart = mGridView.getPaddingStart(); 6211 final int paddingEnd = mGridView.getPaddingEnd(); 6212 final int windowAlignCenter = mGridView.getWidth() / 2; 6213 6214 for (int i = 0; i < options.mAssertItemLocations.length; i++) { 6215 ItemAt assertItemLocation = options.mAssertItemLocations[i]; 6216 setSelectedPosition(assertItemLocation.mScrollPosition); 6217 View view = mGridView.findViewHolderForAdapterPosition(assertItemLocation.mPosition) 6218 .itemView; 6219 switch (assertItemLocation.mLocation) { 6220 case ITEM_AT_LOW: 6221 if (options.mRtl) { 6222 assertEquals(mGridView.getWidth() - paddingStart, view.getRight()); 6223 } else { 6224 assertEquals(paddingStart, view.getLeft()); 6225 } 6226 break; 6227 case ITEM_AT_HIGH: 6228 if (options.mRtl) { 6229 assertEquals(paddingEnd, view.getLeft()); 6230 } else { 6231 assertEquals(mGridView.getWidth() - paddingEnd, view.getRight()); 6232 } 6233 break; 6234 case ITEM_AT_KEY_LINE: 6235 assertEquals(windowAlignCenter, (view.getLeft() + view.getRight()) / 2, DELTA); 6236 break; 6237 } 6238 } 6239 } 6240 6241 @Test 6242 public void testPreferKeyLine1() throws Throwable { 6243 prepareKeyLineTest(1); 6244 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, false, false, 6245 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6246 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, false, true, 6247 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6248 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, true, false, 6249 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6250 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, true, true, 6251 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6252 6253 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, false, false, 6254 ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_LOW); 6255 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, false, true, 6256 ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_LOW); 6257 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, true, false, 6258 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6259 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, true, true, 6260 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6261 6262 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, false, false, 6263 ItemLocation.ITEM_AT_HIGH, ItemLocation.ITEM_AT_HIGH); 6264 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, false, true, 6265 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6266 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, true, false, 6267 ItemLocation.ITEM_AT_HIGH, ItemLocation.ITEM_AT_HIGH); 6268 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, true, true, 6269 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6270 6271 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, false, false, 6272 ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_LOW); 6273 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, false, true, 6274 ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_LOW); 6275 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, true, false, 6276 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6277 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, true, true, 6278 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6279 } 6280 6281 @Test 6282 public void testPreferKeyLine2() throws Throwable { 6283 prepareKeyLineTest(2); 6284 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, false, false, 6285 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6286 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, false, true, 6287 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6288 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, true, false, 6289 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6290 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, true, true, 6291 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6292 6293 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, false, false, 6294 ItemLocation.ITEM_AT_LOW, itemAt(1, 0, ItemLocation.ITEM_AT_LOW)); 6295 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, false, true, 6296 ItemLocation.ITEM_AT_LOW, itemAt(1, 0, ItemLocation.ITEM_AT_LOW)); 6297 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, true, false, 6298 itemAt(0, 1, ItemLocation.ITEM_AT_KEY_LINE), 6299 itemAt(1, 1, ItemLocation.ITEM_AT_KEY_LINE)); 6300 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, true, true, 6301 itemAt(0, 1, ItemLocation.ITEM_AT_KEY_LINE), 6302 itemAt(1, 1, ItemLocation.ITEM_AT_KEY_LINE)); 6303 6304 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, false, false, 6305 itemAt(0, 1, ItemLocation.ITEM_AT_HIGH), 6306 itemAt(1, 1, ItemLocation.ITEM_AT_HIGH)); 6307 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, false, true, 6308 itemAt(0, 0, ItemLocation.ITEM_AT_KEY_LINE), 6309 itemAt(1, 0, ItemLocation.ITEM_AT_KEY_LINE)); 6310 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, true, false, 6311 itemAt(0, 1, ItemLocation.ITEM_AT_HIGH), 6312 itemAt(1, 1, ItemLocation.ITEM_AT_HIGH)); 6313 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, true, true, 6314 itemAt(0, 0, ItemLocation.ITEM_AT_KEY_LINE), 6315 itemAt(1, 0, ItemLocation.ITEM_AT_KEY_LINE)); 6316 6317 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, false, false, 6318 ItemLocation.ITEM_AT_LOW, itemAt(1, 0, ItemLocation.ITEM_AT_LOW)); 6319 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, false, true, 6320 ItemLocation.ITEM_AT_LOW, itemAt(1, 0, ItemLocation.ITEM_AT_LOW)); 6321 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, true, false, 6322 itemAt(0, 1, ItemLocation.ITEM_AT_KEY_LINE), 6323 itemAt(1, 1, ItemLocation.ITEM_AT_KEY_LINE)); 6324 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, true, true, 6325 itemAt(0, 1, ItemLocation.ITEM_AT_KEY_LINE), 6326 itemAt(1, 1, ItemLocation.ITEM_AT_KEY_LINE)); 6327 } 6328 6329 @Test 6330 public void testPreferKeyLine10000() throws Throwable { 6331 prepareKeyLineTest(10000); 6332 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, false, false, 6333 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6334 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, false, true, 6335 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6336 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, true, false, 6337 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6338 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_NO_EDGE, true, true, 6339 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_KEY_LINE); 6340 6341 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, false, false, 6342 ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_KEY_LINE); 6343 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, false, true, 6344 ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_KEY_LINE); 6345 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, true, false, 6346 ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_KEY_LINE); 6347 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_LOW_EDGE, true, true, 6348 ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_KEY_LINE); 6349 6350 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, false, false, 6351 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_HIGH); 6352 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, false, true, 6353 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_HIGH); 6354 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, true, false, 6355 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_HIGH); 6356 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE, true, true, 6357 ItemLocation.ITEM_AT_KEY_LINE, ItemLocation.ITEM_AT_HIGH); 6358 6359 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, false, false, 6360 ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_HIGH); 6361 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, false, true, 6362 ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_HIGH); 6363 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, true, false, 6364 ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_HIGH); 6365 testPreferKeyLine(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE, true, true, 6366 ItemLocation.ITEM_AT_LOW, ItemLocation.ITEM_AT_HIGH); 6367 } 6368 } 6369