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 // focus the listview 59 mActivity.runOnUiThread(() -> mListView.requestFocus()); 60 getInstrumentation().waitForIdleSync(); 61 } 62 63 @Override 64 protected void tearDown() throws Exception { 65 mActivity.finish(); 66 super.tearDown(); 67 } 68 69 @MediumTest 70 public void testPreconditions() throws Exception { 71 assertNotNull(mActivity); 72 assertNotNull(mListView); 73 assertEquals(mNumItems, mActivity.getNumItems()); 74 assertEquals(mNumRowsPerItem, mActivity.getNumRowsPerItem()); 75 } 76 77 @MediumTest 78 public void testScrollingDownInFirstItem() throws Exception { 79 80 for (int i = 0; i < mNumRowsPerItem; i++) { 81 assertEquals(0, mListView.getSelectedItemPosition()); 82 83 InternalSelectionView view = mActivity.getSelectedView(); 84 85 assertInternallySelectedRowOnScreen(view, i); 86 87 // move to next row 88 if (i < mNumRowsPerItem - 1) { 89 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 90 getInstrumentation().waitForIdleSync(); 91 } 92 } 93 94 { 95 assertEquals(0, mListView.getSelectedItemPosition()); 96 InternalSelectionView view = (InternalSelectionView) 97 mListView.getSelectedView(); 98 99 // 1 pixel tolerance in case height / 4 is not an even number 100 final int bottomFadingEdgeTop = 101 mListView.getBottom() - mListView.getVerticalFadingEdgeLength(); 102 assertTrue("bottom of view should be just above fading edge", 103 view.getBottom() == bottomFadingEdgeTop); 104 } 105 106 // make sure fading edge is the expected view 107 { 108 assertEquals("should be a second view visible due to the fading edge", 109 2, mListView.getChildCount()); 110 InternalSelectionView peekingChild = (InternalSelectionView) 111 mListView.getChildAt(1); 112 assertNotNull(peekingChild); 113 assertEquals("wrong value for peeking list item", 114 mActivity.getLabelForPosition(1), peekingChild.getLabel()); 115 } 116 } 117 118 @MediumTest 119 public void testScrollingToSecondItem() throws Exception { 120 121 for (int i = 0; i < mNumRowsPerItem; i++) { 122 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 123 getInstrumentation().waitForIdleSync(); 124 } 125 126 assertEquals("should have moved to second item", 127 1, mListView.getSelectedItemPosition()); 128 } 129 130 @LargeTest 131 public void testNoFadingEdgeAtBottomOfLastItem() { 132 133 // move down to last item 134 for (int i = 0; i < mNumItems; i++) { 135 for (int j = 0; j < mNumRowsPerItem; j++) { 136 if (i < mNumItems - 1 || j < mNumRowsPerItem - 1) { 137 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 138 getInstrumentation().waitForIdleSync(); 139 } 140 } 141 } 142 143 assertEquals(mNumItems - 1, mListView.getSelectedItemPosition()); 144 InternalSelectionView view = mActivity.getSelectedView(); 145 assertEquals(mNumRowsPerItem - 1, view.getSelectedRow()); 146 147 view.getRectForRow(mTempRect, mNumRowsPerItem - 1); 148 mListView.offsetDescendantRectToMyCoords(view, mTempRect); 149 150 assertTrue("bottom of last row of last item should be at " + 151 "the bottom of the list view (no fading edge)", 152 mListView.getBottom() - mListView.getVerticalFadingEdgeLength() < mTempRect.bottom); 153 } 154 155 @LargeTest 156 public void testNavigatingUpThroughInternalSelection() throws Exception { 157 158 // get to bottom of second item 159 for (int i = 0; i < 2; i++) { 160 for (int j = 0; j < mNumRowsPerItem; j++) { 161 if (i < 1 || j < mNumRowsPerItem - 1) { 162 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 163 getInstrumentation().waitForIdleSync(); 164 } 165 } 166 } 167 168 169 // (make sure we are at last row of second item) 170 { 171 assertEquals(1, mListView.getSelectedItemPosition()); 172 InternalSelectionView view = mActivity.getSelectedView(); 173 assertEquals(mNumRowsPerItem - 1, view.getSelectedRow()); 174 } 175 176 // go back up to the top of the second item 177 for (int i = mNumRowsPerItem - 1; i >= 0; i--) { 178 assertEquals(1, mListView.getSelectedItemPosition()); 179 InternalSelectionView view = mActivity.getSelectedView(); 180 181 assertInternallySelectedRowOnScreen(view, i); 182 183 // move up to next row 184 if (i > 0) { 185 sendKeys(KeyEvent.KEYCODE_DPAD_UP); 186 getInstrumentation().waitForIdleSync(); 187 } 188 } 189 190 // now we are at top row, should have caused scrolling, and fading edge... 191 { 192 assertEquals(1, mListView.getSelectedItemPosition()); 193 InternalSelectionView view = mActivity.getSelectedView(); 194 assertEquals(0, view.getSelectedRow()); 195 196 view.getDrawingRect(mTempRect); 197 mListView.offsetDescendantRectToMyCoords(view, mTempRect); 198 assertEquals("top of selected row should be just below top vertical fading edge", 199 mListView.getVerticalFadingEdgeLength(), 200 view.getTop()); 201 } 202 203 // make sure fading edge is the view we expect 204 { 205 final InternalSelectionView view = 206 (InternalSelectionView) mListView.getChildAt(0); 207 assertEquals(mActivity.getLabelForPosition(0), view.getLabel()); 208 } 209 210 211 } 212 213 /** 214 * @param internalFocused The view to check 215 * @param row 216 */ 217 private void assertInternallySelectedRowOnScreen( 218 InternalSelectionView internalFocused, 219 int row) { 220 assertEquals("expecting selected row", 221 row, internalFocused.getSelectedRow()); 222 223 internalFocused.getRectForRow(mTempRect, row); 224 mListView.offsetDescendantRectToMyCoords(internalFocused, mTempRect); 225 226 assertTrue("top of row " + row + " should be on sreen", 227 mTempRect.top >= 0); 228 assertTrue("bottom of row " + row + " should be on sreen", 229 mTempRect.bottom < mActivity.getScreenHeight()); 230 } 231 232 private void ensureNotInTouchMode() { 233 // If in touch mode inject a DPAD down event to exit that mode. 234 if (mListView.isInTouchMode()) { 235 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 236 getInstrumentation().waitForIdleSync(); 237 } 238 } 239} 240