19df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov/*
29df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov * Copyright (C) 2017 The Android Open Source Project
39df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov *
49df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov * Licensed under the Apache License, Version 2.0 (the "License");
59df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov * you may not use this file except in compliance with the License.
69df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov * You may obtain a copy of the License at
79df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov *
89df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov *      http://www.apache.org/licenses/LICENSE-2.0
99df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov *
109df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov * Unless required by applicable law or agreed to in writing, software
119df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov * distributed under the License is distributed on an "AS IS" BASIS,
129df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov * See the License for the specific language governing permissions and
149df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov * limitations under the License.
159df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov */
169df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
179df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovpackage android.support.v4.widget;
189df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
199df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport static org.junit.Assert.assertEquals;
209df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport static org.junit.Assert.assertFalse;
219df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport static org.junit.Assert.assertTrue;
229df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
239df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.support.annotation.NonNull;
249df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.support.annotation.Nullable;
259df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.support.compat.test.R;
269df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.support.test.filters.SmallTest;
279df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.support.test.rule.ActivityTestRule;
289df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.support.v4.BaseInstrumentationTestCase;
299df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.view.LayoutInflater;
309df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.view.View;
319df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.view.ViewGroup;
329df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.view.ViewTreeObserver.OnGlobalLayoutListener;
339df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.widget.BaseAdapter;
349df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.widget.ListView;
359df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport android.widget.TextView;
369df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
379df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport junit.framework.Assert;
389df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
399df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport org.junit.Before;
409df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport org.junit.Test;
419df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
429df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport java.util.concurrent.CountDownLatch;
439df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovimport java.util.concurrent.TimeUnit;
449df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
459df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov@SmallTest
469df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikovpublic class ListViewCompatTest extends BaseInstrumentationTestCase<ListViewTestActivity> {
479df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov    private ListView mListView;
489df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
499df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov    public ListViewCompatTest() {
509df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        super(ListViewTestActivity.class);
519df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov    }
529df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
539df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov    @Before
549df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov    public void setUp() throws Throwable {
559df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        mListView = (ListView) mActivityTestRule.getActivity().findViewById(R.id.content);
569df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        runOnMainAndLayoutSync(mActivityTestRule, mListView, new Runnable() {
579df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov            @Override
589df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov            public void run() {
599df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                mListView.setAdapter(new BaseAdapter() {
609df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    @Override
619df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    public int getCount() {
629df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                        return 500;
639df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    }
649df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
659df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    @Override
669df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    public Object getItem(int position) {
679df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                        return null;
689df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    }
699df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
709df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    @Override
719df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    public long getItemId(int position) {
729df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                        return position;
739df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    }
749df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
759df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    @Override
769df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    public View getView(int position, View convertView, ViewGroup parent) {
779df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                        if (convertView == null) {
789df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                            convertView = LayoutInflater.from(mListView.getContext()).inflate(
799df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                                    R.layout.list_view_row, parent, false);
809df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                        }
819df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                        TextView result = (TextView) convertView;
829df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                        result.setText("row #" + (position + 1));
839df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                        return result;
849df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    }
859df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                });
869df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov            }
879df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        }, false);
889df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov    }
899df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
909df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov    private void runOnMainAndLayoutSync(@NonNull final ActivityTestRule activityTestRule,
919df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov            @NonNull final View view, @Nullable final Runnable runner, final boolean forceLayout)
929df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov            throws Throwable {
939df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        final View rootView = view.getRootView();
949df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
959df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        final CountDownLatch latch = new CountDownLatch(1);
969df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
979df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        activityTestRule.runOnUiThread(new Runnable() {
989df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov            @Override
999df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov            public void run() {
1009df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                final OnGlobalLayoutListener listener = new OnGlobalLayoutListener() {
1019df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    @Override
1029df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    public void onGlobalLayout() {
1039df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                        rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
1049df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                        // countdown immediately since the layout we were waiting on has happened
1059df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                        latch.countDown();
1069df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    }
1079df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                };
1089df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
1099df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                rootView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
1109df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
1119df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                if (runner != null) {
1129df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    runner.run();
1139df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                }
1149df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
1159df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                if (forceLayout) {
1169df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    rootView.requestLayout();
1179df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                }
1189df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov            }
1199df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        });
1209df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
1219df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        try {
1229df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov            Assert.assertTrue("Expected layout pass within 5 seconds",
1239df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    latch.await(5, TimeUnit.SECONDS));
1249df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        } catch (InterruptedException e) {
1259df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov            throw new RuntimeException(e);
1269df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        }
1279df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov    }
1289df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
1299df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov    @Test
1309df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov    public void testCanScroll() throws Throwable {
1319df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        final int itemCount = mListView.getAdapter().getCount();
1329df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
1339df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        assertEquals(0, mListView.getFirstVisiblePosition());
1349df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
1359df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        // Verify that when we're at the top of the list, we can't scroll up but we can scroll
1369df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        // down.
1379df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        assertFalse(ListViewCompat.canScrollList(mListView, -1));
1389df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        assertTrue(ListViewCompat.canScrollList(mListView, 1));
1399df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
1409df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        // Scroll down to the very end of the list
1419df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        runOnMainAndLayoutSync(mActivityTestRule, mListView,
1429df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                new Runnable() {
1439df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    @Override
1449df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    public void run() {
1459df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                        mListView.setStackFromBottom(true);
1469df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                    }
1479df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov                }, false);
1489df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        assertEquals(itemCount - 1, mListView.getLastVisiblePosition());
1499df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov
1509df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        // Verify that when we're at the bottom of the list, we can't scroll down but we can scroll
1519df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        // up.
1529df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        assertFalse(ListViewCompat.canScrollList(mListView, 1));
1539df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov        assertTrue(ListViewCompat.canScrollList(mListView, -1));
1549df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov    }
1559df73e86fb0949ee8c5afee0bf519982f59c016dKirill Grouchnikov}
156