11ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik/* 21ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * Copyright (C) 2016 The Android Open Source Project 31ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * 41ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * Licensed under the Apache License, Version 2.0 (the "License"); 51ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * you may not use this file except in compliance with the License. 61ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * You may obtain a copy of the License at 71ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * 81ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * http://www.apache.org/licenses/LICENSE-2.0 91ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * 101ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * Unless required by applicable law or agreed to in writing, software 111ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * distributed under the License is distributed on an "AS IS" BASIS, 121ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * See the License for the specific language governing permissions and 141ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik * limitations under the License. 151ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik */ 161ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 171ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craikpackage android.support.v7.widget; 181ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 19e9f9cd8d0e9008340985d17a2541ab24b3adb391Aurimas Liutikasimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL; 20e9f9cd8d0e9008340985d17a2541ab24b3adb391Aurimas Liutikasimport static android.support.v7.widget.LinearLayoutManager.VERTICAL; 21e9f9cd8d0e9008340985d17a2541ab24b3adb391Aurimas Liutikas 22e9f9cd8d0e9008340985d17a2541ab24b3adb391Aurimas Liutikasimport static junit.framework.Assert.assertEquals; 23e9f9cd8d0e9008340985d17a2541ab24b3adb391Aurimas Liutikasimport static junit.framework.Assert.assertTrue; 24e9f9cd8d0e9008340985d17a2541ab24b3adb391Aurimas Liutikas 25d8b7d71ac23c6bf56d2ae774eccb41731f77205aChris Craikimport android.os.Build; 26754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport android.support.test.filters.MediumTest; 27d8b7d71ac23c6bf56d2ae774eccb41731f77205aChris Craikimport android.support.test.filters.SdkSuppress; 281ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 291ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craikimport org.junit.Test; 301ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craikimport org.junit.runner.RunWith; 311ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craikimport org.junit.runners.Parameterized; 321ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 331ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craikimport java.util.ArrayList; 341ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craikimport java.util.Arrays; 351ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craikimport java.util.List; 361ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craikimport java.util.concurrent.TimeUnit; 371ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 381ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik@RunWith(Parameterized.class) 39d8b7d71ac23c6bf56d2ae774eccb41731f77205aChris Craik@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP) 401ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craikpublic class StaggeredGridLayoutManagerCacheTest extends BaseStaggeredGridLayoutManagerTest { 411ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 421ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik final Config mConfig; 431ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik final int mDx; 441ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik final int mDy; 451ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 461ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik public StaggeredGridLayoutManagerCacheTest(Config config, int dx, int dy) { 471ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik mConfig = config; 481ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik mDx = dx; 491ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik mDy = dy; 501ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 511ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 52e9f9cd8d0e9008340985d17a2541ab24b3adb391Aurimas Liutikas @Parameterized.Parameters(name = "config:{0},dx:{1},dy:{2}") 531ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik public static List<Object[]> getParams() { 541ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik List<Object[]> result = new ArrayList<>(); 551ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik List<Config> configs = createBaseVariations(); 561ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik for (Config config : configs) { 571ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik for (int dx : new int[] {-1, 0, 1}) { 581ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik for (int dy : new int[] {-1, 0, 1}) { 591ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik result.add(new Object[]{config, dx, dy}); 601ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 611ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 621ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 631ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik return result; 641ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 651ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 661ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik private ArrayList<RecyclerView.ViewHolder> cachedViews() { 671ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik return mRecyclerView.mRecycler.mCachedViews; 681ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 691ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 701ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik private boolean cachedViewsContains(int position) { 711ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik // Note: can't make assumptions about order here, so just check all cached views 721ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik for (int i = 0; i < cachedViews().size(); i++) { 731ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik if (cachedViews().get(i).getAdapterPosition() == position) return true; 741ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 751ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik return false; 761ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 771ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 781ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik public int findFirstVisibleItemPosition() { 791ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik int[] positions = new int[mConfig.mSpanCount]; 801ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik mLayoutManager.findFirstVisibleItemPositions(positions); 811ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik Arrays.sort(positions); 821ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik return positions[0]; 831ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 841ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 851ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik public int findLastVisibleItemPosition() { 861ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik int[] positions = new int[mConfig.mSpanCount]; 871ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik mLayoutManager.findLastVisibleItemPositions(positions); 881ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik Arrays.sort(positions); 891ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik return positions[mConfig.mSpanCount - 1]; 901ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 911ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 921ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik @MediumTest 931ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik @Test 941ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik public void cacheAndPrefetch() throws Throwable { 951ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik final Config config = (Config) mConfig.clone(); 961ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik setupByConfig(config); 971ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik waitFirstLayout(); 981ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 9942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 1001ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik @Override 1011ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik public void run() { 1021ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik // pretend to have an extra 5s before next frame so prefetch won't abort early 1031ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik ((WrappedRecyclerView)mRecyclerView).setDrawingTimeOffset(5000); 1041ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 1051ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik // scroll to the middle, so we can move in either direction 1061ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik mRecyclerView.scrollToPosition(mConfig.mItemCount / 2); 1071ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 1081ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik }); 1091ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 1101ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik mRecyclerView.setItemViewCacheSize(0); 1111ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik { 1121ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik mLayoutManager.expectPrefetch(1); 11342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 1141ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik @Override 1151ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik public void run() { 1161ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik mRecyclerView.mRecycler.recycleAndClearCachedViews(); 11707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik mRecyclerView.mGapWorker.postFromTraversal(mRecyclerView, mDx, mDy); 1181ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 1191ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik // Lie about post time, so prefetch executes even if it is delayed 12007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik mRecyclerView.mGapWorker.mPostTimeNs += TimeUnit.SECONDS.toNanos(5); 1211ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 1221ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik }); 1231ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik mLayoutManager.waitForPrefetch(1); 1241ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 1251ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 12642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 1271ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik @Override 1281ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik public void run() { 1291ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik // validate cache state on UI thread 1301ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik if ((config.mOrientation == HORIZONTAL && mDx == 0) 1311ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik || (config.mOrientation == VERTICAL && mDy == 0)) { 1321ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik assertEquals(0, cachedViews().size()); 1331ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } else { 1341ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik assertEquals(config.mSpanCount, cachedViews().size()); 1351ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 1361ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik boolean reverseScroll = config.mOrientation == HORIZONTAL ? mDx < 0 : mDy < 0; 1371ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik int lastVisibleItemPosition = findLastVisibleItemPosition(); 1381ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik int firstVisibleItemPosition = findFirstVisibleItemPosition(); 1391ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik 1401ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik for (int i = 0; i < config.mSpanCount; i++) { 1411ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik if (mConfig.mReverseLayout == reverseScroll) { 1421ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik // Pos scroll on pos layout, or reverse scroll on reverse layout 1431ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik // = toward last 1441ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik assertTrue(cachedViewsContains(lastVisibleItemPosition + 1 + i)); 1451ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } else { 1461ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik // Pos scroll on reverse layout, or reverse scroll on pos layout 1471ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik // = toward first 1481ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik assertTrue(cachedViewsContains(firstVisibleItemPosition - 1 - i)); 1491ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 1501ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 1511ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 1521ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 1531ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik }); 1541ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik } 1551ce43e3fefb07662431e9fffe62c40242a52cac6Chris Craik} 156