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