• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 package com.android.car.dialer;
17 
18 import android.support.v4.view.ViewCompat;
19 import android.support.v4.view.ViewPropertyAnimatorCompat;
20 import android.support.v4.view.ViewPropertyAnimatorListener;
21 import android.support.v7.widget.RecyclerView.ViewHolder;
22 import android.support.v7.widget.SimpleItemAnimator;
23 import android.view.View;
24 
25 import java.util.ArrayList;
26 import java.util.List;
27 
28 /**
29  * Branch from {@link android.support.v7.widget.DefaultItemAnimator} with changes on
30  * {@link #animateAddImpl}, {@link #animateAdd} and {@link #animateRemoveImpl}.
31  */
32 public class StrequentsItemAnimator extends SimpleItemAnimator {
33     private static final boolean DEBUG = false;
34 
35     private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
36     private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
37     private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
38     private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
39 
40     private ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();
41     private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
42     private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
43 
44     private ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();
45     private ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();
46     private ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();
47     private ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();
48 
49     private static class MoveInfo {
50         public ViewHolder holder;
51         public int fromX, fromY, toX, toY;
52 
MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY)53         private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {
54             this.holder = holder;
55             this.fromX = fromX;
56             this.fromY = fromY;
57             this.toX = toX;
58             this.toY = toY;
59         }
60     }
61 
62     private static class ChangeInfo {
63         public ViewHolder oldHolder, newHolder;
64         public int fromX, fromY, toX, toY;
ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder)65         private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {
66             this.oldHolder = oldHolder;
67             this.newHolder = newHolder;
68         }
69 
ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder, int fromX, int fromY, int toX, int toY)70         private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,
71                 int fromX, int fromY, int toX, int toY) {
72             this(oldHolder, newHolder);
73             this.fromX = fromX;
74             this.fromY = fromY;
75             this.toX = toX;
76             this.toY = toY;
77         }
78 
79         @Override
toString()80         public String toString() {
81             return "ChangeInfo{" +
82                     "oldHolder=" + oldHolder +
83                     ", newHolder=" + newHolder +
84                     ", fromX=" + fromX +
85                     ", fromY=" + fromY +
86                     ", toX=" + toX +
87                     ", toY=" + toY +
88                     '}';
89         }
90     }
91 
92     @Override
runPendingAnimations()93     public void runPendingAnimations() {
94         boolean removalsPending = !mPendingRemovals.isEmpty();
95         boolean movesPending = !mPendingMoves.isEmpty();
96         boolean changesPending = !mPendingChanges.isEmpty();
97         boolean additionsPending = !mPendingAdditions.isEmpty();
98         if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
99             // nothing to animate
100             return;
101         }
102         // First, remove stuff
103         for (ViewHolder holder : mPendingRemovals) {
104             animateRemoveImpl(holder);
105         }
106         mPendingRemovals.clear();
107         // Next, move stuff
108         if (movesPending) {
109             final ArrayList<MoveInfo> moves = new ArrayList<MoveInfo>();
110             moves.addAll(mPendingMoves);
111             mMovesList.add(moves);
112             mPendingMoves.clear();
113             Runnable mover = new Runnable() {
114                 @Override
115                 public void run() {
116                     for (MoveInfo moveInfo : moves) {
117                         animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
118                                 moveInfo.toX, moveInfo.toY);
119                     }
120                     moves.clear();
121                     mMovesList.remove(moves);
122                 }
123             };
124             if (removalsPending) {
125                 View view = moves.get(0).holder.itemView;
126                 ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
127             } else {
128                 mover.run();
129             }
130         }
131         // Next, change stuff, to run in parallel with move animations
132         if (changesPending) {
133             final ArrayList<ChangeInfo> changes = new ArrayList<ChangeInfo>();
134             changes.addAll(mPendingChanges);
135             mChangesList.add(changes);
136             mPendingChanges.clear();
137             Runnable changer = new Runnable() {
138                 @Override
139                 public void run() {
140                     for (ChangeInfo change : changes) {
141                         animateChangeImpl(change);
142                     }
143                     changes.clear();
144                     mChangesList.remove(changes);
145                 }
146             };
147             if (removalsPending) {
148                 ViewHolder holder = changes.get(0).oldHolder;
149                 ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
150             } else {
151                 changer.run();
152             }
153         }
154         // Next, add stuff
155         if (additionsPending) {
156             final ArrayList<ViewHolder> additions = new ArrayList<ViewHolder>();
157             additions.addAll(mPendingAdditions);
158             mAdditionsList.add(additions);
159             mPendingAdditions.clear();
160             Runnable adder = new Runnable() {
161                 public void run() {
162                     for (ViewHolder holder : additions) {
163                         animateAddImpl(holder);
164                     }
165                     additions.clear();
166                     mAdditionsList.remove(additions);
167                 }
168             };
169             if (removalsPending || movesPending || changesPending) {
170                 long removeDuration = removalsPending ? getRemoveDuration() : 0;
171                 long moveDuration = movesPending ? getMoveDuration() : 0;
172                 long changeDuration = changesPending ? getChangeDuration() : 0;
173                 long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
174                 View view = additions.get(0).itemView;
175                 ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
176             } else {
177                 adder.run();
178             }
179         }
180     }
181 
182     @Override
animateRemove(final ViewHolder holder)183     public boolean animateRemove(final ViewHolder holder) {
184         endAnimation(holder);
185         mPendingRemovals.add(holder);
186         return true;
187     }
188 
animateRemoveImpl(final ViewHolder holder)189     private void animateRemoveImpl(final ViewHolder holder) {
190         // Animate on the content if it's CallLogViewHolder.
191         final View view = holder instanceof CallLogViewHolder ?
192                 ((CallLogViewHolder) holder).container : holder.itemView;
193         final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
194         mRemoveAnimations.add(holder);
195         animation.setDuration(getRemoveDuration())
196                 .alpha(0).setListener(new VpaListenerAdapter() {
197             @Override
198             public void onAnimationStart(View view) {
199                 dispatchRemoveStarting(holder);
200             }
201 
202             @Override
203             public void onAnimationEnd(View view) {
204                 animation.setListener(null);
205                 ViewCompat.setAlpha(view, 1);
206                 dispatchRemoveFinished(holder);
207                 mRemoveAnimations.remove(holder);
208                 dispatchFinishedWhenDone();
209             }
210         }).start();
211     }
212 
213     @Override
animateAdd(final ViewHolder holder)214     public boolean animateAdd(final ViewHolder holder) {
215         endAnimation(holder);
216         // for CallLogViewHolder, instead of fade out the whole card, fade out only the content.
217         if (holder instanceof CallLogViewHolder) {
218             ViewCompat.setAlpha(((CallLogViewHolder) holder).container, 0);
219         } else {
220             ViewCompat.setAlpha(holder.itemView, 0);
221         }
222         mPendingAdditions.add(holder);
223         return true;
224     }
225 
animateAddImpl(final ViewHolder holder)226     private void animateAddImpl(final ViewHolder holder) {
227         // Animate on the content if it's CallLogViewHolder.
228         final View view = holder instanceof CallLogViewHolder ?
229                 ((CallLogViewHolder) holder).container : holder.itemView;
230         final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
231         mAddAnimations.add(holder);
232         animation.alpha(1).setDuration(getAddDuration()).
233                 setListener(new VpaListenerAdapter() {
234                     @Override
235                     public void onAnimationStart(View view) {
236                         dispatchAddStarting(holder);
237                     }
238                     @Override
239                     public void onAnimationCancel(View view) {
240                         ViewCompat.setAlpha(view, 1);
241                     }
242 
243                     @Override
244                     public void onAnimationEnd(View view) {
245                         animation.setListener(null);
246                         dispatchAddFinished(holder);
247                         mAddAnimations.remove(holder);
248                         dispatchFinishedWhenDone();
249                     }
250                 }).start();
251     }
252 
253     @Override
animateMove(final ViewHolder holder, int fromX, int fromY, int toX, int toY)254     public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
255             int toX, int toY) {
256         final View view = holder.itemView;
257         fromX += ViewCompat.getTranslationX(holder.itemView);
258         fromY += ViewCompat.getTranslationY(holder.itemView);
259         endAnimation(holder);
260         int deltaX = toX - fromX;
261         int deltaY = toY - fromY;
262         if (deltaX == 0 && deltaY == 0) {
263             dispatchMoveFinished(holder);
264             return false;
265         }
266         if (deltaX != 0) {
267             ViewCompat.setTranslationX(view, -deltaX);
268         }
269         if (deltaY != 0) {
270             ViewCompat.setTranslationY(view, -deltaY);
271         }
272         mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
273         return true;
274     }
275 
animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY)276     private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
277         final View view = holder.itemView;
278         final int deltaX = toX - fromX;
279         final int deltaY = toY - fromY;
280         if (deltaX != 0) {
281             ViewCompat.animate(view).translationX(0);
282         }
283         if (deltaY != 0) {
284             ViewCompat.animate(view).translationY(0);
285         }
286         // TODO: make EndActions end listeners instead, since end actions aren't called when
287         // vpas are canceled (and can't end them. why?)
288         // need listener functionality in VPACompat for this. Ick.
289         final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
290         mMoveAnimations.add(holder);
291         animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
292             @Override
293             public void onAnimationStart(View view) {
294                 dispatchMoveStarting(holder);
295             }
296             @Override
297             public void onAnimationCancel(View view) {
298                 if (deltaX != 0) {
299                     ViewCompat.setTranslationX(view, 0);
300                 }
301                 if (deltaY != 0) {
302                     ViewCompat.setTranslationY(view, 0);
303                 }
304             }
305             @Override
306             public void onAnimationEnd(View view) {
307                 animation.setListener(null);
308                 dispatchMoveFinished(holder);
309                 mMoveAnimations.remove(holder);
310                 dispatchFinishedWhenDone();
311             }
312         }).start();
313     }
314 
315     @Override
animateChange(ViewHolder oldHolder, ViewHolder newHolder, int fromX, int fromY, int toX, int toY)316     public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
317             int fromX, int fromY, int toX, int toY) {
318         final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
319         final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
320         final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
321         endAnimation(oldHolder);
322         int deltaX = (int) (toX - fromX - prevTranslationX);
323         int deltaY = (int) (toY - fromY - prevTranslationY);
324         // recover prev translation state after ending animation
325         ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
326         ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
327         ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
328         if (newHolder != null && newHolder.itemView != null) {
329             // carry over translation values
330             endAnimation(newHolder);
331             ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
332             ViewCompat.setTranslationY(newHolder.itemView, -deltaY);
333             ViewCompat.setAlpha(newHolder.itemView, 0);
334         }
335         mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
336         return true;
337     }
338 
animateChangeImpl(final ChangeInfo changeInfo)339     private void animateChangeImpl(final ChangeInfo changeInfo) {
340         final ViewHolder holder = changeInfo.oldHolder;
341         final View view = holder == null ? null : holder.itemView;
342         final ViewHolder newHolder = changeInfo.newHolder;
343         final View newView = newHolder != null ? newHolder.itemView : null;
344         if (view != null) {
345             final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(
346                     getChangeDuration());
347             mChangeAnimations.add(changeInfo.oldHolder);
348             oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
349             oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
350             oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {
351                 @Override
352                 public void onAnimationStart(View view) {
353                     dispatchChangeStarting(changeInfo.oldHolder, true);
354                 }
355 
356                 @Override
357                 public void onAnimationEnd(View view) {
358                     oldViewAnim.setListener(null);
359                     ViewCompat.setAlpha(view, 1);
360                     ViewCompat.setTranslationX(view, 0);
361                     ViewCompat.setTranslationY(view, 0);
362                     dispatchChangeFinished(changeInfo.oldHolder, true);
363                     mChangeAnimations.remove(changeInfo.oldHolder);
364                     dispatchFinishedWhenDone();
365                 }
366             }).start();
367         }
368         if (newView != null) {
369             final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);
370             mChangeAnimations.add(changeInfo.newHolder);
371             newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).
372                     alpha(1).setListener(new VpaListenerAdapter() {
373                 @Override
374                 public void onAnimationStart(View view) {
375                     dispatchChangeStarting(changeInfo.newHolder, false);
376                 }
377                 @Override
378                 public void onAnimationEnd(View view) {
379                     newViewAnimation.setListener(null);
380                     ViewCompat.setAlpha(newView, 1);
381                     ViewCompat.setTranslationX(newView, 0);
382                     ViewCompat.setTranslationY(newView, 0);
383                     dispatchChangeFinished(changeInfo.newHolder, false);
384                     mChangeAnimations.remove(changeInfo.newHolder);
385                     dispatchFinishedWhenDone();
386                 }
387             }).start();
388         }
389     }
390 
endChangeAnimation(List<ChangeInfo> infoList, ViewHolder item)391     private void endChangeAnimation(List<ChangeInfo> infoList, ViewHolder item) {
392         for (int i = infoList.size() - 1; i >= 0; i--) {
393             ChangeInfo changeInfo = infoList.get(i);
394             if (endChangeAnimationIfNecessary(changeInfo, item)) {
395                 if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
396                     infoList.remove(changeInfo);
397                 }
398             }
399         }
400     }
401 
endChangeAnimationIfNecessary(ChangeInfo changeInfo)402     private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
403         if (changeInfo.oldHolder != null) {
404             endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
405         }
406         if (changeInfo.newHolder != null) {
407             endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
408         }
409     }
endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item)410     private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) {
411         boolean oldItem = false;
412         if (changeInfo.newHolder == item) {
413             changeInfo.newHolder = null;
414         } else if (changeInfo.oldHolder == item) {
415             changeInfo.oldHolder = null;
416             oldItem = true;
417         } else {
418             return false;
419         }
420         ViewCompat.setAlpha(item.itemView, 1);
421         ViewCompat.setTranslationX(item.itemView, 0);
422         ViewCompat.setTranslationY(item.itemView, 0);
423         dispatchChangeFinished(item, oldItem);
424         return true;
425     }
426 
427     @Override
endAnimation(ViewHolder item)428     public void endAnimation(ViewHolder item) {
429         final View view = item.itemView;
430         // this will trigger end callback which should set properties to their target values.
431         ViewCompat.animate(view).cancel();
432         // TODO if some other animations are chained to end, how do we cancel them as well?
433         for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
434             MoveInfo moveInfo = mPendingMoves.get(i);
435             if (moveInfo.holder == item) {
436                 ViewCompat.setTranslationY(view, 0);
437                 ViewCompat.setTranslationX(view, 0);
438                 dispatchMoveFinished(item);
439                 mPendingMoves.remove(i);
440             }
441         }
442         endChangeAnimation(mPendingChanges, item);
443         if (mPendingRemovals.remove(item)) {
444             ViewCompat.setAlpha(view, 1);
445             dispatchRemoveFinished(item);
446         }
447         if (mPendingAdditions.remove(item)) {
448             ViewCompat.setAlpha(view, 1);
449             dispatchAddFinished(item);
450         }
451 
452         for (int i = mChangesList.size() - 1; i >= 0; i--) {
453             ArrayList<ChangeInfo> changes = mChangesList.get(i);
454             endChangeAnimation(changes, item);
455             if (changes.isEmpty()) {
456                 mChangesList.remove(i);
457             }
458         }
459         for (int i = mMovesList.size() - 1; i >= 0; i--) {
460             ArrayList<MoveInfo> moves = mMovesList.get(i);
461             for (int j = moves.size() - 1; j >= 0; j--) {
462                 MoveInfo moveInfo = moves.get(j);
463                 if (moveInfo.holder == item) {
464                     ViewCompat.setTranslationY(view, 0);
465                     ViewCompat.setTranslationX(view, 0);
466                     dispatchMoveFinished(item);
467                     moves.remove(j);
468                     if (moves.isEmpty()) {
469                         mMovesList.remove(i);
470                     }
471                     break;
472                 }
473             }
474         }
475         for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
476             ArrayList<ViewHolder> additions = mAdditionsList.get(i);
477             if (additions.remove(item)) {
478                 ViewCompat.setAlpha(view, 1);
479                 dispatchAddFinished(item);
480                 if (additions.isEmpty()) {
481                     mAdditionsList.remove(i);
482                 }
483             }
484         }
485 
486         // animations should be ended by the cancel above.
487         if (mRemoveAnimations.remove(item) && DEBUG) {
488             throw new IllegalStateException("after animation is cancelled, item should not be in "
489                     + "mRemoveAnimations list");
490         }
491 
492         if (mAddAnimations.remove(item) && DEBUG) {
493             throw new IllegalStateException("after animation is cancelled, item should not be in "
494                     + "mAddAnimations list");
495         }
496 
497         if (mChangeAnimations.remove(item) && DEBUG) {
498             throw new IllegalStateException("after animation is cancelled, item should not be in "
499                     + "mChangeAnimations list");
500         }
501 
502         if (mMoveAnimations.remove(item) && DEBUG) {
503             throw new IllegalStateException("after animation is cancelled, item should not be in "
504                     + "mMoveAnimations list");
505         }
506         dispatchFinishedWhenDone();
507     }
508 
509     @Override
isRunning()510     public boolean isRunning() {
511         return (!mPendingAdditions.isEmpty() ||
512                 !mPendingChanges.isEmpty() ||
513                 !mPendingMoves.isEmpty() ||
514                 !mPendingRemovals.isEmpty() ||
515                 !mMoveAnimations.isEmpty() ||
516                 !mRemoveAnimations.isEmpty() ||
517                 !mAddAnimations.isEmpty() ||
518                 !mChangeAnimations.isEmpty() ||
519                 !mMovesList.isEmpty() ||
520                 !mAdditionsList.isEmpty() ||
521                 !mChangesList.isEmpty());
522     }
523 
524     /**
525      * Check the state of currently pending and running animations. If there are none
526      * pending/running, call {@link #dispatchAnimationsFinished()} to notify any
527      * listeners.
528      */
dispatchFinishedWhenDone()529     private void dispatchFinishedWhenDone() {
530         if (!isRunning()) {
531             dispatchAnimationsFinished();
532         }
533     }
534 
535     @Override
endAnimations()536     public void endAnimations() {
537         int count = mPendingMoves.size();
538         for (int i = count - 1; i >= 0; i--) {
539             MoveInfo item = mPendingMoves.get(i);
540             View view = item.holder.itemView;
541             ViewCompat.setTranslationY(view, 0);
542             ViewCompat.setTranslationX(view, 0);
543             dispatchMoveFinished(item.holder);
544             mPendingMoves.remove(i);
545         }
546         count = mPendingRemovals.size();
547         for (int i = count - 1; i >= 0; i--) {
548             ViewHolder item = mPendingRemovals.get(i);
549             dispatchRemoveFinished(item);
550             mPendingRemovals.remove(i);
551         }
552         count = mPendingAdditions.size();
553         for (int i = count - 1; i >= 0; i--) {
554             ViewHolder item = mPendingAdditions.get(i);
555             View view = item.itemView;
556             ViewCompat.setAlpha(view, 1);
557             dispatchAddFinished(item);
558             mPendingAdditions.remove(i);
559         }
560         count = mPendingChanges.size();
561         for (int i = count - 1; i >= 0; i--) {
562             endChangeAnimationIfNecessary(mPendingChanges.get(i));
563         }
564         mPendingChanges.clear();
565         if (!isRunning()) {
566             return;
567         }
568 
569         int listCount = mMovesList.size();
570         for (int i = listCount - 1; i >= 0; i--) {
571             ArrayList<MoveInfo> moves = mMovesList.get(i);
572             count = moves.size();
573             for (int j = count - 1; j >= 0; j--) {
574                 MoveInfo moveInfo = moves.get(j);
575                 ViewHolder item = moveInfo.holder;
576                 View view = item.itemView;
577                 ViewCompat.setTranslationY(view, 0);
578                 ViewCompat.setTranslationX(view, 0);
579                 dispatchMoveFinished(moveInfo.holder);
580                 moves.remove(j);
581                 if (moves.isEmpty()) {
582                     mMovesList.remove(moves);
583                 }
584             }
585         }
586         listCount = mAdditionsList.size();
587         for (int i = listCount - 1; i >= 0; i--) {
588             ArrayList<ViewHolder> additions = mAdditionsList.get(i);
589             count = additions.size();
590             for (int j = count - 1; j >= 0; j--) {
591                 ViewHolder item = additions.get(j);
592                 View view = item.itemView;
593                 ViewCompat.setAlpha(view, 1);
594                 dispatchAddFinished(item);
595                 additions.remove(j);
596                 if (additions.isEmpty()) {
597                     mAdditionsList.remove(additions);
598                 }
599             }
600         }
601         listCount = mChangesList.size();
602         for (int i = listCount - 1; i >= 0; i--) {
603             ArrayList<ChangeInfo> changes = mChangesList.get(i);
604             count = changes.size();
605             for (int j = count - 1; j >= 0; j--) {
606                 endChangeAnimationIfNecessary(changes.get(j));
607                 if (changes.isEmpty()) {
608                     mChangesList.remove(changes);
609                 }
610             }
611         }
612 
613         cancelAll(mRemoveAnimations);
614         cancelAll(mMoveAnimations);
615         cancelAll(mAddAnimations);
616         cancelAll(mChangeAnimations);
617 
618         dispatchAnimationsFinished();
619     }
620 
cancelAll(List<ViewHolder> viewHolders)621     void cancelAll(List<ViewHolder> viewHolders) {
622         for (int i = viewHolders.size() - 1; i >= 0; i--) {
623             ViewCompat.animate(viewHolders.get(i).itemView).cancel();
624         }
625     }
626 
627     private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {
628         @Override
onAnimationStart(View view)629         public void onAnimationStart(View view) {}
630 
631         @Override
onAnimationEnd(View view)632         public void onAnimationEnd(View view) {}
633 
634         @Override
onAnimationCancel(View view)635         public void onAnimationCancel(View view) {}
636     };
637 }
638