1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.recyclerview.widget; 18 19 import static java.lang.annotation.ElementType.FIELD; 20 import static java.lang.annotation.ElementType.LOCAL_VARIABLE; 21 import static java.lang.annotation.ElementType.METHOD; 22 import static java.lang.annotation.ElementType.PARAMETER; 23 import static java.lang.annotation.RetentionPolicy.CLASS; 24 25 import java.lang.annotation.Retention; 26 import java.lang.annotation.Target; 27 import java.util.ArrayList; 28 import java.util.List; 29 import java.util.concurrent.CountDownLatch; 30 import java.util.concurrent.TimeUnit; 31 32 /** 33 * This is a dummy ItemAnimator class that does not depends on Duration, Tests would use this class 34 * to control whenever they want the Animator to finish. 35 * 1. Test MUST call endAnimation(ViewHolder) on UI thread to finish animation of a given ViewHolder 36 * Or Test calls endAnimations() on UI thread to end animations for all. 37 * 2. Test can call getAddAnimations() etc. to get ViewHolders that currently running animation. 38 * 3. Test can call {@link #expect(int, int)} and {@link #waitFor(int)} to wait given 39 * Events are fired. 40 */ 41 public class DummyItemAnimator extends SimpleItemAnimator { 42 43 static final long TIMEOUT_SECOND = 10; 44 45 ArrayList<RecyclerView.ViewHolder> mAdds = new ArrayList(); 46 ArrayList<RecyclerView.ViewHolder> mRemoves = new ArrayList(); 47 ArrayList<RecyclerView.ViewHolder> mMoves = new ArrayList(); 48 ArrayList<RecyclerView.ViewHolder> mChangesOld = new ArrayList(); 49 ArrayList<RecyclerView.ViewHolder> mChangesNew = new ArrayList(); 50 51 @Retention(CLASS) 52 @Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD}) 53 public @interface CountDownLatchIndex { 54 } 55 56 @CountDownLatchIndex 57 public static final int ADD_START = 0; 58 59 @CountDownLatchIndex 60 public static final int ADD_FINISHED = 1; 61 62 @CountDownLatchIndex 63 public static final int REMOVE_START = 2; 64 65 @CountDownLatchIndex 66 public static final int REMOVE_FINISHED = 3; 67 68 @CountDownLatchIndex 69 public static final int MOVE_START = 4; 70 71 @CountDownLatchIndex 72 public static final int MOVE_FINISHED = 5; 73 74 @CountDownLatchIndex 75 public static final int CHANGE_OLD_START = 6; 76 77 @CountDownLatchIndex 78 public static final int CHANGE_OLD_FINISHED = 7; 79 80 @CountDownLatchIndex 81 public static final int CHANGE_NEW_START = 8; 82 83 @CountDownLatchIndex 84 public static final int CHANGE_NEW_FINISHED = 9; 85 86 static final int NUM_COUNT_DOWN_LATCH = 10; 87 88 CountDownLatch[] mCountDownLatches = new CountDownLatch[NUM_COUNT_DOWN_LATCH]; 89 90 91 public List<RecyclerView.ViewHolder> getAddAnimations() { 92 return mAdds; 93 } 94 95 public List<RecyclerView.ViewHolder> getRemoveAnimations() { 96 return mRemoves; 97 } 98 99 public List<RecyclerView.ViewHolder> getMovesAnimations() { 100 return mMoves; 101 } 102 103 public List<RecyclerView.ViewHolder> getChangesOldAnimations() { 104 return mChangesOld; 105 } 106 107 public List<RecyclerView.ViewHolder> getChangesNewAnimations() { 108 return mChangesNew; 109 } 110 111 @Override 112 public boolean animateRemove(RecyclerView.ViewHolder holder) { 113 mRemoves.add(holder); 114 dispatchRemoveStarting(holder); 115 return false; 116 } 117 118 @Override 119 public boolean animateAdd(RecyclerView.ViewHolder holder) { 120 mAdds.add(holder); 121 dispatchAddStarting(holder); 122 return false; 123 } 124 125 @Override 126 public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, 127 int toY) { 128 mMoves.add(holder); 129 dispatchMoveStarting(holder); 130 return false; 131 } 132 133 @Override 134 public boolean animateChange(RecyclerView.ViewHolder oldHolder, 135 RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop) { 136 mChangesOld.add(oldHolder); 137 mChangesNew.add(newHolder); 138 dispatchChangeStarting(oldHolder, true); 139 dispatchChangeStarting(newHolder, false); 140 return false; 141 } 142 143 public void expect(@CountDownLatchIndex int index, int count) { 144 mCountDownLatches[index] = new CountDownLatch(count); 145 } 146 147 public void waitFor(@CountDownLatchIndex int index) 148 throws InterruptedException { 149 mCountDownLatches[index].await(TIMEOUT_SECOND, TimeUnit.SECONDS); 150 } 151 152 @Override 153 public void onChangeStarting(RecyclerView.ViewHolder item, boolean oldItem) { 154 CountDownLatch latch = mCountDownLatches[oldItem ? CHANGE_OLD_START : CHANGE_NEW_START]; 155 if (latch != null) { 156 latch.countDown(); 157 } 158 } 159 160 @Override 161 public void onMoveStarting(RecyclerView.ViewHolder item) { 162 CountDownLatch latch = mCountDownLatches[MOVE_START]; 163 if (latch != null) { 164 latch.countDown(); 165 } 166 } 167 168 @Override 169 public void onAddStarting(RecyclerView.ViewHolder item) { 170 CountDownLatch latch = mCountDownLatches[ADD_START]; 171 if (latch != null) { 172 latch.countDown(); 173 } 174 } 175 176 @Override 177 public void onRemoveStarting(RecyclerView.ViewHolder item) { 178 CountDownLatch latch = mCountDownLatches[REMOVE_START]; 179 if (latch != null) { 180 latch.countDown(); 181 } 182 } 183 184 @Override 185 public void onChangeFinished(RecyclerView.ViewHolder item, boolean oldItem) { 186 CountDownLatch latch = mCountDownLatches[oldItem 187 ? CHANGE_OLD_FINISHED : CHANGE_NEW_FINISHED]; 188 if (latch != null) { 189 latch.countDown(); 190 } 191 } 192 193 @Override 194 public void onMoveFinished(RecyclerView.ViewHolder item) { 195 CountDownLatch latch = mCountDownLatches[MOVE_FINISHED]; 196 if (latch != null) { 197 latch.countDown(); 198 } 199 } 200 201 @Override 202 public void onAddFinished(RecyclerView.ViewHolder item) { 203 CountDownLatch latch = mCountDownLatches[ADD_FINISHED]; 204 if (latch != null) { 205 latch.countDown(); 206 } 207 } 208 209 @Override 210 public void onRemoveFinished(RecyclerView.ViewHolder item) { 211 CountDownLatch latch = mCountDownLatches[REMOVE_FINISHED]; 212 if (latch != null) { 213 latch.countDown(); 214 } 215 } 216 217 @Override 218 public void runPendingAnimations() { 219 } 220 221 @Override 222 public void endAnimation(RecyclerView.ViewHolder item) { 223 if (mAdds.remove(item)) { 224 dispatchAddFinished(item); 225 } else if (mRemoves.remove(item)) { 226 dispatchRemoveFinished(item); 227 } else if (mMoves.remove(item)) { 228 dispatchMoveFinished(item); 229 } else if (mChangesOld.remove(item)) { 230 dispatchChangeFinished(item, true); 231 } else if (mChangesNew.remove(item)) { 232 dispatchChangeFinished(item, false); 233 } 234 } 235 236 @Override 237 public void endAnimations() { 238 for (int i = mAdds.size() - 1; i >= 0; i--) { 239 endAnimation(mAdds.get(i)); 240 } 241 for (int i = mRemoves.size() - 1; i >= 0; i--) { 242 endAnimation(mRemoves.get(i)); 243 } 244 for (int i = mMoves.size() - 1; i >= 0; i--) { 245 endAnimation(mMoves.get(i)); 246 } 247 for (int i = mChangesOld.size() - 1; i >= 0; i--) { 248 endAnimation(mChangesOld.get(i)); 249 } 250 for (int i = mChangesNew.size() - 1; i >= 0; i--) { 251 endAnimation(mChangesNew.get(i)); 252 } 253 } 254 255 @Override 256 public boolean isRunning() { 257 return mAdds.size() != 0 258 || mRemoves.size() != 0 259 || mMoves.size() != 0 260 || mChangesOld.size() != 0 261 || mChangesNew.size() != 0; 262 } 263 } 264