1d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase/* 2d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Copyright (C) 2014 The Android Open Source Project 3d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * 4d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Licensed under the Apache License, Version 2.0 (the "License"); 5d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * you may not use this file except in compliance with the License. 6d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * You may obtain a copy of the License at 7d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * 8d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * http://www.apache.org/licenses/LICENSE-2.0 9d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * 10d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Unless required by applicable law or agreed to in writing, software 11d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * distributed under the License is distributed on an "AS IS" BASIS, 12d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * See the License for the specific language governing permissions and 14d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * limitations under the License. 15d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase */ 16d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 17d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haasepackage android.support.v7.widget; 18d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 19b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.content.Context; 20888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyarimport android.graphics.Canvas; 21b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.util.AttributeSet; 22b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.util.Log; 23b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.view.View; 24504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup; 25b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 2611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.ArrayList; 278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport java.util.HashMap; 2811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.HashSet; 2911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.List; 3011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.Map; 3111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.Set; 32888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyarimport java.util.concurrent.CountDownLatch; 33d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit; 34d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 35d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 36d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haasepublic class RecyclerViewAnimationsTest extends BaseRecyclerViewInstrumentationTest { 37d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 38668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar private static final boolean DEBUG = false; 39b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 40b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private static final String TAG = "RecyclerViewAnimationsTest"; 41b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 42b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar AnimationLayoutManager mLayoutManager; 43b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 44b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar TestAdapter mTestAdapter; 45b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 46b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public RecyclerViewAnimationsTest() { 47b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super(DEBUG); 48b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 49b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 50b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 51b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar protected void setUp() throws Exception { 52b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super.setUp(); 53b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 54b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 55b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView setupBasic(int itemCount) throws Throwable { 56b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return setupBasic(itemCount, 0, itemCount); 57b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 58b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 59b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView setupBasic(int itemCount, int firstLayoutStartIndex, int firstLayoutItemCount) 60b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar throws Throwable { 6111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar return setupBasic(itemCount, firstLayoutStartIndex, firstLayoutItemCount, null); 6211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 6311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar 6411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar RecyclerView setupBasic(int itemCount, int firstLayoutStartIndex, int firstLayoutItemCount, 6511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar TestAdapter testAdapter) 6611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar throws Throwable { 67888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar final TestRecyclerView recyclerView = new TestRecyclerView(getActivity()); 68b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar recyclerView.setHasFixedSize(true); 6911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar if (testAdapter == null) { 7011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar mTestAdapter = new TestAdapter(itemCount); 7111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } else { 7211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar mTestAdapter = testAdapter; 7311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 74b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar recyclerView.setAdapter(mTestAdapter); 75b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager = new AnimationLayoutManager(); 76b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar recyclerView.setLayoutManager(mLayoutManager); 77b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = firstLayoutStartIndex; 78b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = firstLayoutItemCount; 79b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 80b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(1); 81888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar recyclerView.expectDraw(1); 82b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setRecyclerView(recyclerView); 83b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 84888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar recyclerView.waitForDraw(1); 85888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar mLayoutManager.mOnLayoutCallbacks.reset(); 868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getInstrumentation().waitForIdleSync(); 87b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar assertEquals("extra layouts should not happen", 1, mLayoutManager.getTotalLayoutCount()); 888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals("all expected children should be laid out", firstLayoutItemCount, 898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.getChildCount()); 90b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return recyclerView; 91b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 92b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 930ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar public void testDetachBeforeAnimations() throws Throwable { 940ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar setupBasic(10, 0, 5); 950ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar final RecyclerView rv = mRecyclerView; 960ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar waitForAnimations(2); 970ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar final DefaultItemAnimator animator = new DefaultItemAnimator() { 980ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar @Override 990ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar public void runPendingAnimations() { 1000ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar super.runPendingAnimations(); 1010ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar } 1020ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar }; 1030ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar rv.setItemAnimator(animator); 1040ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar mLayoutManager.expectLayouts(2); 1050ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar mTestAdapter.deleteAndNotify(3, 4); 1060ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar mLayoutManager.waitForLayout(2); 1070ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar removeRecyclerView(); 1080ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar assertNull("test sanity check RV should be removed", rv.getParent()); 1090ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar assertEquals("no views should be hidden", 0, rv.mChildHelper.mHiddenViews.size()); 1100ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar assertFalse("there should not be any animations running", animator.isRunning()); 1110ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar } 1120ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar 11342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar public void testPreLayoutPositionCleanup() throws Throwable { 11442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar setupBasic(4, 0, 4); 11542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar mLayoutManager.expectLayouts(2); 11642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() { 11742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar @Override 11842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar void beforePreLayout(RecyclerView.Recycler recycler, 11942c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar AnimationLayoutManager lm, RecyclerView.State state) { 12042c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar mLayoutMin = 0; 12142c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar mLayoutItemCount = 3; 12242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar } 12342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar 12442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar @Override 12542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar void beforePostLayout(RecyclerView.Recycler recycler, 12642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar AnimationLayoutManager layoutManager, 12742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar RecyclerView.State state) { 12842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar mLayoutMin = 0; 12942c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar mLayoutItemCount = 4; 13042c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar } 13142c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar }; 13242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar mTestAdapter.addAndNotify(0, 1); 13342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar mLayoutManager.waitForLayout(2); 13442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar 13542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar 13642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar 13742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar } 13842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar 13993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar public void testAddRemoveSamePass() throws Throwable { 14093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar final List<RecyclerView.ViewHolder> mRecycledViews 14193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar = new ArrayList<RecyclerView.ViewHolder>(); 14293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar TestAdapter adapter = new TestAdapter(50) { 14393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar @Override 14493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar public void onViewRecycled(TestViewHolder holder) { 14593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar super.onViewRecycled(holder); 14693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar mRecycledViews.add(holder); 14793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar } 14893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar }; 14993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar adapter.setHasStableIds(true); 15093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar setupBasic(50, 3, 5, adapter); 15193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar mRecyclerView.setItemViewCacheSize(0); 15293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar final ArrayList<RecyclerView.ViewHolder> addVH 15393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar = new ArrayList<RecyclerView.ViewHolder>(); 15493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar final ArrayList<RecyclerView.ViewHolder> removeVH 15593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar = new ArrayList<RecyclerView.ViewHolder>(); 15693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar 15793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar final ArrayList<RecyclerView.ViewHolder> moveVH 15893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar = new ArrayList<RecyclerView.ViewHolder>(); 15993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar 16093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar final View[] testView = new View[1]; 16193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar mRecyclerView.setItemAnimator(new DefaultItemAnimator() { 16293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar @Override 16393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar public boolean animateAdd(RecyclerView.ViewHolder holder) { 16493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar addVH.add(holder); 16593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar return true; 16693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar } 16793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar 16893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar @Override 16993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar public boolean animateRemove(RecyclerView.ViewHolder holder) { 17093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar removeVH.add(holder); 17193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar return true; 17293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar } 17393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar 17493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar @Override 17593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, 17693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar int toX, int toY) { 17793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar moveVH.add(holder); 17893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar return true; 17993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar } 18093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar }); 18193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() { 18293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar @Override 18393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar void afterPreLayout(RecyclerView.Recycler recycler, 18493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar AnimationLayoutManager layoutManager, 18593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar RecyclerView.State state) { 18693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar super.afterPreLayout(recycler, layoutManager, state); 18793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar testView[0] = recycler.getViewForPosition(45); 18893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar testView[0].measure(View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.AT_MOST), 18993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.AT_MOST)); 19093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar testView[0].layout(10, 10, 10 + testView[0].getMeasuredWidth(), 19193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar 10 + testView[0].getMeasuredHeight()); 19293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar layoutManager.addView(testView[0], 4); 19393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar } 19493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar 19593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar @Override 19693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar void afterPostLayout(RecyclerView.Recycler recycler, 19793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar AnimationLayoutManager layoutManager, 19893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar RecyclerView.State state) { 19993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar super.afterPostLayout(recycler, layoutManager, state); 20093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar testView[0].layout(50, 50, 50 + testView[0].getMeasuredWidth(), 20193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar 50 + testView[0].getMeasuredHeight()); 20293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar layoutManager.addDisappearingView(testView[0], 4); 20393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar } 20493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar }; 20593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 3; 20693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 5; 20793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar mRecycledViews.clear(); 20893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar mLayoutManager.expectLayouts(2); 20993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar mTestAdapter.deleteAndNotify(3, 1); 21093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar mLayoutManager.waitForLayout(2); 21193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar 21293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar for (RecyclerView.ViewHolder vh : addVH) { 21393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar assertNotSame("add-remove item should not animate add", testView[0], vh.itemView); 21493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar } 21593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar for (RecyclerView.ViewHolder vh : moveVH) { 21693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar assertNotSame("add-remove item should not animate move", testView[0], vh.itemView); 21793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar } 21893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar for (RecyclerView.ViewHolder vh : removeVH) { 21993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar assertNotSame("add-remove item should not animate remove", testView[0], vh.itemView); 22093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar } 22193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar boolean found = false; 22293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar for (RecyclerView.ViewHolder vh : mRecycledViews) { 22393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar found |= vh.itemView == testView[0]; 22493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar } 22593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar assertTrue("added-removed view should be recycled", found); 22693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar } 22793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar 2287c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen public void testChangeAnimations() throws Throwable { 2297c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final boolean[] booleans = {true, false}; 2307c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen for (boolean supportsChange : booleans) { 2317c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen for (boolean changeType : booleans) { 2327c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen for (boolean hasStableIds : booleans) { 2337c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen for (boolean deleteSomeItems : booleans) { 2347c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen changeAnimTest(supportsChange, changeType, hasStableIds, deleteSomeItems); 2357c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 2367c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen removeRecyclerView(); 2377c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 2387c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 2397c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 2407c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 2417c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen public void changeAnimTest(final boolean supportsChangeAnim, final boolean changeType, 2427c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final boolean hasStableIds, final boolean deleteSomeItems) throws Throwable { 2437c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final int changedIndex = 3; 2447c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final int defaultType = 1; 2457c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final AtomicInteger changedIndexNewType = new AtomicInteger(defaultType); 2467c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final String logPrefix = "supportsChangeAnim:" + supportsChangeAnim + 2477c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen ", change view type:" + changeType + 2487c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen ", has stable ids:" + hasStableIds + 2497c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen ", force predictive:" + deleteSomeItems; 2507c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen TestAdapter testAdapter = new TestAdapter(10) { 2517c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen @Override 2527c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen public int getItemViewType(int position) { 2537c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen return position == changedIndex ? changedIndexNewType.get() : defaultType; 2547c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 2557c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 2567c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen @Override 2577c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen public TestViewHolder onCreateViewHolder(ViewGroup parent, 2587c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen int viewType) { 2597c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen TestViewHolder vh = super.onCreateViewHolder(parent, viewType); 2607c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (DEBUG) { 2617c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen Log.d(TAG, logPrefix + " onCreateVH" + vh.toString()); 2627c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 2637c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen return vh; 2647c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 2657c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 2667c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen @Override 2677c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen public void onBindViewHolder(TestViewHolder holder, 2687c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen int position) { 2697c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen super.onBindViewHolder(holder, position); 2707c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (DEBUG) { 2717c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen Log.d(TAG, logPrefix + " onBind to " + position + "" + holder.toString()); 2727c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 2737c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 2747c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen }; 2757c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen testAdapter.setHasStableIds(hasStableIds); 2767c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen setupBasic(testAdapter.getItemCount(), 0, 10, testAdapter); 2777c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mRecyclerView.getItemAnimator().setSupportsChangeAnimations(supportsChangeAnim); 2787c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 2797c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final RecyclerView.ViewHolder toBeChangedVH = 280115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar mRecyclerView.findViewHolderForLayoutPosition(changedIndex); 2817c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() { 2827c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen @Override 2837c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen void afterPreLayout(RecyclerView.Recycler recycler, 2847c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen AnimationLayoutManager layoutManager, 2857c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen RecyclerView.State state) { 286115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition( 287115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar changedIndex); 2887c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (supportsChangeAnim) { 2897c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen assertTrue(logPrefix + " changed view holder should have correct flag" 2907c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen , vh.isChanged()); 2917c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } else { 2927c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen assertFalse(logPrefix + " changed view holder should have correct flag" 2937c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen , vh.isChanged()); 2947c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 2957c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 2967c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 2977c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen @Override 2987c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen void afterPostLayout(RecyclerView.Recycler recycler, 2997c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen AnimationLayoutManager layoutManager, RecyclerView.State state) { 300115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition( 301115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar changedIndex); 3027c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen assertFalse(logPrefix + "VH should not be marked as changed", vh.isChanged()); 3037c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (supportsChangeAnim) { 3047c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen assertNotSame(logPrefix + "a new VH should be given if change is supported", 3057c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen toBeChangedVH, vh); 3067c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } else if (!changeType && hasStableIds) { 3077c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen assertSame(logPrefix + "if change animations are not supported but we have " 3087c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen + "stable ids, same view holder should be returned", toBeChangedVH, vh); 3097c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 3107c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen super.beforePostLayout(recycler, layoutManager, state); 3117c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 3127c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen }; 3137c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mLayoutManager.expectLayouts(1); 3147c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (changeType) { 3157c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen changedIndexNewType.set(defaultType + 1); 3167c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 3177c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (deleteSomeItems) { 3187c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen runTestOnUiThread(new Runnable() { 3197c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen @Override 3207c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen public void run() { 3217c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen try { 3227c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mTestAdapter.deleteAndNotify(changedIndex + 2, 1); 3237c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mTestAdapter.notifyItemChanged(3); 3247c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } catch (Throwable throwable) { 3257c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen throwable.printStackTrace(); 3267c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 3277c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 3287c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 3297c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen }); 3307c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } else { 3317c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mTestAdapter.notifyItemChanged(3); 3327c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 3337c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 3347c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mLayoutManager.waitForLayout(2); 3357c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 3367c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 337504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void testRecycleDuringAnimations() throws Throwable { 338504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final AtomicInteger childCount = new AtomicInteger(0); 339504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestAdapter adapter = new TestAdapter(1000) { 340504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 341504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public TestViewHolder onCreateViewHolder(ViewGroup parent, 342504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int viewType) { 343504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar childCount.incrementAndGet(); 344504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return super.onCreateViewHolder(parent, viewType); 345504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 346504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 347504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar setupBasic(1000, 10, 20, adapter); 348504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 10; 349504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 20; 350504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 351504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool() { 352504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 353504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 354504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.putRecycledView(scrap); 355504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar childCount.decrementAndGet(); 356504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 357504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 358504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 359504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 360504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType); 361504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (recycledView != null) { 362504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar childCount.incrementAndGet(); 363504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 364504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return recycledView; 365504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 366504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }); 367504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 368504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now keep adding children to trigger more children being created etc. 369504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 370504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(15, 1); 371504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(50); 372504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 373504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 374504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 375504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 376504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 377504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 378504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 379b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar public void testNotifyDataSetChanged() throws Throwable { 380b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar setupBasic(10, 3, 4); 381b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar int layoutCount = mLayoutManager.mTotalLayoutCount; 382b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar mLayoutManager.expectLayouts(1); 383b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar runTestOnUiThread(new Runnable() { 384b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar @Override 385b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar public void run() { 386b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar try { 387b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar mTestAdapter.deleteAndNotify(4, 1); 388cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase mTestAdapter.dispatchDataSetChanged(); 389b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar } catch (Throwable throwable) { 390b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar throwable.printStackTrace(); 391b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar } 392b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar 393b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar } 394b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar }); 395b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar mLayoutManager.waitForLayout(2); 396b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar getInstrumentation().waitForIdleSync(); 397b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar assertEquals("on notify data set changed, predictive animations should not run", 398b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar layoutCount + 1, mLayoutManager.mTotalLayoutCount); 399b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar mLayoutManager.expectLayouts(2); 400b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar mTestAdapter.addAndNotify(4, 2); 401b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar // make sure animations recover 402b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar mLayoutManager.waitForLayout(2); 403b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar } 404b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar 405e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void testStableIdNotifyDataSetChanged() throws Throwable { 406e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar final int itemCount = 20; 407e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar List<Item> initialSet = new ArrayList<Item>(); 408e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar final TestAdapter adapter = new TestAdapter(itemCount) { 409e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar @Override 410e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public long getItemId(int position) { 411e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar return mItems.get(position).mId; 412e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 413e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar }; 414e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar adapter.setHasStableIds(true); 415e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar initialSet.addAll(adapter.mItems); 416e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar positionStatesTest(itemCount, 5, 5, adapter, new AdapterOps() { 417e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar @Override 418e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar void onRun(TestAdapter testAdapter) throws Throwable { 419e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar Item item5 = adapter.mItems.get(5); 420e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar Item item6 = adapter.mItems.get(6); 421e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar item5.mAdapterIndex = 6; 422e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar item6.mAdapterIndex = 5; 423e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar adapter.mItems.remove(5); 424e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar adapter.mItems.add(6, item5); 425cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase adapter.dispatchDataSetChanged(); 426e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar //hacky, we support only 1 layout pass 427e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mLayoutManager.layoutLatch.countDown(); 428e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 429e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar }, PositionConstraint.scrap(6, -1, 5), PositionConstraint.scrap(5, -1, 6), 430e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar PositionConstraint.scrap(7, -1, 7), PositionConstraint.scrap(8, -1, 8), 431e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar PositionConstraint.scrap(9, -1, 9)); 432e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar // now mix items. 433e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 434e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 435d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 4368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testGetItemForDeletedView() throws Throwable { 4378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getItemForDeletedViewTest(false); 4388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getItemForDeletedViewTest(true); 43911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 44011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar 4418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void getItemForDeletedViewTest(boolean stableIds) throws Throwable { 44211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final Set<Integer> itemViewTypeQueries = new HashSet<Integer>(); 44311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final Set<Integer> itemIdQueries = new HashSet<Integer>(); 44411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar TestAdapter adapter = new TestAdapter(10) { 44511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar @Override 44611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar public int getItemViewType(int position) { 44711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemViewTypeQueries.add(position); 44811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar return super.getItemViewType(position); 44911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 45011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar 45111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar @Override 45211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar public long getItemId(int position) { 45311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemIdQueries.add(position); 45411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar return mItems.get(position).mId; 45511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 45611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar }; 45711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar adapter.setHasStableIds(stableIds); 45811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar setupBasic(10, 0, 10, adapter); 45911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar assertEquals("getItemViewType for all items should be called", 10, 46011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemViewTypeQueries.size()); 46111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar if (adapter.hasStableIds()) { 46211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar assertEquals("getItemId should be called when adapter has stable ids", 10, 46311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemIdQueries.size()); 46411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } else { 46511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar assertEquals("getItemId should not be called when adapter does not have stable ids", 0, 46611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemIdQueries.size()); 46711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 46811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemViewTypeQueries.clear(); 46911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemIdQueries.clear(); 47011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar mLayoutManager.expectLayouts(2); 47111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar // delete last two 47211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final int deleteStart = 8; 47311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final int deleteCount = adapter.getItemCount() - deleteStart; 47411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar adapter.deleteAndNotify(deleteStart, deleteCount); 47511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar mLayoutManager.waitForLayout(2); 47611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar for (int i = 0; i < deleteStart; i++) { 47711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar assertTrue("getItemViewType for existing item " + i + " should be called", 47811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemViewTypeQueries.contains(i)); 47911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar if (adapter.hasStableIds()) { 48011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar assertTrue("getItemId for existing item " + i 48111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar + " should be called when adapter has stable ids", 48211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemIdQueries.contains(i)); 48311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 48411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 48511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar for (int i = deleteStart; i < deleteStart + deleteCount; i++) { 48611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar assertFalse("getItemViewType for deleted item " + i + " SHOULD NOT be called", 48711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemViewTypeQueries.contains(i)); 48811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar if (adapter.hasStableIds()) { 4898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertFalse("getItemId for deleted item " + i + " SHOULD NOT be called", 49011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemIdQueries.contains(i)); 49111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 49211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 49311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 49411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar 4958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testDeleteInvisibleMultiStep() throws Throwable { 4968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar setupBasic(1000, 1, 7); 4978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1; 4988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7; 4998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.expectLayouts(1); 5008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // try to trigger race conditions 5018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int targetItemCount = mTestAdapter.getItemCount(); 5028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < 100; i++) { 5038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.deleteAndNotify(new int[]{0, 1}, new int[]{7, 1}); 50490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar checkForMainThreadException(); 5058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar targetItemCount -= 2; 5068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // wait until main thread runnables are consumed 5088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar while (targetItemCount != mTestAdapter.getItemCount()) { 5098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Thread.sleep(100); 5108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.waitForLayout(2); 5128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testAddManyMultiStep() throws Throwable { 5158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar setupBasic(10, 1, 7); 5168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1; 5178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7; 5188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.expectLayouts(1); 5198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // try to trigger race conditions 5208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int targetItemCount = mTestAdapter.getItemCount(); 5218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < 100; i++) { 5228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.addAndNotify(0, 1); 5238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.addAndNotify(7, 1); 5248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar targetItemCount += 2; 5258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // wait until main thread runnables are consumed 5278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar while (targetItemCount != mTestAdapter.getItemCount()) { 5288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Thread.sleep(100); 5298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.waitForLayout(2); 5318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testBasicDelete() throws Throwable { 5348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar setupBasic(10); 5358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final OnLayoutCallbacks callbacks = new OnLayoutCallbacks() { 5368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 5378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void postDispatchLayout() { 5388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // verify this only in first layout 5398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals("deleted views should still be children of RV", 5408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.getChildCount() + mDeletedViewCount 5418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar , mRecyclerView.getChildCount()); 5428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 5458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void afterPreLayout(RecyclerView.Recycler recycler, 5468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar AnimationLayoutManager layoutManager, 5478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.State state) { 5488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.afterPreLayout(recycler, layoutManager, state); 5498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutItemCount = 3; 5508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutMin = 0; 5518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }; 5538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar callbacks.mLayoutItemCount = 10; 5548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar callbacks.setExpectedItemCounts(10, 3); 5558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.setOnLayoutCallbacks(callbacks); 5568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.expectLayouts(2); 5588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.deleteAndNotify(0, 7); 5598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.waitForLayout(2); 5608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar callbacks.reset();// when animations end another layout will happen 5618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 564d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar public void testAdapterChangeDuringScrolling() throws Throwable { 565d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar setupBasic(10); 566d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar final AtomicInteger onLayoutItemCount = new AtomicInteger(0); 567d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar final AtomicInteger onScrollItemCount = new AtomicInteger(0); 568d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 569d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar mLayoutManager.setOnLayoutCallbacks(new OnLayoutCallbacks() { 570d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar @Override 571d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar void onLayoutChildren(RecyclerView.Recycler recycler, 572d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar AnimationLayoutManager lm, RecyclerView.State state) { 573d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar onLayoutItemCount.set(state.getItemCount()); 574d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar super.onLayoutChildren(recycler, lm, state); 575d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 576d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 577d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar @Override 578d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { 579d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar onScrollItemCount.set(state.getItemCount()); 580d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar super.onScroll(dx, recycler, state); 581d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 582d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar }); 583d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar runTestOnUiThread(new Runnable() { 584d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar @Override 585d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar public void run() { 586d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar mTestAdapter.mItems.remove(5); 587d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar mTestAdapter.notifyItemRangeRemoved(5, 1); 588d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar mRecyclerView.scrollBy(0, 100); 589d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar assertTrue("scrolling while there are pending adapter updates should " 590d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar + "trigger a layout", mLayoutManager.mOnLayoutCallbacks.mLayoutCount > 0); 591d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar assertEquals("scroll by should be called w/ updated adapter count", 592d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar mTestAdapter.mItems.size(), onScrollItemCount.get()); 593d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 594d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 595d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar }); 596d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 597d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 59817d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar public void testNotifyDataSetChangedDuringScroll() throws Throwable { 59917d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar setupBasic(10); 60017d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar final AtomicInteger onLayoutItemCount = new AtomicInteger(0); 60117d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar final AtomicInteger onScrollItemCount = new AtomicInteger(0); 60217d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar 60317d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar mLayoutManager.setOnLayoutCallbacks(new OnLayoutCallbacks() { 60417d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar @Override 60517d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar void onLayoutChildren(RecyclerView.Recycler recycler, 60617d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar AnimationLayoutManager lm, RecyclerView.State state) { 60717d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar onLayoutItemCount.set(state.getItemCount()); 60817d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar super.onLayoutChildren(recycler, lm, state); 60917d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar } 61017d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar 61117d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar @Override 61217d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { 61317d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar onScrollItemCount.set(state.getItemCount()); 61417d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar super.onScroll(dx, recycler, state); 61517d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar } 61617d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar }); 61717d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar runTestOnUiThread(new Runnable() { 61817d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar @Override 61917d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar public void run() { 62017d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar mTestAdapter.mItems.remove(5); 62117d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar mTestAdapter.notifyDataSetChanged(); 62217d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar mRecyclerView.scrollBy(0, 100); 62317d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar assertTrue("scrolling while there are pending adapter updates should " 62417d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar + "trigger a layout", mLayoutManager.mOnLayoutCallbacks.mLayoutCount > 0); 62517d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar assertEquals("scroll by should be called w/ updated adapter count", 62617d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar mTestAdapter.mItems.size(), onScrollItemCount.get()); 62717d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar 62817d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar } 62917d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar }); 63017d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar } 63117d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar 632b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void testAddInvisibleAndVisible() throws Throwable { 633b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setupBasic(10, 1, 7); 634b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(2); 635b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(10, 12); 6362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mTestAdapter.addAndNotify(new int[]{0, 1}, new int[]{7, 1});// add a new item 0 // invisible 637b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 638b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 639b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 640b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void testAddInvisible() throws Throwable { 641b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setupBasic(10, 1, 7); 642b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(1); 643b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(10, 12); 6442d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mTestAdapter.addAndNotify(new int[]{0, 1}, new int[]{8, 1});// add a new item 0 645b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 646b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 647b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 648b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void testBasicAdd() throws Throwable { 649b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setupBasic(10); 650b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(2); 651b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setExpectedItemCounts(10, 13); 652b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mTestAdapter.addAndNotify(2, 3); 653b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 654b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 655b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 656888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar public TestRecyclerView getTestRecyclerView() { 657888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar return (TestRecyclerView) mRecyclerView; 658888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 659888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar 660888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar public void testRemoveScrapInvalidate() throws Throwable { 661888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar setupBasic(10); 662888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar TestRecyclerView testRecyclerView = getTestRecyclerView(); 663888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar mLayoutManager.expectLayouts(1); 664888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar testRecyclerView.expectDraw(1); 665888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar runTestOnUiThread(new Runnable() { 666888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar @Override 667888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar public void run() { 668888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar mTestAdapter.mItems.clear(); 669888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar mTestAdapter.notifyDataSetChanged(); 670888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 671888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar }); 672888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar mLayoutManager.waitForLayout(2); 673888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar testRecyclerView.waitForDraw(2); 674888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 675888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar 676b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void testDeleteVisibleAndInvisible() throws Throwable { 677b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setupBasic(11, 3, 5); //layout items 3 4 5 6 7 678b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(2); 6798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar setLayoutRange(3, 5); //layout previously invisible child 10 from end of the list 680b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setExpectedItemCounts(9, 8); 681b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mTestAdapter.deleteAndNotify(new int[]{4, 1}, new int[]{7, 2});// delete items 4, 8, 9 682b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 683b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 684b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 6858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testFindPositionOffset() throws Throwable { 6868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar setupBasic(10); 68790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() { 68890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 68990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar void beforePreLayout(RecyclerView.Recycler recycler, 69090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar AnimationLayoutManager lm, RecyclerView.State state) { 69190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.beforePreLayout(recycler, lm, state); 69290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar // [0,2,4] 69390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertEquals("offset check", 0, mAdapterHelper.findPositionOffset(0)); 69490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertEquals("offset check", 1, mAdapterHelper.findPositionOffset(2)); 69590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertEquals("offset check", 2, mAdapterHelper.findPositionOffset(4)); 69690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 69790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar }; 6988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar runTestOnUiThread(new Runnable() { 6998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 7008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 7018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // [0,1,2,3,4] 7028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // delete 1 7038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.notifyItemRangeRemoved(1, 1); 7048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // delete 3 7058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.notifyItemRangeRemoved(2, 1); 7068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }); 70890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mLayoutManager.waitForLayout(2); 7098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 711b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private void setLayoutRange(int start, int count) { 712b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = start; 713b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = count; 714b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 715b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 716b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private void setExpectedItemCounts(int preLayout, int postLayout) { 717b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(preLayout, postLayout); 718b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 719b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 720b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void testDeleteInvisible() throws Throwable { 721b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setupBasic(10, 1, 7); 7228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1; 7238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7; 724b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(1); 725b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(8, 8); 7268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.deleteAndNotify(new int[]{0, 1}, new int[]{7, 1});// delete item id 0,8 727b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 728b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 729b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 730e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar private CollectPositionResult findByPos(RecyclerView recyclerView, 731e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar RecyclerView.Recycler recycler, RecyclerView.State state, int position) { 732e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar View view = recycler.getViewForPosition(position, true); 733e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view); 734e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (vh.wasReturnedFromScrap()) { 735e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar vh.clearReturnedFromScrapFlag(); //keep data consistent. 736e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar return CollectPositionResult.fromScrap(vh); 737e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } else { 738e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar return CollectPositionResult.fromAdapter(vh); 7398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 742e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public Map<Integer, CollectPositionResult> collectPositions(RecyclerView recyclerView, 743e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar RecyclerView.Recycler recycler, RecyclerView.State state, int... positions) { 7448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Map<Integer, CollectPositionResult> positionToAdapterMapping 7458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar = new HashMap<Integer, CollectPositionResult>(); 7468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int position : positions) { 7478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (position < 0) { 7488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar continue; 7498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 750e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar positionToAdapterMapping.put(position, 751e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar findByPos(recyclerView, recycler, state, position)); 7528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return positionToAdapterMapping; 7548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testAddDelete2() throws Throwable { 7578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar positionStatesTest(5, 0, 5, new AdapterOps() { 7588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 1 2 3 4 7598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 1 2 a b 3 4 7608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 1 b 3 4 7618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // pre: 0 1 2 3 4 7628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // pre w/ adap: 0 1 2 b 3 4 763d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 7648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void onRun(TestAdapter adapter) throws Throwable { 7658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapter.addDeleteAndNotify(new int[]{3, 2}, new int[]{2, -2}); 766b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 7678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }, PositionConstraint.scrap(2, 2, -1), PositionConstraint.scrap(1, 1, 1), 7688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(3, 3, 3) 7698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ); 7708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 771d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 7728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testAddDelete1() throws Throwable { 7738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar positionStatesTest(5, 0, 5, new AdapterOps() { 7748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 1 2 3 4 7758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 1 2 a b 3 4 7768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 2 a b 3 4 7778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 c d 2 a b 3 4 7788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 c d 2 a 4 7798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // c d 2 a 4 7808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // pre: 0 1 2 3 4 781b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 7828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void onRun(TestAdapter adapter) throws Throwable { 7838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapter.addDeleteAndNotify(new int[]{3, 2}, new int[]{1, -1}, 7848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new int[]{1, 2}, new int[]{5, -2}, new int[]{0, -1}); 785d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 7868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }, PositionConstraint.scrap(0, 0, -1), PositionConstraint.scrap(1, 1, -1), 7878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(2, 2, 2), PositionConstraint.scrap(3, 3, -1), 7888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(4, 4, 4), PositionConstraint.adapter(0), 7898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.adapter(1), PositionConstraint.adapter(3) 7908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ); 7918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testAddSameIndexTwice() throws Throwable { 7948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar positionStatesTest(12, 2, 7, new AdapterOps() { 7958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 7968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void onRun(TestAdapter adapter) throws Throwable { 7978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapter.addAndNotify(new int[]{1, 2}, new int[]{5, 1}, new int[]{5, 1}, 7988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new int[]{11, 1}); 7998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }, PositionConstraint.adapterScrap(0, 0), PositionConstraint.adapterScrap(1, 3), 8018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(2, 2, 4), PositionConstraint.scrap(3, 3, 7), 8028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(4, 4, 8), PositionConstraint.scrap(7, 7, 12), 8038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(8, 8, 13) 8048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ); 8058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 8078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testDeleteTwice() throws Throwable { 8088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar positionStatesTest(12, 2, 7, new AdapterOps() { 8098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 8108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void onRun(TestAdapter adapter) throws Throwable { 8118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapter.deleteAndNotify(new int[]{0, 1}, new int[]{1, 1}, new int[]{7, 1}, 8128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new int[]{0, 1});// delete item ids 0,2,9,1 8138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }, PositionConstraint.scrap(2, 0, -1), PositionConstraint.scrap(3, 1, 0), 8158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(4, 2, 1), PositionConstraint.scrap(5, 3, 2), 8168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(6, 4, 3), PositionConstraint.scrap(8, 6, 5), 8178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.adapterScrap(7, 6), PositionConstraint.adapterScrap(8, 7) 8188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ); 8198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 821b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 8228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void positionStatesTest(int itemCount, int firstLayoutStartIndex, 823e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar int firstLayoutItemCount, AdapterOps adapterChanges, 824e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar final PositionConstraint... constraints) throws Throwable { 825e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar positionStatesTest(itemCount, firstLayoutStartIndex, firstLayoutItemCount, null, 826e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar adapterChanges, constraints); 827e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 828e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void positionStatesTest(int itemCount, int firstLayoutStartIndex, 829e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar int firstLayoutItemCount,TestAdapter adapter, AdapterOps adapterChanges, 830e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar final PositionConstraint... constraints) throws Throwable { 831e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar setupBasic(itemCount, firstLayoutStartIndex, firstLayoutItemCount, adapter); 832b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(2); 8338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() { 8348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 8358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void beforePreLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm, 8368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.State state) { 8378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.beforePreLayout(recycler, lm, state); 8388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar //harmless 8398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar lm.detachAndScrapAttachedViews(recycler); 8408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[] ids = new int[constraints.length]; 8418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < constraints.length; i++) { 8428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ids[i] = constraints[i].mPreLayoutPos; 8438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Map<Integer, CollectPositionResult> positions 845e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar = collectPositions(lm.mRecyclerView, recycler, state, ids); 8468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (PositionConstraint constraint : constraints) { 8478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (constraint.mPreLayoutPos != -1) { 8488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.validate(state, positions.get(constraint.mPreLayoutPos), 8498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar lm.getLog()); 8508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 8548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 8558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void beforePostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm, 8568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.State state) { 8578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.beforePostLayout(recycler, lm, state); 8588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar lm.detachAndScrapAttachedViews(recycler); 8598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[] ids = new int[constraints.length]; 8608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < constraints.length; i++) { 8618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ids[i] = constraints[i].mPostLayoutPos; 8628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Map<Integer, CollectPositionResult> positions 864e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar = collectPositions(lm.mRecyclerView, recycler, state, ids); 8658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (PositionConstraint constraint : constraints) { 8668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (constraint.mPostLayoutPos >= 0) { 8678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.validate(state, positions.get(constraint.mPostLayoutPos), 8688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar lm.getLog()); 8698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }; 8738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapterChanges.run(mTestAdapter); 874b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 875e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar checkForMainThreadException(); 8768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (PositionConstraint constraint : constraints) { 8778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.assertValidate(); 8788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 879b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 880b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 881b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar class AnimationLayoutManager extends TestLayoutManager { 882b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 8838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private int mTotalLayoutCount = 0; 8848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private String log; 8858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 886b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar OnLayoutCallbacks mOnLayoutCallbacks = new OnLayoutCallbacks() { 887b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }; 888b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 8898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 8908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 891b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 892c35968d173f900d8024bdf38174e2225c9a7f311Chet Haase public boolean supportsPredictiveItemAnimations() { 893b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return true; 894b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 895b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 8968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String getLog() { 8978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return log; 8988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 900668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar private String prepareLog(RecyclerView.Recycler recycler, RecyclerView.State state, boolean done) { 9018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar StringBuilder builder = new StringBuilder(); 902668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar builder.append("is pre layout:").append(state.isPreLayout()).append(", done:").append(done); 903668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar builder.append("\nViewHolders:\n"); 9048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (RecyclerView.ViewHolder vh : ((TestRecyclerView)mRecyclerView).collectViewHolders()) { 9058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar builder.append(vh).append("\n"); 9068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 9078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar builder.append("scrap:\n"); 9088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (RecyclerView.ViewHolder vh : recycler.getScrapList()) { 9098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar builder.append(vh).append("\n"); 9108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 911668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar 912668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (state.isPreLayout() && !done) { 9138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar log = "\n" + builder.toString(); 9148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 9158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar log += "\n" + builder.toString(); 9168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 9178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return log; 9188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 9198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 920b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 921b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void expectLayouts(int count) { 922b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super.expectLayouts(count); 923b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mOnLayoutCallbacks.mLayoutCount = 0; 924b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 925b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 926b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void setOnLayoutCallbacks(OnLayoutCallbacks onLayoutCallbacks) { 927b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mOnLayoutCallbacks = onLayoutCallbacks; 928b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 929b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 930b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 931b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public final void onLayoutChildren(RecyclerView.Recycler recycler, 932b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state) { 933b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar try { 9348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTotalLayoutCount++; 935668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar prepareLog(recycler, state, false); 936668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (state.isPreLayout()) { 937668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar validateOldPositions(recycler, state); 938668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } else { 939668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar validateClearedOldPositions(recycler, state); 940668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 941b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mOnLayoutCallbacks.onLayoutChildren(recycler, this, state); 942668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar prepareLog(recycler, state, true); 943b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } finally { 944b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.countDown(); 945d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 946b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 947b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 948668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar private void validateClearedOldPositions(RecyclerView.Recycler recycler, 949668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar RecyclerView.State state) { 950668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (getTestRecyclerView() == null) { 951668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar return; 952668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 953668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (RecyclerView.ViewHolder viewHolder : getTestRecyclerView().collectViewHolders()) { 954668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar assertEquals("there should NOT be an old position in post layout", 955668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar RecyclerView.NO_POSITION, viewHolder.mOldPosition); 956668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar assertEquals("there should NOT be a pre layout position in post layout", 957668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar RecyclerView.NO_POSITION, viewHolder.mPreLayoutPosition); 958668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 959668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 960668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar 961668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar private void validateOldPositions(RecyclerView.Recycler recycler, 962668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar RecyclerView.State state) { 963668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (getTestRecyclerView() == null) { 964668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar return; 965668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 966668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (RecyclerView.ViewHolder viewHolder : getTestRecyclerView().collectViewHolders()) { 967668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (!viewHolder.isRemoved() && !viewHolder.isInvalid()) { 968668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar assertTrue("there should be an old position in pre-layout", 969668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar viewHolder.mOldPosition != RecyclerView.NO_POSITION); 970668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 971668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 972668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 973668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar 9748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public int getTotalLayoutCount() { 9758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return mTotalLayoutCount; 9768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 9778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 978d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar @Override 979d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar public boolean canScrollVertically() { 980d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar return true; 981d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 982d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 983d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar @Override 984d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 985d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar RecyclerView.State state) { 986d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar mOnLayoutCallbacks.onScroll(dy, recycler, state); 987d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar return super.scrollVerticallyBy(dy, recycler, state); 988d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 989b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 990b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void onPostDispatchLayout() { 991b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mOnLayoutCallbacks.postDispatchLayout(); 992b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 993b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 994b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 995b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable { 996b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super.waitForLayout(timeout, timeUnit); 997b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar checkForMainThreadException(); 998b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 999d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 1000d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 1001b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar abstract class OnLayoutCallbacks { 1002b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1003b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int mLayoutMin = Integer.MIN_VALUE; 1004b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1005b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int mLayoutItemCount = Integer.MAX_VALUE; 1006b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1007b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int expectedPreLayoutItemCount = -1; 1008b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1009b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int expectedPostLayoutItemCount = -1; 1010b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1011b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int mDeletedViewCount; 1012b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 10138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int mLayoutCount = 0; 10148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 1015b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void setExpectedItemCounts(int preLayout, int postLayout) { 1016b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar expectedPreLayoutItemCount = preLayout; 1017b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar expectedPostLayoutItemCount = postLayout; 1018b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1019b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1020b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void reset() { 1021b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutMin = Integer.MIN_VALUE; 1022b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutItemCount = Integer.MAX_VALUE; 1023b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar expectedPreLayoutItemCount = -1; 1024b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar expectedPostLayoutItemCount = -1; 10258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutCount = 0; 1026b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1027b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1028b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void beforePreLayout(RecyclerView.Recycler recycler, 1029b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar AnimationLayoutManager lm, RecyclerView.State state) { 1030b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mDeletedViewCount = 0; 1031b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar for (int i = 0; i < lm.getChildCount(); i++) { 1032b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar View v = lm.getChildAt(i); 1033b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (lm.getLp(v).isItemRemoved()) { 1034b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mDeletedViewCount++; 1035b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1036b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1037b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1038b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1039b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void doLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm, 1040b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state) { 1041b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (DEBUG) { 1042b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar Log.d(TAG, "item count " + state.getItemCount()); 1043b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1044b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar lm.detachAndScrapAttachedViews(recycler); 1045b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar final int start = mLayoutMin == Integer.MIN_VALUE ? 0 : mLayoutMin; 1046b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar final int count = mLayoutItemCount 1047b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar == Integer.MAX_VALUE ? state.getItemCount() : mLayoutItemCount; 1048c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar lm.layoutRange(recycler, start, start + count); 1049b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals("correct # of children should be laid out", 1050c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar count, lm.getChildCount()); 10518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar lm.assertVisibleItemPositions(); 1052b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1053b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 105442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar private void assertNoPreLayoutPosition(RecyclerView.Recycler recycler) { 105542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar for (RecyclerView.ViewHolder vh : recycler.mAttachedScrap) { 105642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar assertPreLayoutPosition(vh); 105742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar } 105842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar } 105942c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar 106042c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar private void assertNoPreLayoutPosition(RecyclerView.LayoutManager lm) { 106142c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar for (int i = 0; i < lm.getChildCount(); i ++) { 106242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar final RecyclerView.ViewHolder vh = mRecyclerView 106342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar .getChildViewHolder(lm.getChildAt(i)); 106442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar assertPreLayoutPosition(vh); 106542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar } 106642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar } 106742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar 106842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar private void assertPreLayoutPosition(RecyclerView.ViewHolder vh) { 106942c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar assertEquals("in post layout, there should not be a view holder w/ a pre " 107042c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar + "layout position", RecyclerView.NO_POSITION, vh.mPreLayoutPosition); 107142c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar assertEquals("in post layout, there should not be a view holder w/ an old " 107242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar + "layout position", RecyclerView.NO_POSITION, vh.mOldPosition); 107342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar } 107442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar 1075b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void onLayoutChildren(RecyclerView.Recycler recycler, AnimationLayoutManager lm, 1076b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state) { 1077b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 10788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (state.isPreLayout()) { 1079b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (expectedPreLayoutItemCount != -1) { 1080b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals("on pre layout, state should return abstracted adapter size", 1081b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar expectedPreLayoutItemCount, state.getItemCount()); 1082b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1083b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar beforePreLayout(recycler, lm, state); 10848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 1085b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (expectedPostLayoutItemCount != -1) { 1086b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals("on post layout, state should return real adapter size", 1087b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar expectedPostLayoutItemCount, state.getItemCount()); 1088b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1089b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar beforePostLayout(recycler, lm, state); 1090b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 109142c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar if (!state.isPreLayout()) { 109242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar assertNoPreLayoutPosition(recycler); 109342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar } 1094b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar doLayout(recycler, lm, state); 10958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (state.isPreLayout()) { 1096b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar afterPreLayout(recycler, lm, state); 10978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 1098b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar afterPostLayout(recycler, lm, state); 109942c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar assertNoPreLayoutPosition(lm); 1100b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1101b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutCount++; 1102b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1103b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1104b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void afterPreLayout(RecyclerView.Recycler recycler, AnimationLayoutManager layoutManager, 1105b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state) { 1106b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1107b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1108b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void beforePostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager layoutManager, 1109b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state) { 1110b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1111b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1112b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void afterPostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager layoutManager, 1113b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state) { 1114b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1115b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1116b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void postDispatchLayout() { 1117b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1118b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1119d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { 1120d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 1121d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 1122b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1123b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1124b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar class TestRecyclerView extends RecyclerView { 112511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar 1126888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar CountDownLatch drawLatch; 1127b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1128b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public TestRecyclerView(Context context) { 1129b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super(context); 1130b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1131b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1132b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public TestRecyclerView(Context context, AttributeSet attrs) { 1133b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super(context, attrs); 1134b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1135b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1136b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public TestRecyclerView(Context context, AttributeSet attrs, int defStyle) { 1137b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super(context, attrs, defStyle); 1138b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1139b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 11408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 11418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void initAdapterManager() { 11428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.initAdapterManager(); 11438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper.mOnItemProcessedCallback = new Runnable() { 11448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 11458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 11468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar validatePostUpdateOp(); 11478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }; 11498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 1151888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar public void expectDraw(int count) { 1152888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar drawLatch = new CountDownLatch(count); 1153888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 1154888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar 1155888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar public void waitForDraw(long timeout) throws Throwable { 1156888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar drawLatch.await(timeout * (DEBUG ? 100 : 1), TimeUnit.SECONDS); 1157888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar assertEquals("all expected draws should happen at the expected time frame", 1158888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar 0, drawLatch.getCount()); 1159888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 1160888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar 11618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar List<ViewHolder> collectViewHolders() { 11628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar List<ViewHolder> holders = new ArrayList<ViewHolder>(); 11638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int childCount = getChildCount(); 11648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < childCount; i++) { 11658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ViewHolder holder = getChildViewHolderInt(getChildAt(i)); 11668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (holder != null) { 11678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar holders.add(holder); 11688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return holders; 11718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void validateViewHolderPositions() { 11758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final Set<Integer> existingOffsets = new HashSet<Integer>(); 1176668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar int childCount = getChildCount(); 1177668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar StringBuilder log = new StringBuilder(); 11788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < childCount; i++) { 11798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ViewHolder vh = getChildViewHolderInt(getChildAt(i)); 1180668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar TestViewHolder tvh = (TestViewHolder) vh; 1181115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar log.append(tvh.mBoundItem).append(vh) 1182668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar .append(" hidden:") 1183668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar .append(mChildHelper.mHiddenViews.contains(vh.itemView)) 1184668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar .append("\n"); 1185668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 1186668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (int i = 0; i < childCount; i++) { 1187668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar ViewHolder vh = getChildViewHolderInt(getChildAt(i)); 1188668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (vh.isInvalid()) { 1189668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar continue; 1190668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 1191115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar if (vh.getLayoutPosition() < 0) { 1192668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar LayoutManager lm = getLayoutManager(); 1193668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (int j = 0; j < lm.getChildCount(); j ++) { 1194668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar assertNotSame("removed view holder should not be in LM's child list", 1195668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar vh.itemView, lm.getChildAt(j)); 11968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1197668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } else if (!mChildHelper.mHiddenViews.contains(vh.itemView)) { 1198115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar if (!existingOffsets.add(vh.getLayoutPosition())) { 1199668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar throw new IllegalStateException("view holder position conflict for " 1200668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar + "existing views " + vh + "\n" + log); 12018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void validatePostUpdateOp() { 12078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar try { 12088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar validateViewHolderPositions(); 12098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (super.mState.isPreLayout()) { 12108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar validatePreLayoutSequence((AnimationLayoutManager) getLayoutManager()); 12118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar validateAdapterPosition((AnimationLayoutManager) getLayoutManager()); 12138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } catch (Throwable t) { 12148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar postExceptionToInstrumentation(t); 12158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void validateAdapterPosition(AnimationLayoutManager lm) { 12218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (ViewHolder vh : collectViewHolders()) { 12228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (!vh.isRemoved() && vh.mPreLayoutPosition >= 0) { 12238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals("adapter position calculations should match view holder " 1224668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar + "pre layout:" + mState.isPreLayout() 1225668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar + " positions\n" + vh + "\n" + lm.getLog(), 12268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper.findPositionOffset(vh.mPreLayoutPosition), vh.mPosition); 12278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // ensures pre layout positions are continuous block. This is not necessarily a case 12328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // but valid in test RV 12338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void validatePreLayoutSequence(AnimationLayoutManager lm) { 12348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Set<Integer> preLayoutPositions = new HashSet<Integer>(); 12358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (ViewHolder vh : collectViewHolders()) { 12368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertTrue("pre layout positions should be distinct " + lm.getLog(), 12378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar preLayoutPositions.add(vh.mPreLayoutPosition)); 12388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int minPos = Integer.MAX_VALUE; 12408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (Integer pos : preLayoutPositions) { 12418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (pos < minPos) { 12428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar minPos = pos; 12438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 1; i < preLayoutPositions.size(); i++) { 12468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertNotNull("next position should exist " + lm.getLog(), 12478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar preLayoutPositions.contains(minPos + i)); 12488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 1251888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar @Override 1252888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar protected void dispatchDraw(Canvas canvas) { 1253888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar super.dispatchDraw(canvas); 1254888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar if (drawLatch != null) { 1255888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar drawLatch.countDown(); 1256888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 1257888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 1258888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar 1259b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 1260b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void dispatchLayout() { 1261b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar try { 1262b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super.dispatchLayout(); 1263b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (getLayoutManager() instanceof AnimationLayoutManager) { 1264b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar ((AnimationLayoutManager) getLayoutManager()).onPostDispatchLayout(); 1265b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1266b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } catch (Throwable t) { 1267b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar postExceptionToInstrumentation(t); 1268b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1269b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1270b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1271b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 12728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar abstract class AdapterOps { 12768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final public void run(TestAdapter adapter) throws Throwable { 12788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar onRun(adapter); 12798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar abstract void onRun(TestAdapter testAdapter) throws Throwable; 12828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar static class CollectPositionResult { 12858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // true if found in scrap 12878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public RecyclerView.ViewHolder scrapResult; 12888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public RecyclerView.ViewHolder adapterResult; 12908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar static CollectPositionResult fromScrap(RecyclerView.ViewHolder viewHolder) { 12928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar CollectPositionResult cpr = new CollectPositionResult(); 12938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar cpr.scrapResult = viewHolder; 12948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return cpr; 12958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 12968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 12978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar static CollectPositionResult fromAdapter(RecyclerView.ViewHolder viewHolder) { 12988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar CollectPositionResult cpr = new CollectPositionResult(); 12998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar cpr.adapterResult = viewHolder; 13008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return cpr; 13018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 13028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 13038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar static class PositionConstraint { 13058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public static enum Type { 13078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar scrap, 13088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapter, 13098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapterScrap /*first pass adapter, second pass scrap*/ 13108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 13118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Type mType; 13138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int mOldPos; // if VH 13158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int mPreLayoutPos; 13178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int mPostLayoutPos; 13198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int mValidateCount = 0; 13218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public static PositionConstraint scrap(int oldPos, int preLayoutPos, int postLayoutPos) { 13238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint constraint = new PositionConstraint(); 13248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mType = Type.scrap; 13258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mOldPos = oldPos; 13268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mPreLayoutPos = preLayoutPos; 13278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mPostLayoutPos = postLayoutPos; 13288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return constraint; 13298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 13308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public static PositionConstraint adapterScrap(int preLayoutPos, int position) { 13328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint constraint = new PositionConstraint(); 13338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mType = Type.adapterScrap; 13348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mOldPos = RecyclerView.NO_POSITION; 13358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mPreLayoutPos = preLayoutPos; 13368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mPostLayoutPos = position;// adapter pos does not change 13378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return constraint; 13388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 13398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public static PositionConstraint adapter(int position) { 13418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint constraint = new PositionConstraint(); 13428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mType = Type.adapter; 13438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mPreLayoutPos = RecyclerView.NO_POSITION; 13448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mOldPos = RecyclerView.NO_POSITION; 13458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mPostLayoutPos = position;// adapter pos does not change 13468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return constraint; 13478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 13488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void assertValidate() { 13508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int expectedValidate = 0; 13518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mPreLayoutPos >= 0) { 13528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar expectedValidate ++; 13538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 13548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mPostLayoutPos >= 0) { 13558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar expectedValidate ++; 13568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 13578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals("should run all validates", expectedValidate, mValidateCount); 13588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 13598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 13618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String toString() { 13628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return "Cons{" + 13638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar "t=" + mType.name() + 13648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ", old=" + mOldPos + 13658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ", pre=" + mPreLayoutPos + 13668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ", post=" + mPostLayoutPos + 13678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar '}'; 13688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 13698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 13708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void validate(RecyclerView.State state, CollectPositionResult result, String log) { 13718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mValidateCount ++; 13728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertNotNull(this + ": result should not be null\n" + log, result); 13738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.ViewHolder viewHolder; 13748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mType == Type.scrap || (mType == Type.adapterScrap && !state.isPreLayout())) { 13758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertNotNull(this + ": result should come from scrap\n" + log, result.scrapResult); 13768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar viewHolder = result.scrapResult; 13778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 13788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertNotNull(this + ": result should come from adapter\n" + log, 13798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar result.adapterResult); 13808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals(this + ": old position should be none when it came from adapter\n" + log, 13818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.NO_POSITION, result.adapterResult.getOldPosition()); 13828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar viewHolder = result.adapterResult; 13838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 13848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (state.isPreLayout()) { 13858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals(this + ": pre-layout position should match\n" + log, mPreLayoutPos, 1386e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar viewHolder.mPreLayoutPosition == -1 ? viewHolder.mPosition : 13878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar viewHolder.mPreLayoutPosition); 13888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals(this + ": pre-layout getPosition should match\n" + log, mPreLayoutPos, 1389115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar viewHolder.getLayoutPosition()); 13908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mType == Type.scrap) { 13918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals(this + ": old position should match\n" + log, mOldPos, 13928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar result.scrapResult.getOldPosition()); 1393b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 13948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else if (mType == Type.adapter || mType == Type.adapterScrap || !result.scrapResult 13958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar .isRemoved()) { 1396e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar assertEquals(this + ": post-layout position should match\n" + log + "\n\n" 1397115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar + viewHolder, mPostLayoutPos, viewHolder.getLayoutPosition()); 1398b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1399b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1400b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1401d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase} 1402