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