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.listview.arrowscroll;
18
19import android.test.ActivityInstrumentationTestCase2;
20import android.test.suitebuilder.annotation.MediumTest;
21import android.view.KeyEvent;
22import android.view.View;
23import android.widget.ListView;
24import android.widget.listview.ListOfItemsTallerThanScreen;
25
26public class ListOfItemsTallerThanScreenTest
27        extends ActivityInstrumentationTestCase2<ListOfItemsTallerThanScreen> {
28
29    private ListView mListView;
30    private ListOfItemsTallerThanScreen mActivity;
31
32
33    protected void setUp() throws Exception {
34        super.setUp();
35        mActivity = getActivity();
36        mListView = getActivity().getListView();
37    }
38
39    public ListOfItemsTallerThanScreenTest() {
40        super(ListOfItemsTallerThanScreen.class);
41    }
42
43    @MediumTest
44    public void testPreconditions() {
45        assertNotNull(mListView);
46        assertEquals("should only be one visible child", 1, mListView.getChildCount());
47        final int amountOffScreen = mListView.getChildAt(0).getBottom() - (mListView.getBottom() - mListView.getListPaddingBottom());
48        assertTrue("must be more than max scroll off screen for this test to work",
49                amountOffScreen > mListView.getMaxScrollAmount());
50    }
51
52    @MediumTest
53    public void testScrollDownAcrossItem() {
54        final View view = mListView.getSelectedView();
55        assertTrue(view.isSelected());
56
57        assertEquals(mListView.getListPaddingTop(),
58                view.getTop());
59
60        assertTrue("view must be taller than screen for this test to be worth anything",
61                view.getBottom() > mListView.getBottom());
62
63        // scroll down until next view is peeking ahead
64        int numScrollsUntilNextViewVisible = getNumDownPressesToScrollDownAcrossSelected();
65
66        for (int i = 0; i < numScrollsUntilNextViewVisible; i++) {
67            assertEquals("after " + i + " down scrolls across tall item",
68                    mListView.getListPaddingTop() - mListView.getMaxScrollAmount() * i,
69                    view.getTop());
70            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
71        }
72
73        // at this point, next view should be on screen peeking ahead, but we haven't given
74        // it selection yet
75        assertEquals("child count", 2, mListView.getChildCount());
76        assertEquals("selected position", 0, mListView.getSelectedItemPosition());
77        assertTrue("same view should be selected", view.isSelected());
78        final View peekingView = mListView.getChildAt(1);
79        assertEquals(view.getBottom(), peekingView.getTop());
80    }
81
82    @MediumTest
83    public void testScrollDownToNextItem() {
84        final int numPresses = getNumDownPressesToScrollDownAcrossSelected();
85        assertEquals(1, mListView.getChildCount());
86
87        for (int i = 0; i < numPresses; i++) {
88            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
89        }
90        // remember top of peeking child
91        final int topOfPeekingNext = mListView.getChildAt(1).getTop();
92
93        // next view is peeking, now press one more time
94        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
95
96        // old view should not have selection
97        assertFalse(mListView.getChildAt(0).isSelected());
98
99        // next view should now have selection, and be scrolled another a third of the list
100        // height
101        assertEquals(2, mListView.getChildCount());
102        final View next = mListView.getChildAt(1);
103        assertTrue("has selection", next.isSelected());
104        assertEquals(topOfPeekingNext - (mListView.getMaxScrollAmount()), next.getTop());
105    }
106
107    @MediumTest
108    public void testScrollFirstItemOffScreen() {
109        int numDownsToGetFirstItemOffScreen =
110                (mListView.getSelectedView().getHeight() / mListView.getMaxScrollAmount()) + 1;
111
112        for (int i = 0; i < numDownsToGetFirstItemOffScreen; i++) {
113            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
114        }
115        getInstrumentation().waitForIdleSync();
116
117        assertEquals("should be at next item",
118                1, mListView.getSelectedItemPosition());
119
120        final int listTop = mListView.getTop() + mListView.getListPaddingTop();
121        assertTrue("top of selected view should be above top of list",
122                mListView.getSelectedView().getTop() < listTop);
123
124        assertEquals("off screen item shouldn't be a child of list view",
125                1, mListView.getChildCount());
126    }
127
128    @MediumTest
129    public void testScrollDownToLastItem() {
130        final int numItems = mListView.getAdapter().getCount();
131
132        int maxDowns = 20;
133        while (mListView.getSelectedItemPosition() < (numItems - 1)) {
134            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
135            if (--maxDowns <= 0) {
136                fail("couldn't get to last item within 20 down arrows");
137            }
138        }
139        getInstrumentation().waitForIdleSync();
140
141        // press down enough times to get to bottom of last item
142        final int numDownsLeft = getNumDownPressesToScrollDownAcrossSelected();
143        assertTrue(numDownsLeft > 0);
144        for (int i = 0; i < numDownsLeft; i++) {
145            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
146        }
147        // one more time to get across last item
148        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
149        getInstrumentation().waitForIdleSync();
150
151        assertEquals(numItems - 1, mListView.getSelectedItemPosition());
152        final int realBottom = mListView.getHeight() - mListView.getListPaddingBottom();
153        assertEquals(realBottom, mListView.getSelectedView().getBottom());
154
155        assertEquals("views scrolled off screen should be removed from view group",
156                1, mListView.getChildCount());
157    }
158
159    @MediumTest
160    public void testScrollUpAcrossFirstItem() {
161        final int listTop = mListView.getListPaddingTop();
162        assertEquals(listTop, mListView.getSelectedView().getTop());
163        final int numPresses = getNumDownPressesToScrollDownAcrossSelected();
164        for (int i = 0; i < numPresses; i++) {
165            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
166        }
167        assertEquals(2, mListView.getChildCount());
168        for (int i = 0; i < numPresses; i++) {
169            sendKeys(KeyEvent.KEYCODE_DPAD_UP);
170            assertEquals(1, mListView.getChildCount());
171        }
172        assertEquals(listTop, mListView.getSelectedView().getTop());
173    }
174
175    /**
176     * Assuming the selected view is overlapping the bottom edge, how many times
177     * do I have to press down to get beyond it so that either:
178     * a) the next view is peeking in
179     * b) the selected view is the last item in the list, and we are scrolled to the bottom
180     * @return
181     */
182    private int getNumDownPressesToScrollDownAcrossSelected() {
183        View selected = mListView.getSelectedView();
184        int realBottom = mListView.getBottom() - mListView.getListPaddingBottom();
185        assertTrue("view should be overlapping bottom",
186                selected.getBottom() > realBottom);
187        assertTrue("view should be overlapping bottom",
188                selected.getTop() < realBottom);
189
190        int pixelsOffScreen = selected.getBottom() - realBottom;
191        return (pixelsOffScreen / mListView.getMaxScrollAmount()) + 1;
192    }
193
194}
195