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 android.support.v17.leanback.widget;
17 
18 import android.app.Instrumentation;
19 import android.content.Intent;
20 import android.graphics.Canvas;
21 import android.graphics.Color;
22 import android.graphics.Rect;
23 import android.graphics.drawable.ColorDrawable;
24 import android.os.Parcelable;
25 import android.support.v17.leanback.tests.R;
26 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
27 import android.support.v7.widget.RecyclerView;
28 import android.support.v7.widget.RecyclerViewAccessibilityDelegate;
29 import android.test.ActivityInstrumentationTestCase2;
30 import android.text.Selection;
31 import android.text.Spannable;
32 import android.util.SparseArray;
33 import android.util.SparseIntArray;
34 import android.view.KeyEvent;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.widget.TextView;
38 
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Comparator;
42 import java.util.HashMap;
43 
44 /**
45  * @hide from javadoc
46  */
47 public class GridWidgetTest extends ActivityInstrumentationTestCase2<GridActivity> {
48 
49     private static final boolean HUMAN_DELAY = false;
50     private static final long WAIT_FOR_SCROLL_IDLE_TIMEOUT_MS = 60000;
51 
52     protected GridActivity mActivity;
53     protected Instrumentation mInstrumentation;
54     protected BaseGridView mGridView;
55     protected GridLayoutManager mLayoutManager;
56     protected int mOrientation;
57     protected int mNumRows;
58 
59     private final Comparator<View> mRowSortComparator = new Comparator<View>() {
60         public int compare(View lhs, View rhs) {
61             if (mOrientation == BaseGridView.HORIZONTAL) {
62                 return lhs.getLeft() - rhs.getLeft();
63             } else {
64                 return lhs.getTop() - rhs.getTop();
65             }
66         };
67     };
68 
69     /**
70      * Verify margins between items on same row are same.
71      */
72     private final Runnable mVerifyLayout = new Runnable() {
73         @Override
74         public void run() {
75             verifyMargin();
76         }
77     };
78 
GridWidgetTest()79     public GridWidgetTest() {
80         super("android.support.v17.leanback.tests", GridActivity.class);
81     }
82 
humanDelay(int delay)83     private void humanDelay(int delay) throws InterruptedException {
84         if (HUMAN_DELAY) Thread.sleep(delay);
85     }
86     /**
87      * Change size of the Adapter and notifyDataSetChanged.
88      */
changeArraySize(final int size)89     private void changeArraySize(final int size) throws Throwable {
90         runTestOnUiThread(new Runnable() {
91             public void run() {
92                 mActivity.changeArraySize(size);
93             }
94         });
95         Thread.sleep(500);
96     }
97 
98     /**
99      * Change selected position.
100      */
setSelectedPosition(final int position, final int scrollExtra)101     private void setSelectedPosition(final int position, final int scrollExtra) throws Throwable {
102         runTestOnUiThread(new Runnable() {
103             public void run() {
104                 mGridView.setSelectedPosition(position, scrollExtra);
105             }
106         });
107         Thread.sleep(500);
108     }
109 
waitForScrollIdleAndItemAnimation(Runnable verify)110     protected void waitForScrollIdleAndItemAnimation(Runnable verify) throws Throwable {
111         waitForScrollIdle();
112         waitForItemAnimation();
113         verify.run();
114     }
115 
waitForItemAnimation()116     protected void waitForItemAnimation() throws Throwable {
117         Thread.sleep(100);
118         while (mGridView.getItemAnimator() != null && mGridView.getItemAnimator().isRunning()) {
119             try {
120                 Thread.sleep(100);
121             } catch (InterruptedException ex) {
122                 break;
123             }
124         }
125     }
126 
127     /**
128      * Wait for grid view stop scroll and optionally verify state of grid view.
129      */
waitForScrollIdle(Runnable verify)130     protected void waitForScrollIdle(Runnable verify) throws Throwable {
131         Thread.sleep(100);
132         int total = 0;
133         while (mGridView.getLayoutManager().isSmoothScrolling() ||
134                 mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE) {
135             if ((total += 100) >= WAIT_FOR_SCROLL_IDLE_TIMEOUT_MS) {
136                 throw new RuntimeException("waitForScrollIdle Timeout");
137             }
138             try {
139                 Thread.sleep(100);
140             } catch (InterruptedException ex) {
141                 break;
142             }
143             if (verify != null) {
144                 runTestOnUiThread(verify);
145             }
146         }
147     }
148 
149     /**
150      * Wait for grid view stop animation and optionally verify state of grid view.
151      */
waitForTransientStateGone(Runnable verify)152     protected void waitForTransientStateGone(Runnable verify) throws Throwable {
153         do {
154             try {
155                 Thread.sleep(100);
156             } catch (InterruptedException ex) {
157                 break;
158             }
159             if (verify != null) {
160                 runTestOnUiThread(verify);
161             }
162         } while (mGridView.hasTransientState());
163     }
164 
165     /**
166      * Wait for grid view stop scroll.
167      */
waitForScrollIdle()168     protected void waitForScrollIdle() throws Throwable {
169         waitForScrollIdle(null);
170     }
171 
172     /**
173      * Scrolls using given key.
174      */
scroll(int key, Runnable verify)175     protected void scroll(int key, Runnable verify) throws Throwable {
176         do {
177             if (verify != null) {
178                 runTestOnUiThread(verify);
179             }
180             sendRepeatedKeys(10, key);
181             try {
182                 Thread.sleep(300);
183             } catch (InterruptedException ex) {
184                 break;
185             }
186         } while (mGridView.getLayoutManager().isSmoothScrolling() ||
187                 mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE);
188     }
189 
scrollToBegin(Runnable verify)190     protected void scrollToBegin(Runnable verify) throws Throwable {
191         int key;
192         if (mOrientation == BaseGridView.HORIZONTAL) {
193             if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
194                 key = KeyEvent.KEYCODE_DPAD_RIGHT;
195             } else {
196                 key = KeyEvent.KEYCODE_DPAD_LEFT;
197             }
198         } else {
199             key = KeyEvent.KEYCODE_DPAD_UP;
200         }
201         scroll(key, verify);
202     }
203 
scrollToEnd(Runnable verify)204     protected void scrollToEnd(Runnable verify) throws Throwable {
205         int key;
206         if (mOrientation == BaseGridView.HORIZONTAL) {
207             if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
208                 key = KeyEvent.KEYCODE_DPAD_LEFT;
209             } else {
210                 key = KeyEvent.KEYCODE_DPAD_RIGHT;
211             }
212         } else {
213             key = KeyEvent.KEYCODE_DPAD_DOWN;
214         }
215         scroll(key, verify);
216     }
217 
218     /**
219      * Group and sort children by their position on each row (HORIZONTAL) or column(VERTICAL).
220      */
sortByRows()221     protected View[][] sortByRows() {
222         final HashMap<Integer, ArrayList<View>> rows = new HashMap<Integer, ArrayList<View>>();
223         ArrayList<Integer> rowLocations = new ArrayList();
224         for (int i = 0; i < mGridView.getChildCount(); i++) {
225             View v = mGridView.getChildAt(i);
226             int rowLocation;
227             if (mOrientation == BaseGridView.HORIZONTAL) {
228                 rowLocation = v.getTop();
229             } else {
230                 rowLocation = mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL ?
231                     v.getRight() : v.getLeft();
232             }
233             ArrayList<View> views = rows.get(rowLocation);
234             if (views == null) {
235                 views = new ArrayList<View>();
236                 rows.put(rowLocation, views);
237                 rowLocations.add(rowLocation);
238             }
239             views.add(v);
240         }
241         Object[] sortedLocations = rowLocations.toArray();
242         Arrays.sort(sortedLocations);
243         if (mNumRows != rows.size()) {
244             assertEquals("Dump Views by rows "+rows, mNumRows, rows.size());
245         }
246         View[][] sorted = new View[rows.size()][];
247         for (int i = 0; i < rowLocations.size(); i++) {
248             Integer rowLocation = rowLocations.get(i);
249             ArrayList<View> arr = rows.get(rowLocation);
250             View[] views = arr.toArray(new View[arr.size()]);
251             Arrays.sort(views, mRowSortComparator);
252             sorted[i] = views;
253         }
254         return sorted;
255     }
256 
verifyMargin()257     protected void verifyMargin() {
258         View[][] sorted = sortByRows();
259         for (int row = 0; row < sorted.length; row++) {
260             View[] views = sorted[row];
261             int margin = -1;
262             for (int i = 1; i < views.length; i++) {
263                 if (mOrientation == BaseGridView.HORIZONTAL) {
264                     assertEquals(mGridView.getHorizontalMargin(),
265                             views[i].getLeft() - views[i - 1].getRight());
266                 } else {
267                     assertEquals(mGridView.getVerticalMargin(),
268                             views[i].getTop() - views[i - 1].getBottom());
269                 }
270             }
271         }
272     }
273 
verifyBeginAligned()274     protected void verifyBeginAligned() {
275         View[][] sorted = sortByRows();
276         int alignedLocation = 0;
277         if (mOrientation == BaseGridView.HORIZONTAL) {
278             if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
279                 for (int i = 0; i < sorted.length; i++) {
280                     if (i == 0) {
281                         alignedLocation = sorted[i][sorted[i].length - 1].getRight();
282                     } else {
283                         assertEquals(alignedLocation, sorted[i][sorted[i].length - 1].getRight());
284                     }
285                 }
286             } else {
287                 for (int i = 0; i < sorted.length; i++) {
288                     if (i == 0) {
289                         alignedLocation = sorted[i][0].getLeft();
290                     } else {
291                         assertEquals(alignedLocation, sorted[i][0].getLeft());
292                     }
293                 }
294             }
295         } else {
296             for (int i = 0; i < sorted.length; i++) {
297                 if (i == 0) {
298                     alignedLocation = sorted[i][0].getTop();
299                 } else {
300                     assertEquals(alignedLocation, sorted[i][0].getTop());
301                 }
302             }
303         }
304     }
305 
getEndEdges()306     protected int[] getEndEdges() {
307         View[][] sorted = sortByRows();
308         int[] edges = new int[sorted.length];
309         if (mOrientation == BaseGridView.HORIZONTAL) {
310             if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
311                 for (int i = 0; i < sorted.length; i++) {
312                     edges[i] = sorted[i][0].getLeft();
313                 }
314             } else {
315                 for (int i = 0; i < sorted.length; i++) {
316                     edges[i] = sorted[i][sorted[i].length - 1].getRight();
317                 }
318             }
319         } else {
320             for (int i = 0; i < sorted.length; i++) {
321                 edges[i] = sorted[i][sorted[i].length - 1].getBottom();
322             }
323         }
324         return edges;
325     }
326 
verifyEdgesSame(int[] edges, int[] edges2)327     protected void verifyEdgesSame(int[] edges, int[] edges2) {
328         assertEquals(edges.length, edges2.length);
329         for (int i = 0; i < edges.length; i++) {
330             assertEquals(edges[i], edges2[i]);
331         }
332     }
333 
verifyBoundCount(int count)334     protected void verifyBoundCount(int count) {
335         if (mActivity.getBoundCount() != count) {
336             StringBuffer b = new StringBuffer();
337             b.append("ItemsLength: ");
338             for (int i = 0; i < mActivity.mItemLengths.length; i++) {
339                 b.append(mActivity.mItemLengths[i]).append(",");
340             }
341             assertEquals("Bound count does not match, ItemsLengths: "+ b,
342                     count, mActivity.getBoundCount());
343         }
344     }
345 
getCenterY(View v)346     private static int getCenterY(View v) {
347         return (v.getTop() + v.getBottom())/2;
348     }
349 
getCenterX(View v)350     private static int getCenterX(View v) {
351         return (v.getLeft() + v.getRight())/2;
352     }
353 
initActivity(Intent intent)354     private void initActivity(Intent intent) {
355         setActivityIntent(intent);
356         mActivity = getActivity();
357         final String testName = getName();
358         try {
359             runTestOnUiThread(new Runnable() {
360                 public void run() {
361                     mActivity.setTitle(testName);
362                 }
363             });
364             Thread.sleep(1000);
365         } catch (Throwable t) {
366             t.printStackTrace();
367         }
368         mGridView = mActivity.mGridView;
369     }
370 
testThreeRowHorizontalBasic()371     public void testThreeRowHorizontalBasic() throws Throwable {
372         mInstrumentation = getInstrumentation();
373         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
374         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid);
375         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100);
376         initActivity(intent);
377         mOrientation = BaseGridView.HORIZONTAL;
378         mNumRows = 3;
379 
380         scrollToEnd(mVerifyLayout);
381         verifyBoundCount(100);
382 
383         scrollToBegin(mVerifyLayout);
384 
385         verifyBeginAligned();
386     }
387 
388     static class DividerDecoration extends RecyclerView.ItemDecoration {
389 
390         private ColorDrawable mTopDivider;
391         private ColorDrawable mBottomDivider;
392         private int mLeftOffset;
393         private int mRightOffset;
394         private int mTopOffset;
395         private int mBottomOffset;
396 
DividerDecoration(int leftOffset, int topOffset, int rightOffset, int bottomOffset)397         DividerDecoration(int leftOffset, int topOffset, int rightOffset, int bottomOffset) {
398             mLeftOffset = leftOffset;
399             mTopOffset = topOffset;
400             mRightOffset = rightOffset;
401             mBottomOffset = bottomOffset;
402         }
403 
404         @Override
onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state)405         public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
406             if (mTopDivider == null) {
407                 mTopDivider = new ColorDrawable(Color.RED);
408             }
409             if (mBottomDivider == null) {
410                 mBottomDivider = new ColorDrawable(Color.BLUE);
411             }
412             final int childCount = parent.getChildCount();
413             final int width = parent.getWidth();
414             for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) {
415                 final View view = parent.getChildAt(childViewIndex);
416                 mTopDivider.setBounds(0, (int) view.getY() - mTopOffset, width, (int) view.getY());
417                 mTopDivider.draw(c);
418                 mBottomDivider.setBounds(0, (int) view.getY() + view.getHeight(), width,
419                         (int) view.getY() + view.getHeight() + mBottomOffset);
420                 mBottomDivider.draw(c);
421             }
422         }
423 
424         @Override
getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)425         public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
426                                    RecyclerView.State state) {
427             outRect.left = mLeftOffset;
428             outRect.top = mTopOffset;
429             outRect.right = mRightOffset;
430             outRect.bottom = mBottomOffset;
431         }
432     }
433 
testItemDecorationAndMargins()434     public void testItemDecorationAndMargins() throws Throwable {
435 
436         final int leftMargin = 3;
437         final int topMargin = 4;
438         final int rightMargin = 7;
439         final int bottomMargin = 8;
440         final int itemHeight = 100;
441 
442         mInstrumentation = getInstrumentation();
443         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
444         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
445         intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{itemHeight, itemHeight, itemHeight});
446         intent.putExtra(GridActivity.EXTRA_LAYOUT_MARGINS,
447                 new int[]{leftMargin, topMargin, rightMargin, bottomMargin});
448         initActivity(intent);
449         mOrientation = BaseGridView.VERTICAL;
450         mNumRows = 1;
451 
452         final int paddingLeft = mGridView.getPaddingLeft();
453         final int paddingTop = mGridView.getPaddingTop();
454         final int verticalSpace = mGridView.getVerticalMargin();
455         final int decorationLeft = 17;
456         final int decorationTop = 1;
457         final int decorationRight = 19;
458         final int decorationBottom = 2;
459 
460         runTestOnUiThread(new Runnable() {
461             public void run() {
462                 mGridView.addItemDecoration(new DividerDecoration(decorationLeft, decorationTop,
463                         decorationRight, decorationBottom));
464             }
465         });
466         waitForScrollIdle();
467 
468         View child0 = mGridView.getChildAt(0);
469         View child1 = mGridView.getChildAt(1);
470         View child2 = mGridView.getChildAt(2);
471 
472         assertEquals(itemHeight, child0.getBottom() - child0.getTop());
473 
474         // verify left margins
475         assertEquals(paddingLeft + leftMargin + decorationLeft, child0.getLeft());
476         assertEquals(paddingLeft + leftMargin + decorationLeft, child1.getLeft());
477         assertEquals(paddingLeft + leftMargin + decorationLeft, child2.getLeft());
478         // verify top bottom margins and decoration offset
479         assertEquals(paddingTop + topMargin + decorationTop, child0.getTop());
480         assertEquals(bottomMargin + decorationBottom + verticalSpace + decorationTop + topMargin,
481                 child1.getTop() - child0.getBottom());
482         assertEquals(bottomMargin + decorationBottom + verticalSpace + decorationTop + topMargin,
483                 child2.getTop() - child1.getBottom());
484 
485     }
486 
testItemDecorationAndMarginsAndOpticalBounds()487     public void testItemDecorationAndMarginsAndOpticalBounds() throws Throwable {
488         final int leftMargin = 3;
489         final int topMargin = 4;
490         final int rightMargin = 7;
491         final int bottomMargin = 8;
492         final int itemHeight = 100;
493         final int ninePatchDrawableResourceId = R.drawable.lb_card_shadow_focused;
494 
495         mInstrumentation = getInstrumentation();
496         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
497         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
498         intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{itemHeight, itemHeight, itemHeight});
499         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout);
500         intent.putExtra(GridActivity.EXTRA_LAYOUT_MARGINS,
501                 new int[]{leftMargin, topMargin, rightMargin, bottomMargin});
502         intent.putExtra(GridActivity.EXTRA_NINEPATCH_SHADOW, ninePatchDrawableResourceId);
503         initActivity(intent);
504         mOrientation = BaseGridView.VERTICAL;
505         mNumRows = 1;
506 
507         final int paddingLeft = mGridView.getPaddingLeft();
508         final int paddingTop = mGridView.getPaddingTop();
509         final int verticalSpace = mGridView.getVerticalMargin();
510         final int decorationLeft = 17;
511         final int decorationTop = 1;
512         final int decorationRight = 19;
513         final int decorationBottom = 2;
514 
515         final Rect opticalPaddings = new Rect();
516         mGridView.getContext().getDrawable(ninePatchDrawableResourceId).getPadding(opticalPaddings);
517         final int opticalInsetsLeft = opticalPaddings.left;
518         final int opticalInsetsTop = opticalPaddings.top;
519         final int opticalInsetsRight = opticalPaddings.right;
520         final int opticalInsetsBottom = opticalPaddings.bottom;
521         assertTrue(opticalInsetsLeft > 0);
522         assertTrue(opticalInsetsTop > 0);
523         assertTrue(opticalInsetsRight > 0);
524         assertTrue(opticalInsetsBottom > 0);
525 
526         runTestOnUiThread(new Runnable() {
527             public void run() {
528                 mGridView.addItemDecoration(new DividerDecoration(decorationLeft, decorationTop,
529                         decorationRight, decorationBottom));
530             }
531         });
532         waitForScrollIdle();
533 
534         View child0 = mGridView.getChildAt(0);
535         View child1 = mGridView.getChildAt(1);
536         View child2 = mGridView.getChildAt(2);
537 
538         assertEquals(itemHeight + opticalInsetsTop + opticalInsetsBottom,
539                 child0.getBottom() - child0.getTop());
540 
541         // verify left margins decoration and optical insets
542         assertEquals(paddingLeft + leftMargin + decorationLeft - opticalInsetsLeft,
543                 child0.getLeft());
544         assertEquals(paddingLeft + leftMargin + decorationLeft - opticalInsetsLeft,
545                 child1.getLeft());
546         assertEquals(paddingLeft + leftMargin + decorationLeft - opticalInsetsLeft,
547                 child2.getLeft());
548         // verify top bottom margins decoration offset and optical insets
549         assertEquals(paddingTop + topMargin + decorationTop, child0.getTop() + opticalInsetsTop);
550         assertEquals(bottomMargin + decorationBottom + verticalSpace + decorationTop + topMargin,
551                 (child1.getTop() + opticalInsetsTop) - (child0.getBottom() - opticalInsetsBottom));
552         assertEquals(bottomMargin + decorationBottom + verticalSpace + decorationTop + topMargin,
553                 (child2.getTop() + opticalInsetsTop) - (child1.getBottom() - opticalInsetsBottom));
554 
555     }
556 
testThreeColumnVerticalBasic()557     public void testThreeColumnVerticalBasic() throws Throwable {
558 
559         mInstrumentation = getInstrumentation();
560         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
561         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid);
562         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
563         initActivity(intent);
564         mOrientation = BaseGridView.VERTICAL;
565         mNumRows = 3;
566 
567         scrollToEnd(mVerifyLayout);
568         verifyBoundCount(200);
569 
570         scrollToBegin(mVerifyLayout);
571 
572         verifyBeginAligned();
573     }
574 
testRedundantAppendRemove()575     public void testRedundantAppendRemove() throws Throwable {
576         mInstrumentation = getInstrumentation();
577         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
578         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
579                 R.layout.vertical_grid_testredundantappendremove);
580         intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{
581                 149,177,128,234,227,187,163,223,146,210,228,148,227,193,182,197,177,142,225,207,
582                 157,171,209,204,187,184,123,221,197,153,202,179,193,214,226,173,225,143,188,159,
583                 139,193,233,143,227,203,222,124,228,223,164,131,228,126,211,160,165,152,235,184,
584                 155,224,149,181,171,229,200,234,177,130,164,172,188,139,132,203,179,220,147,131,
585                 226,127,230,239,183,203,206,227,123,170,239,234,200,149,237,204,160,133,202,234,
586                 173,122,139,149,151,153,216,231,121,145,227,153,186,174,223,180,123,215,206,216,
587                 239,222,219,207,193,218,140,133,171,153,183,132,233,138,159,174,189,171,143,128,
588                 152,222,141,202,224,190,134,120,181,231,230,136,132,224,136,210,207,150,128,183,
589                 221,194,179,220,126,221,137,205,223,193,172,132,226,209,133,191,227,127,159,171,
590                 180,149,237,177,194,207,170,202,161,144,147,199,205,186,164,140,193,203,224,129});
591         initActivity(intent);
592         mOrientation = BaseGridView.VERTICAL;
593         mNumRows = 3;
594 
595         scrollToEnd(mVerifyLayout);
596 
597         verifyBoundCount(200);
598 
599         scrollToBegin(mVerifyLayout);
600 
601         verifyBeginAligned();
602     }
603 
testRedundantAppendRemove2()604     public void testRedundantAppendRemove2() throws Throwable {
605         mInstrumentation = getInstrumentation();
606         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
607         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
608                 R.layout.horizontal_grid_testredundantappendremove2);
609         intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{
610                 318,333,199,224,246,273,269,289,340,313,265,306,349,269,185,282,257,354,316,252,
611                 237,290,283,343,196,313,290,343,191,262,342,228,343,349,251,203,226,305,265,213,
612                 216,333,295,188,187,281,288,311,244,232,224,332,290,181,267,276,226,261,335,355,
613                 225,217,219,183,234,285,257,304,182,250,244,223,257,219,342,185,347,205,302,315,
614                 299,309,292,237,192,309,228,250,347,227,337,298,299,185,185,331,223,284,265,351});
615         initActivity(intent);
616         mOrientation = BaseGridView.HORIZONTAL;
617         mNumRows = 3;
618         mLayoutManager = (GridLayoutManager) mGridView.getLayoutManager();
619 
620         // test append without staggered result cache
621         scrollToEnd(mVerifyLayout);
622 
623         verifyBoundCount(100);
624         int[] endEdges = getEndEdges();
625 
626         scrollToBegin(mVerifyLayout);
627 
628         verifyBeginAligned();
629 
630         // now test append with staggered result cache
631         changeArraySize(3);
632         assertEquals("Staggerd cache should be kept as is when no item size change",
633                 100, ((StaggeredGrid) mLayoutManager.mGrid).mLocations.size());
634 
635         mActivity.resetBoundCount();
636         changeArraySize(100);
637 
638         scrollToEnd(mVerifyLayout);
639         verifyBoundCount(100);
640 
641         // we should get same aligned end edges
642         int[] endEdges2 = getEndEdges();
643         verifyEdgesSame(endEdges, endEdges2);
644     }
645 
testItemMovedHorizontal()646     public void testItemMovedHorizontal() throws Throwable {
647 
648         mInstrumentation = getInstrumentation();
649         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
650         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
651                 R.layout.horizontal_grid);
652         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
653         initActivity(intent);
654         mOrientation = BaseGridView.HORIZONTAL;
655         mNumRows = 3;
656 
657         mGridView.setSelectedPositionSmooth(150);
658         waitForScrollIdle(mVerifyLayout);
659         mActivity.swap(150, 152);
660         waitForTransientStateGone(null);
661 
662         runTestOnUiThread(mVerifyLayout);
663 
664         scrollToBegin(mVerifyLayout);
665 
666         verifyBeginAligned();
667     }
668 
testItemMovedVertical()669     public void testItemMovedVertical() throws Throwable {
670 
671         mInstrumentation = getInstrumentation();
672         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
673         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
674                 R.layout.vertical_grid);
675         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
676         initActivity(intent);
677         mOrientation = BaseGridView.VERTICAL;
678         mNumRows = 3;
679 
680         mGridView.setSelectedPositionSmooth(150);
681         waitForScrollIdle(mVerifyLayout);
682         mActivity.swap(150, 152);
683         waitForTransientStateGone(null);
684 
685         runTestOnUiThread(mVerifyLayout);
686 
687         scrollToEnd(mVerifyLayout);
688         scrollToBegin(mVerifyLayout);
689 
690         verifyBeginAligned();
691     }
692 
testItemAddRemoveHorizontal()693     public void testItemAddRemoveHorizontal() throws Throwable {
694 
695         mInstrumentation = getInstrumentation();
696         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
697         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
698                 R.layout.horizontal_grid);
699         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
700         initActivity(intent);
701         mOrientation = BaseGridView.HORIZONTAL;
702         mNumRows = 3;
703 
704         scrollToEnd(mVerifyLayout);
705         int[] endEdges = getEndEdges();
706 
707         mGridView.setSelectedPositionSmooth(150);
708         waitForScrollIdle(mVerifyLayout);
709         int[] removedItems = mActivity.removeItems(151, 4);
710         waitForTransientStateGone(null);
711 
712         scrollToEnd(mVerifyLayout);
713         mGridView.setSelectedPositionSmooth(150);
714         waitForScrollIdle(mVerifyLayout);
715 
716         mActivity.addItems(151, removedItems);
717         waitForTransientStateGone(null);
718         scrollToEnd(mVerifyLayout);
719 
720         // we should get same aligned end edges
721         int[] endEdges2 = getEndEdges();
722         verifyEdgesSame(endEdges, endEdges2);
723 
724         scrollToBegin(mVerifyLayout);
725         verifyBeginAligned();
726     }
727 
testSetSelectedPositionDetached()728     public void testSetSelectedPositionDetached() throws Throwable {
729 
730         mInstrumentation = getInstrumentation();
731         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
732         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
733                 R.layout.horizontal_linear);
734         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50);
735         initActivity(intent);
736         mOrientation = BaseGridView.HORIZONTAL;
737         mNumRows = 1;
738 
739         final int focusToIndex = 49;
740         final ViewGroup parent = (ViewGroup) mGridView.getParent();
741         runTestOnUiThread(new Runnable() {
742             public void run() {
743                 parent.removeView(mGridView);
744             }
745         });
746         runTestOnUiThread(new Runnable() {
747             public void run() {
748                 mGridView.setSelectedPositionSmooth(focusToIndex);
749             }
750         });
751         runTestOnUiThread(new Runnable() {
752             public void run() {
753                 parent.addView(mGridView);
754                 mGridView.requestFocus();
755             }
756         });
757         waitForTransientStateGone(null);
758         waitForScrollIdle();
759         assertEquals(mGridView.getSelectedPosition(), focusToIndex);
760         assertTrue(mGridView.getLayoutManager().findViewByPosition(focusToIndex).hasFocus());
761 
762         final int focusToIndex2 = 0;
763         runTestOnUiThread(new Runnable() {
764             public void run() {
765                 parent.removeView(mGridView);
766             }
767         });
768         runTestOnUiThread(new Runnable() {
769             public void run() {
770                 mGridView.setSelectedPosition(focusToIndex2);
771             }
772         });
773         runTestOnUiThread(new Runnable() {
774             public void run() {
775                 parent.addView(mGridView);
776                 mGridView.requestFocus();
777             }
778         });
779         assertEquals(mGridView.getSelectedPosition(), focusToIndex2);
780         waitForTransientStateGone(null);
781         waitForScrollIdle();
782         assertTrue(mGridView.getLayoutManager().findViewByPosition(focusToIndex2).hasFocus());
783     }
784 
testBug22209986()785     public void testBug22209986() throws Throwable {
786 
787         mInstrumentation = getInstrumentation();
788         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
789         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
790                 R.layout.horizontal_linear);
791         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50);
792         initActivity(intent);
793         mOrientation = BaseGridView.HORIZONTAL;
794         mNumRows = 1;
795 
796         final int focusToIndex = mGridView.getChildCount() - 1;
797         runTestOnUiThread(new Runnable() {
798             public void run() {
799                 mGridView.setSelectedPositionSmooth(focusToIndex);
800             }
801         });
802 
803         waitForTransientStateGone(null);
804         waitForScrollIdle();
805         runTestOnUiThread(new Runnable() {
806             public void run() {
807                 mGridView.setSelectedPositionSmooth(focusToIndex + 1);
808             }
809         });
810         // let the scroll running for a while and requestLayout during scroll
811         Thread.sleep(80);
812         runTestOnUiThread(new Runnable() {
813             public void run() {
814                 assertEquals(mGridView.getScrollState(), BaseGridView.SCROLL_STATE_SETTLING);
815                 mGridView.requestLayout();
816             }
817         });
818         waitForTransientStateGone(null);
819         waitForScrollIdle();
820 
821         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft();
822 
823         runTestOnUiThread(new Runnable() {
824             public void run() {
825                 mGridView.requestLayout();
826             }
827         });
828         waitForTransientStateGone(null);
829         waitForScrollIdle();
830         assertEquals(leftEdge,
831                 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft());
832     }
833 
testScrollAndRemove()834     public void testScrollAndRemove() throws Throwable {
835 
836         mInstrumentation = getInstrumentation();
837         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
838         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
839                 R.layout.horizontal_linear);
840         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50);
841         initActivity(intent);
842         mOrientation = BaseGridView.HORIZONTAL;
843         mNumRows = 1;
844 
845         final int focusToIndex = mGridView.getChildCount() - 1;
846         runTestOnUiThread(new Runnable() {
847             public void run() {
848                 mGridView.setSelectedPositionSmooth(focusToIndex);
849             }
850         });
851 
852         runTestOnUiThread(new Runnable() {
853             public void run() {
854                 mActivity.removeItems(focusToIndex, 1);
855             }
856         });
857 
858         waitForTransientStateGone(null);
859         waitForScrollIdle();
860         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft();
861 
862         runTestOnUiThread(new Runnable() {
863             public void run() {
864                 mGridView.requestLayout();
865             }
866         });
867         waitForTransientStateGone(null);
868         waitForScrollIdle();
869         assertEquals(leftEdge,
870                 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft());
871     }
872 
testScrollAndInsert()873     public void testScrollAndInsert() throws Throwable {
874 
875         mInstrumentation = getInstrumentation();
876         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
877         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
878                 R.layout.vertical_grid);
879         int[] items = new int[1000];
880         for (int i = 0; i < items.length; i++) {
881             items[i] = 300 + (int)(Math.random() * 100);
882         }
883         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
884         intent.putExtra(GridActivity.EXTRA_STAGGERED, true);
885         mOrientation = BaseGridView.VERTICAL;
886         mNumRows = 3;
887 
888         initActivity(intent);
889 
890         runTestOnUiThread(new Runnable() {
891             public void run() {
892                 mGridView.setSelectedPositionSmooth(150);
893             }
894         });
895         waitForScrollIdle(mVerifyLayout);
896 
897         View view =  mGridView.getChildAt(mGridView.getChildCount() - 1);
898         final int focusToIndex = mGridView.getChildAdapterPosition(view);
899         runTestOnUiThread(new Runnable() {
900             public void run() {
901                 mGridView.setSelectedPositionSmooth(focusToIndex);
902             }
903         });
904 
905         runTestOnUiThread(new Runnable() {
906             public void run() {
907                 int[] newItems = new int[]{300, 300, 300};
908                 mActivity.addItems(0, newItems);
909             }
910         });
911 
912         waitForTransientStateGone(null);
913         waitForScrollIdle();
914     }
915 
testScrollAndInsertBeforeVisibleItem()916     public void testScrollAndInsertBeforeVisibleItem() throws Throwable {
917 
918         mInstrumentation = getInstrumentation();
919         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
920         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
921                 R.layout.vertical_grid);
922         int[] items = new int[1000];
923         for (int i = 0; i < items.length; i++) {
924             items[i] = 300 + (int)(Math.random() * 100);
925         }
926         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
927         intent.putExtra(GridActivity.EXTRA_STAGGERED, true);
928         mOrientation = BaseGridView.VERTICAL;
929         mNumRows = 3;
930 
931         initActivity(intent);
932 
933         runTestOnUiThread(new Runnable() {
934             public void run() {
935                 mGridView.setSelectedPositionSmooth(150);
936             }
937         });
938         waitForScrollIdle(mVerifyLayout);
939 
940         View view =  mGridView.getChildAt(mGridView.getChildCount() - 1);
941         final int focusToIndex = mGridView.getChildAdapterPosition(view);
942         runTestOnUiThread(new Runnable() {
943             public void run() {
944                 mGridView.setSelectedPositionSmooth(focusToIndex);
945             }
946         });
947 
948         runTestOnUiThread(new Runnable() {
949             public void run() {
950                 int[] newItems = new int[]{300, 300, 300};
951                 mActivity.addItems(focusToIndex, newItems);
952             }
953         });
954 
955         waitForTransientStateGone(null);
956         waitForScrollIdle();
957     }
958 
testSmoothScrollAndRemove()959     public void testSmoothScrollAndRemove() throws Throwable {
960 
961         mInstrumentation = getInstrumentation();
962         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
963         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
964                 R.layout.horizontal_linear);
965         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50);
966         initActivity(intent);
967         mOrientation = BaseGridView.HORIZONTAL;
968         mNumRows = 1;
969 
970         final int focusToIndex = 40;
971         runTestOnUiThread(new Runnable() {
972             public void run() {
973                 mGridView.setSelectedPositionSmooth(focusToIndex);
974             }
975         });
976 
977         runTestOnUiThread(new Runnable() {
978             public void run() {
979                 mActivity.removeItems(focusToIndex, 1);
980             }
981         });
982 
983         Thread.sleep(20); // wait for layout
984         assertTrue("removing the index of not attached child should not affect smooth scroller",
985                 mGridView.getLayoutManager().isSmoothScrolling());
986         waitForTransientStateGone(null);
987         waitForScrollIdle();
988         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft();
989 
990         runTestOnUiThread(new Runnable() {
991             public void run() {
992                 mGridView.requestLayout();
993             }
994         });
995         waitForTransientStateGone(null);
996         waitForScrollIdle();
997         assertEquals(leftEdge,
998                 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft());
999     }
1000 
testSmoothScrollAndRemove2()1001     public void testSmoothScrollAndRemove2() throws Throwable {
1002 
1003         mInstrumentation = getInstrumentation();
1004         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1005         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1006                 R.layout.horizontal_linear);
1007         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50);
1008         initActivity(intent);
1009         mOrientation = BaseGridView.HORIZONTAL;
1010         mNumRows = 1;
1011 
1012         final int focusToIndex = 40;
1013         runTestOnUiThread(new Runnable() {
1014             public void run() {
1015                 mGridView.setSelectedPositionSmooth(focusToIndex);
1016             }
1017         });
1018 
1019         final int removeIndex = mGridView.getChildCount() - 1;
1020         runTestOnUiThread(new Runnable() {
1021             public void run() {
1022                 mActivity.removeItems(removeIndex, 1);
1023             }
1024         });
1025 
1026         Thread.sleep(20); // wait for layout
1027         assertFalse("removing the index of attached child should kill smooth scroller",
1028                 mGridView.getLayoutManager().isSmoothScrolling());
1029         waitForTransientStateGone(null);
1030         waitForScrollIdle();
1031         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft();
1032 
1033         runTestOnUiThread(new Runnable() {
1034             public void run() {
1035                 mGridView.requestLayout();
1036             }
1037         });
1038         waitForTransientStateGone(null);
1039         waitForScrollIdle();
1040         assertEquals(leftEdge,
1041                 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft());
1042     }
1043 
testPendingSmoothScrollAndRemove()1044     public void testPendingSmoothScrollAndRemove() throws Throwable {
1045         mInstrumentation = getInstrumentation();
1046         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1047         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1048                 R.layout.vertical_linear);
1049         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
1050         int[] items = new int[100];
1051         for (int i = 0; i < items.length; i++) {
1052             items[i] = 630 + (int)(Math.random() * 100);
1053         }
1054         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
1055         intent.putExtra(GridActivity.EXTRA_STAGGERED, true);
1056         mOrientation = BaseGridView.VERTICAL;
1057         mNumRows = 1;
1058 
1059         initActivity(intent);
1060 
1061         mGridView.setSelectedPositionSmooth(0);
1062         waitForScrollIdle(mVerifyLayout);
1063         assertTrue(mGridView.getChildAt(0).hasFocus());
1064 
1065         // Pressing lots of key to make sure smooth scroller is running
1066         for (int i = 0; i < 20; i++) {
1067             sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
1068         }
1069         Thread.sleep(100);
1070 
1071         assertTrue(mGridView.getLayoutManager().isSmoothScrolling());
1072         final int removeIndex = mGridView.getChildCount() - 1;
1073         runTestOnUiThread(new Runnable() {
1074             public void run() {
1075                 mActivity.removeItems(removeIndex, 1);
1076             }
1077         });
1078 
1079         Thread.sleep(20); // wait for layout
1080         assertFalse("removing the index of attached child should kill smooth scroller",
1081                 mGridView.getLayoutManager().isSmoothScrolling());
1082 
1083         waitForTransientStateGone(null);
1084         waitForScrollIdle();
1085         int focusIndex = mGridView.getSelectedPosition();
1086         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusIndex).getLeft();
1087 
1088         runTestOnUiThread(new Runnable() {
1089             public void run() {
1090                 mGridView.requestLayout();
1091             }
1092         });
1093         waitForTransientStateGone(null);
1094         waitForScrollIdle();
1095         assertEquals(leftEdge,
1096                 mGridView.getLayoutManager().findViewByPosition(focusIndex).getLeft());
1097     }
1098 
testFocusToFirstItem()1099     public void testFocusToFirstItem() throws Throwable {
1100 
1101         mInstrumentation = getInstrumentation();
1102         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1103         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1104                 R.layout.horizontal_grid);
1105         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
1106         initActivity(intent);
1107         mOrientation = BaseGridView.HORIZONTAL;
1108         mNumRows = 3;
1109 
1110         int[] removedItems = mActivity.removeItems(0, 200);
1111 
1112         waitForTransientStateGone(null);
1113         humanDelay(500);
1114         mActivity.addItems(0, removedItems);
1115 
1116         waitForTransientStateGone(null);
1117         humanDelay(500);
1118         assertTrue(mGridView.getLayoutManager().findViewByPosition(0).hasFocus());
1119 
1120         changeArraySize(0);
1121 
1122         changeArraySize(200);
1123         assertTrue(mGridView.getLayoutManager().findViewByPosition(0).hasFocus());
1124     }
1125 
testNonFocusableHorizontal()1126     public void testNonFocusableHorizontal() throws Throwable {
1127         final int numItems = 200;
1128         final int startPos = 45;
1129         final int skips = 20;
1130         final int numColumns = 3;
1131         final int endPos = startPos + numColumns * (skips + 1);
1132 
1133         mInstrumentation = getInstrumentation();
1134         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1135         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1136                 R.layout.horizontal_grid);
1137         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
1138         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1139         mOrientation = BaseGridView.HORIZONTAL;
1140         mNumRows = numColumns;
1141         boolean[] focusable = new boolean[numItems];
1142         for (int i = 0; i < focusable.length; i++) {
1143             focusable[i] = true;
1144         }
1145         for (int i = startPos + mNumRows, j = 0; j < skips; i += mNumRows, j++) {
1146             focusable[i] = false;
1147         }
1148         intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
1149         initActivity(intent);
1150 
1151         mGridView.setSelectedPositionSmooth(startPos);
1152         waitForScrollIdle(mVerifyLayout);
1153 
1154         if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
1155             sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
1156         } else {
1157             sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
1158         }
1159         waitForScrollIdle(mVerifyLayout);
1160         assertEquals(endPos, mGridView.getSelectedPosition());
1161 
1162         if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
1163             sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
1164         } else {
1165             sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
1166         }
1167         waitForScrollIdle(mVerifyLayout);
1168         assertEquals(startPos, mGridView.getSelectedPosition());
1169 
1170     }
1171 
testNoInitialFocusable()1172     public void testNoInitialFocusable() throws Throwable {
1173 
1174         mInstrumentation = getInstrumentation();
1175         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1176         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1177                 R.layout.horizontal_linear);
1178         final int numItems = 100;
1179         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
1180         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1181         mOrientation = BaseGridView.HORIZONTAL;
1182         mNumRows = 1;
1183         boolean[] focusable = new boolean[numItems];
1184         final int firstFocusableIndex = 10;
1185         for (int i = 0; i < firstFocusableIndex; i++) {
1186             focusable[i] = false;
1187         }
1188         for (int i = firstFocusableIndex; i < focusable.length; i++) {
1189             focusable[i] = true;
1190         }
1191         intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
1192         initActivity(intent);
1193         assertTrue(mGridView.isFocused());
1194 
1195         if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
1196             sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
1197         } else {
1198             sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
1199         }
1200         waitForScrollIdle(mVerifyLayout);
1201         assertEquals(firstFocusableIndex, mGridView.getSelectedPosition());
1202         assertTrue(mGridView.getLayoutManager().findViewByPosition(firstFocusableIndex).hasFocus());
1203     }
1204 
testFocusOutOfEmptyListView()1205     public void testFocusOutOfEmptyListView() throws Throwable {
1206 
1207         mInstrumentation = getInstrumentation();
1208         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1209         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1210                 R.layout.horizontal_linear);
1211         final int numItems = 100;
1212         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
1213         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1214         mOrientation = BaseGridView.HORIZONTAL;
1215         mNumRows = 1;
1216         initActivity(intent);
1217 
1218         final View horizontalGridView = new HorizontalGridViewEx(mGridView.getContext());
1219         runTestOnUiThread(new Runnable() {
1220             @Override
1221             public void run() {
1222                 horizontalGridView.setFocusable(true);
1223                 horizontalGridView.setFocusableInTouchMode(true);
1224                 horizontalGridView.setLayoutParams(new ViewGroup.LayoutParams(100, 100));
1225                 ((ViewGroup) mGridView.getParent()).addView(horizontalGridView, 0);
1226                 horizontalGridView.requestFocus();
1227             }
1228         });
1229 
1230         assertTrue(horizontalGridView.isFocused());
1231 
1232         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
1233 
1234         assertTrue(mGridView.hasFocus());
1235     }
1236 
testTransferFocusToChildWhenGainFocus()1237     public void testTransferFocusToChildWhenGainFocus() throws Throwable {
1238 
1239         mInstrumentation = getInstrumentation();
1240         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1241         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1242                 R.layout.horizontal_linear);
1243         final int numItems = 100;
1244         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
1245         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1246         mOrientation = BaseGridView.HORIZONTAL;
1247         mNumRows = 1;
1248         boolean[] focusable = new boolean[numItems];
1249         final int firstFocusableIndex = 1;
1250         for (int i = 0; i < firstFocusableIndex; i++) {
1251             focusable[i] = false;
1252         }
1253         for (int i = firstFocusableIndex; i < focusable.length; i++) {
1254             focusable[i] = true;
1255         }
1256         intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
1257         initActivity(intent);
1258 
1259         assertEquals(firstFocusableIndex, mGridView.getSelectedPosition());
1260         assertTrue(mGridView.getLayoutManager().findViewByPosition(firstFocusableIndex).hasFocus());
1261     }
1262 
testFocusFromSecondChild()1263     public void testFocusFromSecondChild() throws Throwable {
1264 
1265         mInstrumentation = getInstrumentation();
1266         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1267         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1268                 R.layout.horizontal_linear);
1269         final int numItems = 100;
1270         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
1271         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1272         mOrientation = BaseGridView.HORIZONTAL;
1273         mNumRows = 1;
1274         boolean[] focusable = new boolean[numItems];
1275         for (int i = 0; i < focusable.length; i++) {
1276             focusable[i] = false;
1277         }
1278         intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
1279         initActivity(intent);
1280 
1281         // switching Adapter to cause a full rebind,  test if it will focus to second item.
1282         runTestOnUiThread(new Runnable() {
1283             @Override
1284             public void run() {
1285                 mActivity.mNumItems = numItems;
1286                 mActivity.mItemFocusables[1] = true;
1287                 mActivity.rebindToNewAdapter();
1288             }
1289         });
1290     }
1291 
testNonFocusableVertical()1292     public void testNonFocusableVertical() throws Throwable {
1293         final int numItems = 200;
1294         final int startPos = 44;
1295         final int skips = 20;
1296         final int numColumns = 3;
1297         final int endPos = startPos + numColumns * (skips + 1);
1298 
1299         mInstrumentation = getInstrumentation();
1300         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1301         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1302                 R.layout.vertical_grid);
1303         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
1304         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1305         mOrientation = BaseGridView.VERTICAL;
1306         mNumRows = numColumns;
1307         boolean[] focusable = new boolean[numItems];
1308         for (int i = 0; i < focusable.length; i++) {
1309             focusable[i] = true;
1310         }
1311         for (int i = startPos + mNumRows, j = 0; j < skips; i += mNumRows, j++) {
1312             focusable[i] = false;
1313         }
1314         intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
1315         initActivity(intent);
1316 
1317         mGridView.setSelectedPositionSmooth(startPos);
1318         waitForScrollIdle(mVerifyLayout);
1319 
1320         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
1321         waitForScrollIdle(mVerifyLayout);
1322         assertEquals(endPos, mGridView.getSelectedPosition());
1323 
1324         sendKeys(KeyEvent.KEYCODE_DPAD_UP);
1325         waitForScrollIdle(mVerifyLayout);
1326         assertEquals(startPos, mGridView.getSelectedPosition());
1327 
1328     }
1329 
testLtrFocusOutStartDisabled()1330     public void testLtrFocusOutStartDisabled() throws Throwable {
1331         final int numItems = 200;
1332 
1333         mInstrumentation = getInstrumentation();
1334         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1335         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid_ltr);
1336         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
1337         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1338         mOrientation = BaseGridView.VERTICAL;
1339         mNumRows = 1;
1340         initActivity(intent);
1341 
1342         runTestOnUiThread(new Runnable() {
1343             @Override
1344             public void run() {
1345                 mGridView.requestFocus();
1346                 mGridView.setSelectedPositionSmooth(0);
1347             }
1348         });
1349         waitForScrollIdle(mVerifyLayout);
1350 
1351         sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
1352         waitForScrollIdle(mVerifyLayout);
1353         assertTrue(mGridView.hasFocus());
1354     }
1355 
testRtlFocusOutStartDisabled()1356     public void testRtlFocusOutStartDisabled() throws Throwable {
1357         final int numItems = 200;
1358 
1359         mInstrumentation = getInstrumentation();
1360         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1361         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid_rtl);
1362         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
1363         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1364         mOrientation = BaseGridView.VERTICAL;
1365         mNumRows = 1;
1366         initActivity(intent);
1367 
1368         runTestOnUiThread(new Runnable() {
1369             @Override
1370             public void run() {
1371                 mGridView.requestFocus();
1372                 mGridView.setSelectedPositionSmooth(0);
1373             }
1374         });
1375         waitForScrollIdle(mVerifyLayout);
1376 
1377         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
1378         waitForScrollIdle(mVerifyLayout);
1379         assertTrue(mGridView.hasFocus());
1380     }
1381 
testTransferFocusable()1382     public void testTransferFocusable() throws Throwable {
1383         final int numItems = 200;
1384         final int numColumns = 3;
1385         final int startPos = 1;
1386 
1387         mInstrumentation = getInstrumentation();
1388         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1389         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1390                 R.layout.horizontal_grid);
1391         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
1392         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1393         mOrientation = BaseGridView.HORIZONTAL;
1394         mNumRows = numColumns;
1395         boolean[] focusable = new boolean[numItems];
1396         for (int i = 0; i < focusable.length; i++) {
1397             focusable[i] = true;
1398         }
1399         for (int i = 0; i < startPos; i++) {
1400             focusable[i] = false;
1401         }
1402         intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
1403         initActivity(intent);
1404 
1405         changeArraySize(0);
1406         assertTrue(mGridView.isFocused());
1407 
1408         changeArraySize(numItems);
1409         assertTrue(mGridView.getLayoutManager().findViewByPosition(startPos).hasFocus());
1410     }
1411 
testTransferFocusable2()1412     public void testTransferFocusable2() throws Throwable {
1413         final int numItems = 200;
1414         final int numColumns = 3;
1415         final int startPos = 10;
1416 
1417         mInstrumentation = getInstrumentation();
1418         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1419         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1420                 R.layout.horizontal_grid);
1421         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
1422         intent.putExtra(GridActivity.EXTRA_STAGGERED, true);
1423         mOrientation = BaseGridView.HORIZONTAL;
1424         mNumRows = numColumns;
1425         boolean[] focusable = new boolean[numItems];
1426         for (int i = 0; i < focusable.length; i++) {
1427             focusable[i] = true;
1428         }
1429         for (int i = 0; i < startPos; i++) {
1430             focusable[i] = false;
1431         }
1432         intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
1433         initActivity(intent);
1434 
1435         changeArraySize(0);
1436         assertTrue(mGridView.isFocused());
1437 
1438         changeArraySize(numItems);
1439         assertTrue(mGridView.getLayoutManager().findViewByPosition(startPos).hasFocus());
1440     }
1441 
testNonFocusableLoseInFastLayout()1442     public void testNonFocusableLoseInFastLayout() throws Throwable {
1443         mInstrumentation = getInstrumentation();
1444         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1445         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1446                 R.layout.vertical_linear);
1447         int[] items = new int[300];
1448         for (int i = 0; i < items.length; i++) {
1449             items[i] = 480;
1450         }
1451         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
1452         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1453         intent.putExtra(GridActivity.EXTRA_REQUEST_LAYOUT_ONFOCUS, true);
1454         mOrientation = BaseGridView.VERTICAL;
1455         mNumRows = 1;
1456         int pressDown = 15;
1457 
1458         initActivity(intent);
1459 
1460         mGridView.setSelectedPositionSmooth(0);
1461         waitForScrollIdleAndItemAnimation(mVerifyLayout);
1462 
1463         for (int i = 0; i < pressDown; i++) {
1464             sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
1465         }
1466         waitForScrollIdleAndItemAnimation(mVerifyLayout);
1467         assertFalse(mGridView.isFocused());
1468 
1469     }
1470 
testSetSelectionWithDelta()1471     public void testSetSelectionWithDelta() throws Throwable {
1472         mInstrumentation = getInstrumentation();
1473         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1474         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1475                 R.layout.vertical_linear);
1476         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 300);
1477         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1478         mOrientation = BaseGridView.VERTICAL;
1479         mNumRows = 1;
1480 
1481         initActivity(intent);
1482 
1483         runTestOnUiThread(new Runnable() {
1484             public void run() {
1485                 mGridView.setSelectedPositionSmooth(3);
1486             }
1487         });
1488         waitForScrollIdle(mVerifyLayout);
1489         int top1 = mGridView.getLayoutManager().findViewByPosition(3).getTop();
1490 
1491         humanDelay(1000);
1492 
1493         // scroll to position with delta
1494         setSelectedPosition(3, 100);
1495         int top2 = mGridView.getLayoutManager().findViewByPosition(3).getTop();
1496         assertEquals(top1 - 100, top2);
1497 
1498         // scroll to same position without delta, it will be reset
1499         setSelectedPosition(3, 0);
1500         int top3 = mGridView.getLayoutManager().findViewByPosition(3).getTop();
1501         assertEquals(top1, top3);
1502 
1503         // scroll invisible item after last visible item
1504         final int lastVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager())
1505                 .mGrid.getLastVisibleIndex();
1506         setSelectedPosition(lastVisiblePos + 1, 100);
1507         int top4 = mGridView.getLayoutManager().findViewByPosition(lastVisiblePos + 1).getTop();
1508         assertEquals(top1 - 100, top4);
1509 
1510         // scroll invisible item before first visible item
1511         final int firstVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager())
1512                 .mGrid.getFirstVisibleIndex();
1513         setSelectedPosition(firstVisiblePos - 1, 100);
1514         int top5 = mGridView.getLayoutManager().findViewByPosition(firstVisiblePos - 1).getTop();
1515         assertEquals(top1 - 100, top5);
1516 
1517         // scroll to invisible item that is far away.
1518         setSelectedPosition(50, 100);
1519         int top6 = mGridView.getLayoutManager().findViewByPosition(50).getTop();
1520         assertEquals(top1 - 100, top6);
1521 
1522         // scroll to invisible item that is far away.
1523         runTestOnUiThread(new Runnable() {
1524             public void run() {
1525                 mGridView.setSelectedPositionSmooth(100);
1526             }
1527         });
1528         waitForScrollIdle(mVerifyLayout);
1529         int top7 = mGridView.getLayoutManager().findViewByPosition(100).getTop();
1530         assertEquals(top1, top7);
1531 
1532         // scroll to invisible item that is far away.
1533         setSelectedPosition(10, 50);
1534         int top8 = mGridView.getLayoutManager().findViewByPosition(10).getTop();
1535         assertEquals(top1 - 50, top8);
1536     }
1537 
testSetSelectionWithDeltaInGrid()1538     public void testSetSelectionWithDeltaInGrid() throws Throwable {
1539         mInstrumentation = getInstrumentation();
1540         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1541         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1542                 R.layout.vertical_grid);
1543         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 500);
1544         intent.putExtra(GridActivity.EXTRA_STAGGERED, true);
1545         mOrientation = BaseGridView.VERTICAL;
1546         mNumRows = 3;
1547 
1548         initActivity(intent);
1549 
1550         runTestOnUiThread(new Runnable() {
1551             public void run() {
1552                 mGridView.setSelectedPositionSmooth(10);
1553             }
1554         });
1555         waitForScrollIdle(mVerifyLayout);
1556         int top1 = getCenterY(mGridView.getLayoutManager().findViewByPosition(10));
1557 
1558         humanDelay(500);
1559 
1560         // scroll to position with delta
1561         setSelectedPosition(20, 100);
1562         int top2 = getCenterY(mGridView.getLayoutManager().findViewByPosition(20));
1563         assertEquals(top1 - 100, top2);
1564 
1565         // scroll to same position without delta, it will be reset
1566         setSelectedPosition(20, 0);
1567         int top3 = getCenterY(mGridView.getLayoutManager().findViewByPosition(20));
1568         assertEquals(top1, top3);
1569 
1570         // scroll invisible item after last visible item
1571         final int lastVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager())
1572                 .mGrid.getLastVisibleIndex();
1573         setSelectedPosition(lastVisiblePos + 1, 100);
1574         int top4 = getCenterY(mGridView.getLayoutManager().findViewByPosition(lastVisiblePos + 1));
1575         verifyMargin();
1576         assertEquals(top1 - 100, top4);
1577 
1578         // scroll invisible item before first visible item
1579         final int firstVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager())
1580                 .mGrid.getFirstVisibleIndex();
1581         setSelectedPosition(firstVisiblePos - 1, 100);
1582         int top5 = getCenterY(mGridView.getLayoutManager().findViewByPosition(firstVisiblePos - 1));
1583         assertEquals(top1 - 100, top5);
1584 
1585         // scroll to invisible item that is far away.
1586         setSelectedPosition(100, 100);
1587         int top6 = getCenterY(mGridView.getLayoutManager().findViewByPosition(100));
1588         assertEquals(top1 - 100, top6);
1589 
1590         // scroll to invisible item that is far away.
1591         runTestOnUiThread(new Runnable() {
1592             public void run() {
1593                 mGridView.setSelectedPositionSmooth(200);
1594             }
1595         });
1596         waitForScrollIdle(mVerifyLayout);
1597         Thread.sleep(500);
1598         int top7 = getCenterY(mGridView.getLayoutManager().findViewByPosition(200));
1599         assertEquals(top1, top7);
1600 
1601         // scroll to invisible item that is far away.
1602         setSelectedPosition(10, 50);
1603         int top8 = getCenterY(mGridView.getLayoutManager().findViewByPosition(10));
1604         assertEquals(top1 - 50, top8);
1605     }
1606 
1607 
testSetSelectionWithDeltaInGrid1()1608     public void testSetSelectionWithDeltaInGrid1() throws Throwable {
1609         mInstrumentation = getInstrumentation();
1610         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1611         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1612                 R.layout.vertical_grid);
1613         intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{
1614                 193,176,153,141,203,184,232,139,177,206,222,136,132,237,172,137,
1615                 188,172,163,213,158,219,209,147,133,229,170,197,138,215,188,205,
1616                 223,192,225,170,195,127,229,229,210,195,134,142,160,139,130,222,
1617                 150,163,180,176,157,137,234,169,159,167,182,150,224,231,202,236,
1618                 123,140,181,223,120,185,183,221,123,210,134,158,166,208,149,128,
1619                 192,214,212,198,133,140,158,133,229,173,226,141,180,128,127,218,
1620                 192,235,183,213,216,150,143,193,125,141,219,210,195,195,192,191,
1621                 212,236,157,189,160,220,147,158,220,199,233,231,201,180,168,141,
1622                 156,204,191,183,190,153,123,210,238,151,139,221,223,200,175,191,
1623                 132,184,197,204,236,157,230,151,195,219,212,143,172,149,219,184,
1624                 164,211,132,187,172,142,174,146,127,147,206,238,188,129,199,226,
1625                 132,220,210,159,235,153,208,182,196,123,180,159,131,135,175,226,
1626                 127,134,237,211,133,225,132,124,160,226,224,200,173,137,217,169,
1627                 182,183,176,185,122,168,195,159,172,129,126,129,166,136,149,220,
1628                 178,191,192,238,180,208,234,154,222,206,239,228,129,140,203,125,
1629                 214,175,125,169,196,132,234,138,192,142,234,190,215,232,239,122,
1630                 188,158,128,221,159,237,207,157,232,138,132,214,122,199,121,191,
1631                 199,209,126,164,175,187,173,186,194,224,191,196,146,208,213,210,
1632                 164,176,202,213,123,157,179,138,217,129,186,166,237,211,157,130,
1633                 137,132,171,232,216,239,180,151,137,132,190,133,218,155,171,227,
1634                 193,147,197,164,120,218,193,154,170,196,138,222,161,235,143,154,
1635                 192,178,228,195,178,133,203,178,173,206,178,212,136,157,169,124,
1636                 172,121,128,223,238,125,217,187,184,156,169,215,231,124,210,174,
1637                 146,226,185,134,223,228,183,182,136,133,199,146,180,233,226,225,
1638                 174,233,145,235,216,170,192,171,132,132,134,223,233,148,154,162,
1639                 192,179,197,203,139,197,174,187,135,132,180,136,192,195,124,221,
1640                 120,189,233,233,146,225,234,163,215,143,132,198,156,205,151,190,
1641                 204,239,221,229,123,138,134,217,219,136,218,215,167,139,195,125,
1642                 202,225,178,226,145,208,130,194,228,197,157,215,124,147,174,123,
1643                 237,140,172,181,161,151,229,216,199,199,179,213,146,122,222,162,
1644                 139,173,165,150,160,217,207,137,165,175,129,158,134,133,178,199,
1645                 215,213,122,197
1646         });
1647         intent.putExtra(GridActivity.EXTRA_STAGGERED, true);
1648         mOrientation = BaseGridView.VERTICAL;
1649         mNumRows = 3;
1650 
1651         initActivity(intent);
1652 
1653         runTestOnUiThread(new Runnable() {
1654             public void run() {
1655                 mGridView.setSelectedPositionSmooth(10);
1656             }
1657         });
1658         waitForScrollIdle(mVerifyLayout);
1659         int top1 = getCenterY(mGridView.getLayoutManager().findViewByPosition(10));
1660 
1661         humanDelay(500);
1662 
1663         // scroll to position with delta
1664         setSelectedPosition(20, 100);
1665         int top2 = getCenterY(mGridView.getLayoutManager().findViewByPosition(20));
1666         assertEquals(top1 - 100, top2);
1667 
1668         // scroll to same position without delta, it will be reset
1669         setSelectedPosition(20, 0);
1670         int top3 = getCenterY(mGridView.getLayoutManager().findViewByPosition(20));
1671         assertEquals(top1, top3);
1672 
1673         // scroll invisible item after last visible item
1674         final int lastVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager())
1675                 .mGrid.getLastVisibleIndex();
1676         setSelectedPosition(lastVisiblePos + 1, 100);
1677         int top4 = getCenterY(mGridView.getLayoutManager().findViewByPosition(lastVisiblePos + 1));
1678         verifyMargin();
1679         assertEquals(top1 - 100, top4);
1680 
1681         // scroll invisible item before first visible item
1682         final int firstVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager())
1683                 .mGrid.getFirstVisibleIndex();
1684         setSelectedPosition(firstVisiblePos - 1, 100);
1685         int top5 = getCenterY(mGridView.getLayoutManager().findViewByPosition(firstVisiblePos - 1));
1686         assertEquals(top1 - 100, top5);
1687 
1688         // scroll to invisible item that is far away.
1689         setSelectedPosition(100, 100);
1690         int top6 = getCenterY(mGridView.getLayoutManager().findViewByPosition(100));
1691         assertEquals(top1 - 100, top6);
1692 
1693         // scroll to invisible item that is far away.
1694         runTestOnUiThread(new Runnable() {
1695             public void run() {
1696                 mGridView.setSelectedPositionSmooth(200);
1697             }
1698         });
1699         waitForScrollIdle(mVerifyLayout);
1700         Thread.sleep(500);
1701         int top7 = getCenterY(mGridView.getLayoutManager().findViewByPosition(200));
1702         assertEquals(top1, top7);
1703 
1704         // scroll to invisible item that is far away.
1705         setSelectedPosition(10, 50);
1706         int top8 = getCenterY(mGridView.getLayoutManager().findViewByPosition(10));
1707         assertEquals(top1 - 50, top8);
1708     }
1709 
testSmoothScrollSelectionEvents()1710     public void testSmoothScrollSelectionEvents() throws Throwable {
1711         mInstrumentation = getInstrumentation();
1712         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1713         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1714                 R.layout.vertical_grid);
1715         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 500);
1716         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1717         mOrientation = BaseGridView.VERTICAL;
1718         mNumRows = 3;
1719         initActivity(intent);
1720 
1721         runTestOnUiThread(new Runnable() {
1722             public void run() {
1723                 mGridView.setSelectedPositionSmooth(30);
1724             }
1725         });
1726         waitForScrollIdle(mVerifyLayout);
1727         humanDelay(500);
1728 
1729         final ArrayList<Integer> selectedPositions = new ArrayList<Integer>();
1730         mGridView.setOnChildSelectedListener(new OnChildSelectedListener() {
1731             @Override
1732             public void onChildSelected(ViewGroup parent, View view, int position, long id) {
1733                 selectedPositions.add(position);
1734             }
1735         });
1736 
1737         sendRepeatedKeys(10, KeyEvent.KEYCODE_DPAD_UP);
1738         humanDelay(500);
1739         waitForScrollIdle(mVerifyLayout);
1740         // should only get childselected event for item 0 once
1741         assertTrue(selectedPositions.size() > 0);
1742         assertEquals(0, selectedPositions.get(selectedPositions.size() - 1).intValue());
1743         for (int i = selectedPositions.size() - 2; i >= 0; i--) {
1744             assertFalse(0 == selectedPositions.get(i).intValue());
1745         }
1746 
1747     }
1748 
testSmoothScrollSelectionEventsLinear()1749     public void testSmoothScrollSelectionEventsLinear() throws Throwable {
1750         mInstrumentation = getInstrumentation();
1751         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1752         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1753                 R.layout.vertical_linear);
1754         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 500);
1755         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1756         mOrientation = BaseGridView.VERTICAL;
1757         mNumRows = 1;
1758         initActivity(intent);
1759 
1760         runTestOnUiThread(new Runnable() {
1761             public void run() {
1762                 mGridView.setSelectedPositionSmooth(10);
1763             }
1764         });
1765         waitForScrollIdle(mVerifyLayout);
1766         humanDelay(500);
1767 
1768         final ArrayList<Integer> selectedPositions = new ArrayList<Integer>();
1769         mGridView.setOnChildSelectedListener(new OnChildSelectedListener() {
1770             @Override
1771             public void onChildSelected(ViewGroup parent, View view, int position, long id) {
1772                 selectedPositions.add(position);
1773             }
1774         });
1775 
1776         sendRepeatedKeys(10, KeyEvent.KEYCODE_DPAD_UP);
1777         humanDelay(500);
1778         waitForScrollIdle(mVerifyLayout);
1779         // should only get childselected event for item 0 once
1780         assertTrue(selectedPositions.size() > 0);
1781         assertEquals(0, selectedPositions.get(selectedPositions.size() - 1).intValue());
1782         for (int i = selectedPositions.size() - 2; i >= 0; i--) {
1783             assertFalse(0 == selectedPositions.get(i).intValue());
1784         }
1785 
1786     }
1787 
testScrollToNoneExisting()1788     public void testScrollToNoneExisting() throws Throwable {
1789         mInstrumentation = getInstrumentation();
1790         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1791         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1792                 R.layout.vertical_grid);
1793         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100);
1794         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1795         mOrientation = BaseGridView.VERTICAL;
1796         mNumRows = 3;
1797         initActivity(intent);
1798 
1799         runTestOnUiThread(new Runnable() {
1800             public void run() {
1801                 mGridView.setSelectedPositionSmooth(99);
1802             }
1803         });
1804         waitForScrollIdle(mVerifyLayout);
1805         humanDelay(500);
1806 
1807 
1808         runTestOnUiThread(new Runnable() {
1809             public void run() {
1810                 mGridView.setSelectedPositionSmooth(50);
1811             }
1812         });
1813         Thread.sleep(100);
1814         runTestOnUiThread(new Runnable() {
1815             public void run() {
1816                 mGridView.requestLayout();
1817                 mGridView.setSelectedPositionSmooth(0);
1818             }
1819         });
1820         waitForScrollIdle(mVerifyLayout);
1821         humanDelay(500);
1822 
1823     }
1824 
testSmoothscrollerInterrupted()1825     public void testSmoothscrollerInterrupted() throws Throwable {
1826         mInstrumentation = getInstrumentation();
1827         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1828         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1829                 R.layout.vertical_linear);
1830         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
1831         int[] items = new int[100];
1832         for (int i = 0; i < items.length; i++) {
1833             items[i] = 680;
1834         }
1835         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
1836         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1837         mOrientation = BaseGridView.VERTICAL;
1838         mNumRows = 1;
1839 
1840         initActivity(intent);
1841 
1842         mGridView.setSelectedPositionSmooth(0);
1843         waitForScrollIdle(mVerifyLayout);
1844         assertTrue(mGridView.getChildAt(0).hasFocus());
1845 
1846         // Pressing lots of key to make sure smooth scroller is running
1847         for (int i = 0; i < 20; i++) {
1848             sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
1849         }
1850         Thread.sleep(100);
1851         int total = 0;
1852         while (mGridView.getLayoutManager().isSmoothScrolling() ||
1853                 mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE) {
1854             if ((total += 10) >= WAIT_FOR_SCROLL_IDLE_TIMEOUT_MS) {
1855                 throw new RuntimeException("waitForScrollIdle Timeout");
1856             }
1857             try {
1858                 // Repeatedly pressing to make sure pending keys does not drop to zero.
1859                 Thread.sleep(10);
1860                 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
1861             } catch (InterruptedException ex) {
1862                 break;
1863             }
1864         }
1865 
1866         assertTrue("LinearSmoothScroller would not use many RV.smoothScrollBy() calls",
1867                 ((VerticalGridViewEx) mGridView).mSmoothScrollByCalled < 10);
1868     }
1869 
testSmoothscrollerCancelled()1870     public void testSmoothscrollerCancelled() throws Throwable {
1871         mInstrumentation = getInstrumentation();
1872         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1873         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1874                 R.layout.vertical_linear);
1875         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
1876         int[] items = new int[100];
1877         for (int i = 0; i < items.length; i++) {
1878             items[i] = 680;
1879         }
1880         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
1881         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1882         mOrientation = BaseGridView.VERTICAL;
1883         mNumRows = 1;
1884 
1885         initActivity(intent);
1886 
1887         mGridView.setSelectedPositionSmooth(0);
1888         waitForScrollIdle(mVerifyLayout);
1889         assertTrue(mGridView.getChildAt(0).hasFocus());
1890 
1891         int targetPosition = items.length - 1;
1892         mGridView.setSelectedPositionSmooth(targetPosition);
1893         runTestOnUiThread(new Runnable() {
1894             public void run() {
1895                 mGridView.stopScroll();
1896             }
1897         });
1898         Thread.sleep(100);
1899         assertEquals(mGridView.getSelectedPosition(), targetPosition);
1900         assertSame(mGridView.getLayoutManager().findViewByPosition(targetPosition),
1901                 mGridView.findFocus());
1902     }
1903 
testSetNumRowsAndAddItem()1904     public void testSetNumRowsAndAddItem() throws Throwable {
1905         mInstrumentation = getInstrumentation();
1906         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1907         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1908                 R.layout.vertical_linear);
1909         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
1910         int[] items = new int[2];
1911         for (int i = 0; i < items.length; i++) {
1912             items[i] = 300;
1913         }
1914         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
1915         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1916         mOrientation = BaseGridView.VERTICAL;
1917         mNumRows = 1;
1918 
1919         initActivity(intent);
1920 
1921         mGridView.setSelectedPositionSmooth(0);
1922         waitForScrollIdle(mVerifyLayout);
1923 
1924         mActivity.addItems(items.length, new int[]{300});
1925 
1926         runTestOnUiThread(new Runnable() {
1927             public void run() {
1928                 ((VerticalGridView) mGridView).setNumColumns(2);
1929             }
1930         });
1931         Thread.sleep(1000);
1932         assertTrue(mGridView.getChildAt(2).getLeft() != mGridView.getChildAt(1).getLeft());
1933     }
1934 
1935 
testRequestLayoutBugInLayout()1936     public void testRequestLayoutBugInLayout() throws Throwable {
1937         mInstrumentation = getInstrumentation();
1938         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1939         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1940                 R.layout.vertical_linear);
1941         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout);
1942         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
1943         int[] items = new int[100];
1944         for (int i = 0; i < items.length; i++) {
1945             items[i] = 300;
1946         }
1947         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
1948         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1949         mOrientation = BaseGridView.VERTICAL;
1950         mNumRows = 1;
1951 
1952         initActivity(intent);
1953 
1954         runTestOnUiThread(new Runnable() {
1955             public void run() {
1956                 mGridView.setSelectedPositionSmooth(1);
1957             }
1958         });
1959         waitForScrollIdle(mVerifyLayout);
1960 
1961         sendKeys(KeyEvent.KEYCODE_DPAD_UP);
1962         waitForScrollIdle(mVerifyLayout);
1963 
1964         assertEquals("Line 2", ((TextView) mGridView.findFocus()).getText().toString());
1965     }
1966 
1967 
testChangeLayoutInChild()1968     public void testChangeLayoutInChild() throws Throwable {
1969         mInstrumentation = getInstrumentation();
1970         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
1971         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
1972                 R.layout.vertical_linear_wrap_content);
1973         intent.putExtra(GridActivity.EXTRA_REQUEST_LAYOUT_ONFOCUS, true);
1974         int[] items = new int[2];
1975         for (int i = 0; i < items.length; i++) {
1976             items[i] = 300;
1977         }
1978         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
1979         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
1980         mOrientation = BaseGridView.VERTICAL;
1981         mNumRows = 1;
1982 
1983         initActivity(intent);
1984 
1985         runTestOnUiThread(new Runnable() {
1986             public void run() {
1987                 mGridView.setSelectedPositionSmooth(0);
1988             }
1989         });
1990         waitForScrollIdleAndItemAnimation(mVerifyLayout);
1991         verifyMargin();
1992 
1993         runTestOnUiThread(new Runnable() {
1994             public void run() {
1995                 mGridView.setSelectedPositionSmooth(1);
1996             }
1997         });
1998         waitForScrollIdleAndItemAnimation(mVerifyLayout);
1999         verifyMargin();
2000     }
2001 
testWrapContent()2002     public void testWrapContent() throws Throwable {
2003         mInstrumentation = getInstrumentation();
2004         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2005         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
2006                 R.layout.horizontal_grid_wrap);
2007         int[] items = new int[200];
2008         for (int i = 0; i < items.length; i++) {
2009             items[i] = 300;
2010         }
2011         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
2012         mOrientation = BaseGridView.HORIZONTAL;
2013         mNumRows = 1;
2014 
2015         initActivity(intent);
2016 
2017         runTestOnUiThread(new Runnable() {
2018             public void run() {
2019                 mActivity.attachToNewAdapter(new int[0]);
2020             }
2021         });
2022 
2023     }
2024 
2025 
testZeroFixedSecondarySize()2026     public void testZeroFixedSecondarySize() throws Throwable {
2027         mInstrumentation = getInstrumentation();
2028         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2029         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
2030                 R.layout.vertical_linear_measured_with_zero);
2031         intent.putExtra(GridActivity.EXTRA_SECONDARY_SIZE_ZERO, true);
2032         int[] items = new int[2];
2033         for (int i = 0; i < items.length; i++) {
2034             items[i] = 0;
2035         }
2036         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
2037         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
2038         mOrientation = BaseGridView.VERTICAL;
2039         mNumRows = 1;
2040 
2041         initActivity(intent);
2042 
2043     }
2044 
testChildStates()2045     public void testChildStates() throws Throwable {
2046         mInstrumentation = getInstrumentation();
2047         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2048         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
2049         int[] items = new int[100];
2050         for (int i = 0; i < items.length; i++) {
2051             items[i] = 200;
2052         }
2053         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
2054         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
2055         intent.putExtra(GridActivity.EXTRA_REQUEST_LAYOUT_ONFOCUS, true);
2056         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.selectable_text_view);
2057         mOrientation = BaseGridView.VERTICAL;
2058         mNumRows = 1;
2059 
2060         initActivity(intent);
2061         mGridView.setSaveChildrenPolicy(VerticalGridView.SAVE_ALL_CHILD);
2062 
2063         final SparseArray<Parcelable> container = new SparseArray<Parcelable>();
2064 
2065         // 1 Save view states
2066         runTestOnUiThread(new Runnable() {
2067             public void run() {
2068                 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(0))
2069                         .getText()), 0, 1);
2070                 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(1))
2071                         .getText()), 0, 1);
2072                 mGridView.saveHierarchyState(container);
2073             }
2074         });
2075 
2076         // 2 Change view states
2077         runTestOnUiThread(new Runnable() {
2078             public void run() {
2079                 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(0))
2080                         .getText()), 1, 2);
2081                 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(1))
2082                         .getText()), 1, 2);
2083             }
2084         });
2085 
2086         // 3 Detached and re-attached,  should still maintain state of (2)
2087         runTestOnUiThread(new Runnable() {
2088             public void run() {
2089                 mGridView.setSelectedPositionSmooth(1);
2090             }
2091         });
2092         waitForScrollIdleAndItemAnimation(mVerifyLayout);
2093         assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionStart(), 1);
2094         assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionEnd(), 2);
2095         assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionStart(), 1);
2096         assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionEnd(), 2);
2097 
2098         // 4 Recycled and rebound, should load state from (2)
2099         runTestOnUiThread(new Runnable() {
2100             public void run() {
2101                 mGridView.setSelectedPositionSmooth(20);
2102             }
2103         });
2104         waitForScrollIdle(mVerifyLayout);
2105         runTestOnUiThread(new Runnable() {
2106             public void run() {
2107                 mGridView.setSelectedPositionSmooth(0);
2108             }
2109         });
2110         waitForScrollIdleAndItemAnimation(mVerifyLayout);
2111         assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionStart(), 1);
2112         assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionEnd(), 2);
2113         assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionStart(), 1);
2114         assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionEnd(), 2);
2115     }
2116 
2117 
testNoDispatchSaveChildState()2118     public void testNoDispatchSaveChildState() throws Throwable {
2119         mInstrumentation = getInstrumentation();
2120         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2121         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
2122         int[] items = new int[100];
2123         for (int i = 0; i < items.length; i++) {
2124             items[i] = 200;
2125         }
2126         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
2127         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
2128         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.selectable_text_view);
2129         mOrientation = BaseGridView.VERTICAL;
2130         mNumRows = 1;
2131 
2132         initActivity(intent);
2133         mGridView.setSaveChildrenPolicy(VerticalGridView.SAVE_NO_CHILD);
2134 
2135         final SparseArray<Parcelable> container = new SparseArray<Parcelable>();
2136 
2137         // 1. Set text selection, save view states should do nothing on child
2138         runTestOnUiThread(new Runnable() {
2139             public void run() {
2140                 for (int i = 0; i < mGridView.getChildCount(); i++) {
2141                     Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(i))
2142                             .getText()), 0, 1);
2143                 }
2144                 mGridView.saveHierarchyState(container);
2145             }
2146         });
2147 
2148         // 2. clear the text selection
2149         runTestOnUiThread(new Runnable() {
2150             public void run() {
2151                 for (int i = 0; i < mGridView.getChildCount(); i++) {
2152                     Selection.removeSelection((Spannable)(((TextView) mGridView.getChildAt(i))
2153                             .getText()));
2154                 }
2155             }
2156         });
2157 
2158         // 3. Restore view states should be a no-op for child
2159         runTestOnUiThread(new Runnable() {
2160             public void run() {
2161                 mGridView.restoreHierarchyState(container);
2162                 for (int i = 0; i < mGridView.getChildCount(); i++) {
2163                     assertEquals(-1, ((TextView) mGridView.getChildAt(i)).getSelectionStart());
2164                     assertEquals(-1, ((TextView) mGridView.getChildAt(i)).getSelectionEnd());
2165                 }
2166             }
2167         });
2168     }
2169 
2170 
2171     static interface ViewTypeProvider {
getViewType(int position)2172         public int getViewType(int position);
2173     }
2174 
2175     static interface ItemAlignmentFacetProvider {
getItemAlignmentFacet(int viewType)2176         public ItemAlignmentFacet getItemAlignmentFacet(int viewType);
2177     }
2178 
2179     static class TwoViewTypesProvider implements ViewTypeProvider {
2180         static int VIEW_TYPE_FIRST = 1;
2181         static int VIEW_TYPE_DEFAULT = 0;
2182         @Override
getViewType(int position)2183         public int getViewType(int position) {
2184             if (position == 0) {
2185                 return VIEW_TYPE_FIRST;
2186             } else {
2187                 return VIEW_TYPE_DEFAULT;
2188             }
2189         }
2190     }
2191 
2192     static class ChangeableViewTypesProvider implements ViewTypeProvider {
2193         static SparseIntArray sViewTypes = new SparseIntArray();
2194         @Override
getViewType(int position)2195         public int getViewType(int position) {
2196             return sViewTypes.get(position);
2197         }
clear()2198         public static void clear() {
2199             sViewTypes.clear();
2200         }
setViewType(int position, int type)2201         public static void setViewType(int position, int type) {
2202             sViewTypes.put(position, type);
2203         }
2204     }
2205 
2206     static class PositionItemAlignmentFacetProviderForRelativeLayout1
2207             implements ItemAlignmentFacetProvider {
2208         ItemAlignmentFacet mMultipleFacet;
2209 
PositionItemAlignmentFacetProviderForRelativeLayout1()2210         PositionItemAlignmentFacetProviderForRelativeLayout1() {
2211             mMultipleFacet = new ItemAlignmentFacet();
2212             ItemAlignmentFacet.ItemAlignmentDef[] defs =
2213                     new ItemAlignmentFacet.ItemAlignmentDef[2];
2214             defs[0] = new ItemAlignmentFacet.ItemAlignmentDef();
2215             defs[0].setItemAlignmentViewId(R.id.t1);
2216             defs[1] = new ItemAlignmentFacet.ItemAlignmentDef();
2217             defs[1].setItemAlignmentViewId(R.id.t2);
2218             defs[1].setItemAlignmentOffsetPercent(100);
2219             defs[1].setItemAlignmentOffset(-10);
2220             mMultipleFacet.setAlignmentDefs(defs);
2221         }
2222 
2223         @Override
getItemAlignmentFacet(int position)2224         public ItemAlignmentFacet getItemAlignmentFacet(int position) {
2225             if (position == 0) {
2226                 return mMultipleFacet;
2227             } else {
2228                 return null;
2229             }
2230         }
2231     }
2232 
testMultipleScrollPosition1()2233     public void testMultipleScrollPosition1() throws Throwable {
2234         mInstrumentation = getInstrumentation();
2235         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2236         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
2237                 R.layout.vertical_linear);
2238         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout);
2239         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
2240         int[] items = new int[100];
2241         for (int i = 0; i < items.length; i++) {
2242             items[i] = 300;
2243         }
2244         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
2245         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
2246         intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS,
2247                 TwoViewTypesProvider.class.getName());
2248         // Set ItemAlignment for each ViewHolder and view type,  ViewHolder should
2249         // override the view type settings.
2250         intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_CLASS,
2251                 PositionItemAlignmentFacetProviderForRelativeLayout1.class.getName());
2252         intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_VIEWTYPE_CLASS,
2253                 ViewTypePositionItemAlignmentFacetProviderForRelativeLayout2.class.getName());
2254         mOrientation = BaseGridView.VERTICAL;
2255         mNumRows = 1;
2256 
2257         initActivity(intent);
2258 
2259         assertEquals("First view is aligned with padding top",
2260                 mGridView.getPaddingTop(), mGridView.getChildAt(0).getTop());
2261 
2262         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
2263         waitForScrollIdle(mVerifyLayout);
2264 
2265         final View v = mGridView.getChildAt(0);
2266         View t1 = v.findViewById(R.id.t1);
2267         int t1align = (t1.getTop() + t1.getBottom()) / 2;
2268         View t2 = v.findViewById(R.id.t2);
2269         int t2align = t2.getBottom() - 10;
2270         assertEquals("Expected alignment for 2nd textview",
2271                 mGridView.getPaddingTop() - (t2align - t1align),
2272                 v.getTop());
2273     }
2274 
2275     static class PositionItemAlignmentFacetProviderForRelativeLayout2 implements
2276             ItemAlignmentFacetProvider {
2277         ItemAlignmentFacet mMultipleFacet;
2278 
PositionItemAlignmentFacetProviderForRelativeLayout2()2279         PositionItemAlignmentFacetProviderForRelativeLayout2() {
2280             mMultipleFacet = new ItemAlignmentFacet();
2281             ItemAlignmentFacet.ItemAlignmentDef[] defs = new ItemAlignmentFacet.ItemAlignmentDef[2];
2282             defs[0] = new ItemAlignmentFacet.ItemAlignmentDef();
2283             defs[0].setItemAlignmentViewId(R.id.t1);
2284             defs[0].setItemAlignmentOffsetPercent(0);
2285             defs[1] = new ItemAlignmentFacet.ItemAlignmentDef();
2286             defs[1].setItemAlignmentViewId(R.id.t2);
2287             defs[1].setItemAlignmentOffsetPercent(ItemAlignmentFacet.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
2288             defs[1].setItemAlignmentOffset(-10);
2289             mMultipleFacet.setAlignmentDefs(defs);
2290         }
2291 
2292         @Override
getItemAlignmentFacet(int position)2293         public ItemAlignmentFacet getItemAlignmentFacet(int position) {
2294             if (position == 0) {
2295                 return mMultipleFacet;
2296             } else {
2297                 return null;
2298             }
2299         }
2300     }
2301 
testMultipleScrollPosition2()2302     public void testMultipleScrollPosition2() throws Throwable {
2303         mInstrumentation = getInstrumentation();
2304         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2305         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
2306         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout);
2307         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
2308         int[] items = new int[100];
2309         for (int i = 0; i < items.length; i++) {
2310             items[i] = 300;
2311         }
2312         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
2313         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
2314         intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS,
2315                 TwoViewTypesProvider.class.getName());
2316         intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_CLASS,
2317                 PositionItemAlignmentFacetProviderForRelativeLayout2.class.getName());
2318         mOrientation = BaseGridView.VERTICAL;
2319         mNumRows = 1;
2320 
2321         initActivity(intent);
2322 
2323         assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(),
2324                 mGridView.getChildAt(0).getTop());
2325 
2326         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
2327         waitForScrollIdle(mVerifyLayout);
2328 
2329         final View v = mGridView.getChildAt(0);
2330         View t1 = v.findViewById(R.id.t1);
2331         int t1align = t1.getTop();
2332         View t2 = v.findViewById(R.id.t2);
2333         int t2align = t2.getTop() - 10;
2334         assertEquals("Expected alignment for 2nd textview",
2335                 mGridView.getPaddingTop() - (t2align - t1align), v.getTop());
2336     }
2337 
2338     static class ViewTypePositionItemAlignmentFacetProviderForRelativeLayout2 implements
2339             ItemAlignmentFacetProvider {
2340         ItemAlignmentFacet mMultipleFacet;
2341 
ViewTypePositionItemAlignmentFacetProviderForRelativeLayout2()2342         ViewTypePositionItemAlignmentFacetProviderForRelativeLayout2() {
2343             mMultipleFacet = new ItemAlignmentFacet();
2344             ItemAlignmentFacet.ItemAlignmentDef[] defs = new ItemAlignmentFacet.ItemAlignmentDef[2];
2345             defs[0] = new ItemAlignmentFacet.ItemAlignmentDef();
2346             defs[0].setItemAlignmentViewId(R.id.t1);
2347             defs[0].setItemAlignmentOffsetPercent(0);
2348             defs[1] = new ItemAlignmentFacet.ItemAlignmentDef();
2349             defs[1].setItemAlignmentViewId(R.id.t2);
2350             defs[1].setItemAlignmentOffsetPercent(100);
2351             defs[1].setItemAlignmentOffset(-10);
2352             mMultipleFacet.setAlignmentDefs(defs);
2353         }
2354 
2355         @Override
getItemAlignmentFacet(int viewType)2356         public ItemAlignmentFacet getItemAlignmentFacet(int viewType) {
2357             if (viewType == TwoViewTypesProvider.VIEW_TYPE_FIRST) {
2358                 return mMultipleFacet;
2359             } else {
2360                 return null;
2361             }
2362         }
2363     }
2364 
testMultipleScrollPosition3()2365     public void testMultipleScrollPosition3() throws Throwable {
2366         mInstrumentation = getInstrumentation();
2367         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2368         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
2369         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout);
2370         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
2371         int[] items = new int[100];
2372         for (int i = 0; i < items.length; i++) {
2373             items[i] = 300;
2374         }
2375         intent.putExtra(GridActivity.EXTRA_ITEMS, items);
2376         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
2377         intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS,
2378                 TwoViewTypesProvider.class.getName());
2379         intent.putExtra(GridActivity.EXTRA_ITEMALIGNMENTPROVIDER_VIEWTYPE_CLASS,
2380                 ViewTypePositionItemAlignmentFacetProviderForRelativeLayout2.class.getName());
2381         mOrientation = BaseGridView.VERTICAL;
2382         mNumRows = 1;
2383 
2384         initActivity(intent);
2385 
2386         assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(),
2387                 mGridView.getChildAt(0).getTop());
2388 
2389         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
2390         waitForScrollIdle(mVerifyLayout);
2391 
2392         final View v = mGridView.getChildAt(0);
2393         View t1 = v.findViewById(R.id.t1);
2394         int t1align = t1.getTop();
2395         View t2 = v.findViewById(R.id.t2);
2396         int t2align = t2.getBottom() - 10;
2397         assertEquals("Expected alignment for 2nd textview",
2398                 mGridView.getPaddingTop() - (t2align - t1align), v.getTop());
2399     }
2400 
testSelectionAndAddItemInOneCycle()2401     public void testSelectionAndAddItemInOneCycle() throws Throwable {
2402         mInstrumentation = getInstrumentation();
2403         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2404         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
2405                 R.layout.vertical_linear);
2406         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 0);
2407         initActivity(intent);
2408         mOrientation = BaseGridView.HORIZONTAL;
2409         mNumRows = 1;
2410 
2411         runTestOnUiThread(new Runnable() {
2412             public void run() {
2413                 mActivity.addItems(0, new int[]{300, 300});
2414                 mGridView.setSelectedPosition(0);
2415             }
2416         });
2417         waitForTransientStateGone(null);
2418         assertEquals(0, mGridView.getSelectedPosition());
2419     }
2420 
testNotifyItemTypeChangedSelectionEvent()2421     public void testNotifyItemTypeChangedSelectionEvent() throws Throwable {
2422         mInstrumentation = getInstrumentation();
2423         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2424         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
2425                 R.layout.vertical_linear);
2426         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 10);
2427         intent.putExtra(GridActivity.EXTRA_VIEWTYPEPROVIDER_CLASS,
2428                 ChangeableViewTypesProvider.class.getName());
2429         ChangeableViewTypesProvider.clear();
2430         initActivity(intent);
2431         mOrientation = BaseGridView.HORIZONTAL;
2432         mNumRows = 1;
2433 
2434         final ArrayList<Integer> selectedLog = new ArrayList<Integer>();
2435         mGridView.setOnChildSelectedListener(new OnChildSelectedListener() {
2436             public void onChildSelected(ViewGroup parent, View view, int position, long id) {
2437                 selectedLog.add(position);
2438             }
2439         });
2440 
2441         runTestOnUiThread(new Runnable() {
2442             public void run() {
2443                 ChangeableViewTypesProvider.setViewType(0, 1);
2444                 mGridView.getAdapter().notifyItemChanged(0, 1);
2445             }
2446         });
2447         waitForTransientStateGone(null);
2448         assertEquals(0, mGridView.getSelectedPosition());
2449         assertEquals(selectedLog.size(), 1);
2450         assertEquals((int) selectedLog.get(0), 0);
2451     }
2452 
testSelectionSmoothAndAddItemInOneCycle()2453     public void testSelectionSmoothAndAddItemInOneCycle() throws Throwable {
2454         mInstrumentation = getInstrumentation();
2455         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2456         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
2457                 R.layout.vertical_linear);
2458         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 0);
2459         initActivity(intent);
2460         mOrientation = BaseGridView.HORIZONTAL;
2461         mNumRows = 1;
2462 
2463         runTestOnUiThread(new Runnable() {
2464             public void run() {
2465                 mActivity.addItems(0, new int[]{300, 300});
2466                 mGridView.setSelectedPositionSmooth(0);
2467             }
2468         });
2469         waitForTransientStateGone(null);
2470         assertEquals(0, mGridView.getSelectedPosition());
2471     }
2472 
testExtraLayoutSpace()2473     public void testExtraLayoutSpace() throws Throwable {
2474         mInstrumentation = getInstrumentation();
2475         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2476         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
2477                 R.layout.vertical_linear);
2478         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000);
2479         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
2480         initActivity(intent);
2481 
2482         final int windowSize = mGridView.getHeight();
2483         final int extraLayoutSize = windowSize;
2484         int itemLength = mActivity.mItemLengths[0];
2485         mOrientation = BaseGridView.VERTICAL;
2486         mNumRows = 1;
2487 
2488         // add extra layout space
2489         runTestOnUiThread(new Runnable() {
2490             public void run() {
2491                 mGridView.setExtraLayoutSpace(extraLayoutSize);
2492             }
2493         });
2494         Thread.sleep(50);
2495         View v;
2496         v = mGridView.getChildAt(mGridView.getChildCount() - 1);
2497         assertTrue(v.getTop() < windowSize + extraLayoutSize);
2498         assertTrue(v.getBottom() >= windowSize + extraLayoutSize -
2499                 mGridView.getVerticalMargin());
2500 
2501         mGridView.setSelectedPositionSmooth(150);
2502         waitForScrollIdle(mVerifyLayout);
2503         v = mGridView.getChildAt(0);
2504         assertTrue(v.getBottom() > - extraLayoutSize);
2505         assertTrue(v.getTop() <= -extraLayoutSize + mGridView.getVerticalMargin());
2506 
2507         // clear extra layout space
2508         runTestOnUiThread(new Runnable() {
2509             public void run() {
2510                 mGridView.setExtraLayoutSpace(0);
2511                 verifyMargin();
2512             }
2513         });
2514         Thread.sleep(50);
2515         v = mGridView.getChildAt(mGridView.getChildCount() - 1);
2516         assertTrue(v.getTop() < windowSize);
2517         assertTrue(v.getBottom() >= windowSize - mGridView.getVerticalMargin());
2518     }
2519 
testFocusFinder()2520     public void testFocusFinder() throws Throwable {
2521         mInstrumentation = getInstrumentation();
2522         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2523         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
2524                 R.layout.vertical_linear_with_button);
2525         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 3);
2526         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
2527         initActivity(intent);
2528         mOrientation = BaseGridView.VERTICAL;
2529         mNumRows = 1;
2530 
2531         // test focus from button to vertical grid view
2532         final View button = mActivity.findViewById(R.id.button);
2533         assertTrue(button.isFocused());
2534         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
2535         assertFalse(mGridView.isFocused());
2536         assertTrue(mGridView.hasFocus());
2537 
2538         // FocusFinder should find last focused(2nd) item on DPAD_DOWN
2539         final View secondChild = mGridView.getChildAt(1);
2540         runTestOnUiThread(new Runnable() {
2541             @Override
2542             public void run() {
2543                 secondChild.requestFocus();
2544                 button.requestFocus();
2545             }
2546         });
2547         assertTrue(button.isFocused());
2548         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
2549         assertTrue(secondChild.isFocused());
2550 
2551         // Bug 26918143 Even VerticalGridView is not focusable, FocusFinder should find last focused
2552         // (2nd) item on DPAD_DOWN.
2553         runTestOnUiThread(new Runnable() {
2554             @Override
2555             public void run() {
2556                 button.requestFocus();
2557             }
2558         });
2559         mGridView.setFocusable(false);
2560         mGridView.setFocusableInTouchMode(false);
2561         assertTrue(button.isFocused());
2562         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
2563         assertTrue(secondChild.isFocused());
2564     }
2565 
testRestoreIndexAndAddItems()2566     public void testRestoreIndexAndAddItems() throws Throwable {
2567         mInstrumentation = getInstrumentation();
2568         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2569         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
2570                 R.layout.vertical_linear);
2571         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.horizontal_item);
2572         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 4);
2573         initActivity(intent);
2574         mOrientation = BaseGridView.VERTICAL;
2575         mNumRows = 1;
2576 
2577         assertEquals(mGridView.getSelectedPosition(), 0);
2578         final SparseArray states = new SparseArray();
2579         runTestOnUiThread(new Runnable() {
2580             @Override
2581             public void run() {
2582                 mGridView.saveHierarchyState(states);
2583                 mGridView.setAdapter(null);
2584             }
2585 
2586         });
2587         runTestOnUiThread(new Runnable() {
2588             @Override
2589             public void run() {
2590                 mGridView.restoreHierarchyState(states);
2591                 mActivity.attachToNewAdapter(new int[0]);
2592                 mActivity.addItems(0, new int[]{100, 100, 100, 100});
2593             }
2594 
2595         });
2596         waitForTransientStateGone(null);
2597         assertEquals(mGridView.getSelectedPosition(), 0);
2598     }
2599 
test27766012()2600     public void test27766012() throws Throwable {
2601         mInstrumentation = getInstrumentation();
2602         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2603         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
2604                 R.layout.vertical_linear_with_button_onleft);
2605         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.horizontal_item);
2606         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2);
2607         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
2608         intent.putExtra(GridActivity.EXTRA_UPDATE_SIZE, false);
2609         initActivity(intent);
2610         mOrientation = BaseGridView.VERTICAL;
2611         mNumRows = 1;
2612 
2613         // set remove animator two seconds
2614         mGridView.getItemAnimator().setRemoveDuration(2000);
2615         final View view = mGridView.getChildAt(1);
2616         runTestOnUiThread(new Runnable() {
2617             @Override
2618             public void run() {
2619                 view.requestFocus();
2620             }
2621         });
2622         assertTrue(view.hasFocus());
2623         runTestOnUiThread(new Runnable() {
2624             @Override
2625             public void run() {
2626                 mActivity.removeItems(0, 2);
2627             }
2628 
2629         });
2630         // wait one second, removing second view is still attached to parent
2631         Thread.sleep(1000);
2632         assertSame(view.getParent(), mGridView);
2633         runTestOnUiThread(new Runnable() {
2634             @Override
2635             public void run() {
2636                 // refocus to the removed item and do a focus search.
2637                 view.requestFocus();
2638                 view.focusSearch(View.FOCUS_UP);
2639             }
2640 
2641         });
2642     }
2643 
testBug27258366()2644     public void testBug27258366() throws Throwable {
2645         mInstrumentation = getInstrumentation();
2646         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2647         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
2648                 R.layout.vertical_linear_with_button_onleft);
2649         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.horizontal_item);
2650         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 10);
2651         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
2652         intent.putExtra(GridActivity.EXTRA_UPDATE_SIZE, false);
2653         initActivity(intent);
2654         mOrientation = BaseGridView.VERTICAL;
2655         mNumRows = 1;
2656 
2657         // move item1 500 pixels right, when focus is on item1, default focus finder will pick
2658         // item0 and item2 for the best match of focusSearch(FOCUS_LEFT).  The grid widget
2659         // must override default addFocusables(), not to add item0 or item2.
2660         mActivity.mAdapterListener = new GridActivity.AdapterListener() {
2661             public void onBind(RecyclerView.ViewHolder vh, int position) {
2662                 if (position == 1) {
2663                     vh.itemView.setPaddingRelative(500, 0, 0, 0);
2664                 } else {
2665                     vh.itemView.setPaddingRelative(0, 0, 0, 0);
2666                 }
2667             }
2668         };
2669         runTestOnUiThread(new Runnable() {
2670             @Override
2671             public void run() {
2672                 mGridView.getAdapter().notifyDataSetChanged();
2673             }
2674         });
2675         Thread.sleep(100);
2676 
2677         final ViewGroup secondChild = (ViewGroup) mGridView.getChildAt(1);
2678         runTestOnUiThread(new Runnable() {
2679             @Override
2680             public void run() {
2681                 secondChild.requestFocus();
2682             }
2683         });
2684         sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
2685         Thread.sleep(100);
2686         final View button = mActivity.findViewById(R.id.button);
2687         assertTrue(button.isFocused());
2688     }
2689 
testAccessibility()2690     public void testAccessibility() throws Throwable {
2691         mInstrumentation = getInstrumentation();
2692         Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
2693         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
2694                 R.layout.vertical_linear);
2695         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000);
2696         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
2697         initActivity(intent);
2698         mOrientation = BaseGridView.VERTICAL;
2699         mNumRows = 1;
2700 
2701         assertTrue(0 == mGridView.getSelectedPosition());
2702 
2703         final RecyclerViewAccessibilityDelegate delegateCompat = mGridView
2704                 .getCompatAccessibilityDelegate();
2705         final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
2706         runTestOnUiThread(new Runnable() {
2707             @Override
2708             public void run() {
2709                 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
2710             }
2711         });
2712         assertTrue("test sanity", info.isScrollable());
2713         runTestOnUiThread(new Runnable() {
2714             @Override
2715             public void run() {
2716                 delegateCompat.performAccessibilityAction(mGridView,
2717                         AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null);
2718             }
2719         });
2720         waitForScrollIdle(mVerifyLayout);
2721         int selectedPosition1 = mGridView.getSelectedPosition();
2722         assertTrue(0 < selectedPosition1);
2723 
2724         runTestOnUiThread(new Runnable() {
2725             @Override
2726             public void run() {
2727                 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
2728             }
2729         });
2730         assertTrue("test sanity", info.isScrollable());
2731         runTestOnUiThread(new Runnable() {
2732             @Override
2733             public void run() {
2734                 delegateCompat.performAccessibilityAction(mGridView,
2735                         AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, null);
2736             }
2737         });
2738         waitForScrollIdle(mVerifyLayout);
2739         int selectedPosition2 = mGridView.getSelectedPosition();
2740         assertTrue(selectedPosition2 < selectedPosition1);
2741     }
2742 
2743 }
2744