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