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