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