1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget.focus;
18
19import android.graphics.Rect;
20import android.test.InstrumentationTestCase;
21import android.test.suitebuilder.annotation.LargeTest;
22import android.test.suitebuilder.annotation.MediumTest;
23import android.util.InternalSelectionView;
24import android.view.KeyEvent;
25import android.widget.ListView;
26
27
28/**
29 * TODO: extract base test case that launches {@link ListOfInternalSelectionViews} with
30 * bundle params.
31 */
32public class ScrollingThroughListOfFocusablesTest extends InstrumentationTestCase {
33
34    Rect mTempRect = new Rect();
35
36    private ListOfInternalSelectionViews mActivity;
37    private ListView mListView;
38
39    private int mNumItems = 4;
40    private int mNumRowsPerItem = 5;
41    private double mScreenHeightFactor = 5 /4;
42
43    @Override
44    protected void setUp() throws Exception {
45        mActivity = launchActivity(
46                "com.android.frameworks.coretests",
47                ListOfInternalSelectionViews.class,
48                ListOfInternalSelectionViews.getBundleFor(
49                    mNumItems,      // 4 items
50                    mNumRowsPerItem,      // 5 internally selectable rows per item
51                    mScreenHeightFactor)); // each item is 5 / 4 screen height tall
52        mListView = mActivity.getListView();
53        // Make sure we have some fading edge regardless of ListView style.
54        mListView.setVerticalFadingEdgeEnabled(true);
55        mListView.setFadingEdgeLength(10);
56        ensureNotInTouchMode();
57    }
58
59    @Override
60    protected void tearDown() throws Exception {
61        mActivity.finish();
62        super.tearDown();
63    }
64
65    @MediumTest
66    public void testPreconditions() throws Exception {
67        assertNotNull(mActivity);
68        assertNotNull(mListView);
69        assertEquals(mNumItems, mActivity.getNumItems());
70        assertEquals(mNumRowsPerItem, mActivity.getNumRowsPerItem());
71    }
72
73    @MediumTest
74    public void testScrollingDownInFirstItem() throws Exception {
75
76        for (int i = 0; i < mNumRowsPerItem; i++) {
77            assertEquals(0, mListView.getSelectedItemPosition());
78
79            InternalSelectionView view = mActivity.getSelectedView();
80
81            assertInternallySelectedRowOnScreen(view, i);
82
83            // move to next row
84            if (i < mNumRowsPerItem - 1) {
85                sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
86                getInstrumentation().waitForIdleSync();
87            }
88        }
89
90        {
91            assertEquals(0, mListView.getSelectedItemPosition());
92            InternalSelectionView view = (InternalSelectionView)
93                    mListView.getSelectedView();
94
95            // 1 pixel tolerance in case height / 4 is not an even number
96            final int bottomFadingEdgeTop =
97                mListView.getBottom() - mListView.getVerticalFadingEdgeLength();
98            assertTrue("bottom of view should be just above fading edge",
99                    view.getBottom() == bottomFadingEdgeTop);
100        }
101
102        // make sure fading edge is the expected view
103        {
104            assertEquals("should be a second view visible due to the fading edge",
105                            2, mListView.getChildCount());
106            InternalSelectionView peekingChild = (InternalSelectionView)
107                    mListView.getChildAt(1);
108            assertNotNull(peekingChild);
109            assertEquals("wrong value for peeking list item",
110                    mActivity.getLabelForPosition(1), peekingChild.getLabel());
111        }
112    }
113
114    @MediumTest
115    public void testScrollingToSecondItem() throws Exception {
116
117        for (int i = 0; i < mNumRowsPerItem; i++) {
118            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
119            getInstrumentation().waitForIdleSync();
120        }
121
122        assertEquals("should have moved to second item",
123                1, mListView.getSelectedItemPosition());
124    }
125
126    @LargeTest
127    public void testNoFadingEdgeAtBottomOfLastItem() {
128
129        // move down to last item
130        for (int i = 0; i < mNumItems; i++) {
131            for (int j = 0; j < mNumRowsPerItem; j++) {
132                if (i < mNumItems - 1 || j < mNumRowsPerItem - 1) {
133                    sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
134                    getInstrumentation().waitForIdleSync();
135                }
136            }
137        }
138
139        assertEquals(mNumItems - 1, mListView.getSelectedItemPosition());
140        InternalSelectionView view = mActivity.getSelectedView();
141        assertEquals(mNumRowsPerItem - 1, view.getSelectedRow());
142
143        view.getRectForRow(mTempRect, mNumRowsPerItem - 1);
144        mListView.offsetDescendantRectToMyCoords(view, mTempRect);
145
146        assertTrue("bottom of last row of last item should be at " +
147                "the bottom of the list view (no fading edge)",
148                mListView.getBottom() - mListView.getVerticalFadingEdgeLength() < mTempRect.bottom);
149    }
150
151    @LargeTest
152    public void testNavigatingUpThroughInternalSelection() throws Exception {
153
154        // get to bottom of second item
155        for (int i = 0; i < 2; i++) {
156            for (int j = 0; j < mNumRowsPerItem; j++) {
157                if (i < 1 || j < mNumRowsPerItem - 1) {
158                    sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
159                    getInstrumentation().waitForIdleSync();
160                }
161            }
162        }
163
164
165        // (make sure we are at last row of second item)
166        {
167            assertEquals(1, mListView.getSelectedItemPosition());
168            InternalSelectionView view = mActivity.getSelectedView();
169            assertEquals(mNumRowsPerItem - 1, view.getSelectedRow());
170        }
171
172        // go back up to the top of the second item
173        for (int i = mNumRowsPerItem - 1; i >= 0; i--) {
174            assertEquals(1, mListView.getSelectedItemPosition());
175            InternalSelectionView view = mActivity.getSelectedView();
176
177            assertInternallySelectedRowOnScreen(view, i);
178
179            // move up to next row
180            if (i > 0) {
181                sendKeys(KeyEvent.KEYCODE_DPAD_UP);
182                getInstrumentation().waitForIdleSync();
183            }
184        }
185
186        // now we are at top row, should have caused scrolling, and fading edge...
187        {
188            assertEquals(1, mListView.getSelectedItemPosition());
189            InternalSelectionView view = mActivity.getSelectedView();
190            assertEquals(0, view.getSelectedRow());
191
192            view.getDrawingRect(mTempRect);
193            mListView.offsetDescendantRectToMyCoords(view, mTempRect);
194            assertEquals("top of selected row should be just below top vertical fading edge",
195                    mListView.getVerticalFadingEdgeLength(),
196                    view.getTop());
197        }
198
199        // make sure fading edge is the view we expect
200        {
201            final InternalSelectionView view =
202                    (InternalSelectionView) mListView.getChildAt(0);
203            assertEquals(mActivity.getLabelForPosition(0), view.getLabel());
204        }
205
206
207    }
208
209    /**
210     * @param internalFocused The view to check
211     * @param row
212     */
213    private void assertInternallySelectedRowOnScreen(
214            InternalSelectionView internalFocused,
215            int row) {
216        assertEquals("expecting selected row",
217                row, internalFocused.getSelectedRow());
218
219        internalFocused.getRectForRow(mTempRect, row);
220        mListView.offsetDescendantRectToMyCoords(internalFocused, mTempRect);
221
222        assertTrue("top of row " + row + " should be on sreen",
223                mTempRect.top >= 0);
224        assertTrue("bottom of row " + row + " should be on sreen",
225                mTempRect.bottom < mActivity.getScreenHeight());
226    }
227
228    private void ensureNotInTouchMode() {
229        // If in touch mode inject a DPAD down event to exit that mode.
230        if (mListView.isInTouchMode()) {
231            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
232            getInstrumentation().waitForIdleSync();
233        }
234    }
235}
236