1/*
2 * Copyright (C) 2017 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.support.v4.widget;
18
19import static org.junit.Assert.assertEquals;
20import static org.junit.Assert.assertFalse;
21import static org.junit.Assert.assertTrue;
22
23import android.support.annotation.NonNull;
24import android.support.annotation.Nullable;
25import android.support.compat.test.R;
26import android.support.test.filters.SmallTest;
27import android.support.test.rule.ActivityTestRule;
28import android.support.v4.BaseInstrumentationTestCase;
29import android.view.LayoutInflater;
30import android.view.View;
31import android.view.ViewGroup;
32import android.view.ViewTreeObserver.OnGlobalLayoutListener;
33import android.widget.BaseAdapter;
34import android.widget.ListView;
35import android.widget.TextView;
36
37import junit.framework.Assert;
38
39import org.junit.Before;
40import org.junit.Test;
41
42import java.util.concurrent.CountDownLatch;
43import java.util.concurrent.TimeUnit;
44
45@SmallTest
46public class ListViewCompatTest extends BaseInstrumentationTestCase<ListViewTestActivity> {
47    private ListView mListView;
48
49    public ListViewCompatTest() {
50        super(ListViewTestActivity.class);
51    }
52
53    @Before
54    public void setUp() throws Throwable {
55        mListView = (ListView) mActivityTestRule.getActivity().findViewById(R.id.content);
56        runOnMainAndLayoutSync(mActivityTestRule, mListView, new Runnable() {
57            @Override
58            public void run() {
59                mListView.setAdapter(new BaseAdapter() {
60                    @Override
61                    public int getCount() {
62                        return 500;
63                    }
64
65                    @Override
66                    public Object getItem(int position) {
67                        return null;
68                    }
69
70                    @Override
71                    public long getItemId(int position) {
72                        return position;
73                    }
74
75                    @Override
76                    public View getView(int position, View convertView, ViewGroup parent) {
77                        if (convertView == null) {
78                            convertView = LayoutInflater.from(mListView.getContext()).inflate(
79                                    R.layout.list_view_row, parent, false);
80                        }
81                        TextView result = (TextView) convertView;
82                        result.setText("row #" + (position + 1));
83                        return result;
84                    }
85                });
86            }
87        }, false);
88    }
89
90    private void runOnMainAndLayoutSync(@NonNull final ActivityTestRule activityTestRule,
91            @NonNull final View view, @Nullable final Runnable runner, final boolean forceLayout)
92            throws Throwable {
93        final View rootView = view.getRootView();
94
95        final CountDownLatch latch = new CountDownLatch(1);
96
97        activityTestRule.runOnUiThread(new Runnable() {
98            @Override
99            public void run() {
100                final OnGlobalLayoutListener listener = new OnGlobalLayoutListener() {
101                    @Override
102                    public void onGlobalLayout() {
103                        rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
104                        // countdown immediately since the layout we were waiting on has happened
105                        latch.countDown();
106                    }
107                };
108
109                rootView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
110
111                if (runner != null) {
112                    runner.run();
113                }
114
115                if (forceLayout) {
116                    rootView.requestLayout();
117                }
118            }
119        });
120
121        try {
122            Assert.assertTrue("Expected layout pass within 5 seconds",
123                    latch.await(5, TimeUnit.SECONDS));
124        } catch (InterruptedException e) {
125            throw new RuntimeException(e);
126        }
127    }
128
129    @Test
130    public void testCanScroll() throws Throwable {
131        final int itemCount = mListView.getAdapter().getCount();
132
133        assertEquals(0, mListView.getFirstVisiblePosition());
134
135        // Verify that when we're at the top of the list, we can't scroll up but we can scroll
136        // down.
137        assertFalse(ListViewCompat.canScrollList(mListView, -1));
138        assertTrue(ListViewCompat.canScrollList(mListView, 1));
139
140        // Scroll down to the very end of the list
141        runOnMainAndLayoutSync(mActivityTestRule, mListView,
142                new Runnable() {
143                    @Override
144                    public void run() {
145                        mListView.setStackFromBottom(true);
146                    }
147                }, false);
148        assertEquals(itemCount - 1, mListView.getLastVisiblePosition());
149
150        // Verify that when we're at the bottom of the list, we can't scroll down but we can scroll
151        // up.
152        assertFalse(ListViewCompat.canScrollList(mListView, 1));
153        assertTrue(ListViewCompat.canScrollList(mListView, -1));
154    }
155}
156