• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 android.app;
17 
18 import android.animation.Animator;
19 import android.animation.AnimatorListenerAdapter;
20 import android.animation.ObjectAnimator;
21 import android.app.SharedElementCallback.OnSharedElementsReadyListener;
22 import android.graphics.drawable.Drawable;
23 import android.os.Bundle;
24 import android.os.ResultReceiver;
25 import android.text.TextUtils;
26 import android.transition.Transition;
27 import android.transition.TransitionManager;
28 import android.util.ArrayMap;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.view.ViewGroupOverlay;
32 import android.view.ViewTreeObserver;
33 import android.view.ViewTreeObserver.OnPreDrawListener;
34 import android.view.Window;
35 import android.view.accessibility.AccessibilityEvent;
36 
37 import java.util.ArrayList;
38 
39 /**
40  * This ActivityTransitionCoordinator is created by the Activity to manage
41  * the enter scene and shared element transfer into the Scene, either during
42  * launch of an Activity or returning from a launched Activity.
43  */
44 class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
45     private static final String TAG = "EnterTransitionCoordinator";
46 
47     private static final int MIN_ANIMATION_FRAMES = 2;
48 
49     private boolean mSharedElementTransitionStarted;
50     private Activity mActivity;
51     private boolean mHasStopped;
52     private boolean mIsCanceled;
53     private ObjectAnimator mBackgroundAnimator;
54     private boolean mIsExitTransitionComplete;
55     private boolean mIsReadyForTransition;
56     private Bundle mSharedElementsBundle;
57     private boolean mWasOpaque;
58     private boolean mAreViewsReady;
59     private boolean mIsViewsTransitionStarted;
60     private Transition mEnterViewsTransition;
61     private OnPreDrawListener mViewsReadyListener;
62     private final boolean mIsCrossTask;
63 
EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver, ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask)64     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
65             ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask) {
66         super(activity.getWindow(), sharedElementNames,
67                 getListener(activity, isReturning && !isCrossTask), isReturning);
68         mActivity = activity;
69         mIsCrossTask = isCrossTask;
70         setResultReceiver(resultReceiver);
71         prepareEnter();
72         Bundle resultReceiverBundle = new Bundle();
73         resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this);
74         mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
75         final View decorView = getDecor();
76         if (decorView != null) {
77             decorView.getViewTreeObserver().addOnPreDrawListener(
78                     new ViewTreeObserver.OnPreDrawListener() {
79                         @Override
80                         public boolean onPreDraw() {
81                             if (mIsReadyForTransition) {
82                                 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
83                             }
84                             return mIsReadyForTransition;
85                         }
86                     });
87         }
88     }
89 
isCrossTask()90     boolean isCrossTask() {
91         return mIsCrossTask;
92     }
93 
viewInstancesReady(ArrayList<String> accepted, ArrayList<String> localNames, ArrayList<View> localViews)94     public void viewInstancesReady(ArrayList<String> accepted, ArrayList<String> localNames,
95             ArrayList<View> localViews) {
96         boolean remap = false;
97         for (int i = 0; i < localViews.size(); i++) {
98             View view = localViews.get(i);
99             if (!TextUtils.equals(view.getTransitionName(), localNames.get(i))
100                     || !view.isAttachedToWindow()) {
101                 remap = true;
102                 break;
103             }
104         }
105         if (remap) {
106             triggerViewsReady(mapNamedElements(accepted, localNames));
107         } else {
108             triggerViewsReady(mapSharedElements(accepted, localViews));
109         }
110     }
111 
namedViewsReady(ArrayList<String> accepted, ArrayList<String> localNames)112     public void namedViewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
113         triggerViewsReady(mapNamedElements(accepted, localNames));
114     }
115 
getEnterViewsTransition()116     public Transition getEnterViewsTransition() {
117         return mEnterViewsTransition;
118     }
119 
120     @Override
viewsReady(ArrayMap<String, View> sharedElements)121     protected void viewsReady(ArrayMap<String, View> sharedElements) {
122         super.viewsReady(sharedElements);
123         mIsReadyForTransition = true;
124         hideViews(mSharedElements);
125         if (getViewsTransition() != null && mTransitioningViews != null) {
126             hideViews(mTransitioningViews);
127         }
128         if (mIsReturning) {
129             sendSharedElementDestination();
130         } else {
131             moveSharedElementsToOverlay();
132         }
133         if (mSharedElementsBundle != null) {
134             onTakeSharedElements();
135         }
136     }
137 
triggerViewsReady(final ArrayMap<String, View> sharedElements)138     private void triggerViewsReady(final ArrayMap<String, View> sharedElements) {
139         if (mAreViewsReady) {
140             return;
141         }
142         mAreViewsReady = true;
143         final ViewGroup decor = getDecor();
144         // Ensure the views have been laid out before capturing the views -- we need the epicenter.
145         if (decor == null || (decor.isAttachedToWindow() &&
146                 (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) {
147             viewsReady(sharedElements);
148         } else {
149             mViewsReadyListener = new ViewTreeObserver.OnPreDrawListener() {
150                 @Override
151                 public boolean onPreDraw() {
152                     mViewsReadyListener = null;
153                     decor.getViewTreeObserver().removeOnPreDrawListener(this);
154                     viewsReady(sharedElements);
155                     return true;
156                 }
157             };
158             decor.getViewTreeObserver().addOnPreDrawListener(mViewsReadyListener);
159             decor.invalidate();
160         }
161     }
162 
mapNamedElements(ArrayList<String> accepted, ArrayList<String> localNames)163     private ArrayMap<String, View> mapNamedElements(ArrayList<String> accepted,
164             ArrayList<String> localNames) {
165         ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
166         ViewGroup decorView = getDecor();
167         if (decorView != null) {
168             decorView.findNamedViews(sharedElements);
169         }
170         if (accepted != null) {
171             for (int i = 0; i < localNames.size(); i++) {
172                 String localName = localNames.get(i);
173                 String acceptedName = accepted.get(i);
174                 if (localName != null && !localName.equals(acceptedName)) {
175                     View view = sharedElements.remove(localName);
176                     if (view != null) {
177                         sharedElements.put(acceptedName, view);
178                     }
179                 }
180             }
181         }
182         return sharedElements;
183     }
184 
sendSharedElementDestination()185     private void sendSharedElementDestination() {
186         boolean allReady;
187         final View decorView = getDecor();
188         if (allowOverlappingTransitions() && getEnterViewsTransition() != null) {
189             allReady = false;
190         } else if (decorView == null) {
191             allReady = true;
192         } else {
193             allReady = !decorView.isLayoutRequested();
194             if (allReady) {
195                 for (int i = 0; i < mSharedElements.size(); i++) {
196                     if (mSharedElements.get(i).isLayoutRequested()) {
197                         allReady = false;
198                         break;
199                     }
200                 }
201             }
202         }
203         if (allReady) {
204             Bundle state = captureSharedElementState();
205             moveSharedElementsToOverlay();
206             mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
207         } else if (decorView != null) {
208             decorView.getViewTreeObserver()
209                     .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
210                         @Override
211                         public boolean onPreDraw() {
212                             decorView.getViewTreeObserver().removeOnPreDrawListener(this);
213                             if (mResultReceiver != null) {
214                                 Bundle state = captureSharedElementState();
215                                 moveSharedElementsToOverlay();
216                                 mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
217                             }
218                             return true;
219                         }
220                     });
221         }
222         if (allowOverlappingTransitions()) {
223             startEnterTransitionOnly();
224         }
225     }
226 
getListener(Activity activity, boolean isReturning)227     private static SharedElementCallback getListener(Activity activity, boolean isReturning) {
228         return isReturning ? activity.mExitTransitionListener : activity.mEnterTransitionListener;
229     }
230 
231     @Override
onReceiveResult(int resultCode, Bundle resultData)232     protected void onReceiveResult(int resultCode, Bundle resultData) {
233         switch (resultCode) {
234             case MSG_TAKE_SHARED_ELEMENTS:
235                 if (!mIsCanceled) {
236                     mSharedElementsBundle = resultData;
237                     onTakeSharedElements();
238                 }
239                 break;
240             case MSG_EXIT_TRANSITION_COMPLETE:
241                 if (!mIsCanceled) {
242                     mIsExitTransitionComplete = true;
243                     if (mSharedElementTransitionStarted) {
244                         onRemoteExitTransitionComplete();
245                     }
246                 }
247                 break;
248             case MSG_CANCEL:
249                 cancel();
250                 break;
251         }
252     }
253 
isWaitingForRemoteExit()254     public boolean isWaitingForRemoteExit() {
255         return mIsReturning && mResultReceiver != null;
256     }
257 
258     /**
259      * This is called onResume. If an Activity is resuming and the transitions
260      * haven't started yet, force the views to appear. This is likely to be
261      * caused by the top Activity finishing before the transitions started.
262      * In that case, we can finish any transition that was started, but we
263      * should cancel any pending transition and just bring those Views visible.
264      */
forceViewsToAppear()265     public void forceViewsToAppear() {
266         if (!mIsReturning) {
267             return;
268         }
269         if (!mIsReadyForTransition) {
270             mIsReadyForTransition = true;
271             final ViewGroup decor = getDecor();
272             if (decor != null && mViewsReadyListener != null) {
273                 decor.getViewTreeObserver().removeOnPreDrawListener(mViewsReadyListener);
274                 mViewsReadyListener = null;
275             }
276             showViews(mTransitioningViews, true);
277             setTransitioningViewsVisiblity(View.VISIBLE, true);
278             mSharedElements.clear();
279             mAllSharedElementNames.clear();
280             mTransitioningViews.clear();
281             mIsReadyForTransition = true;
282             viewsTransitionComplete();
283             sharedElementTransitionComplete();
284         } else {
285             if (!mSharedElementTransitionStarted) {
286                 moveSharedElementsFromOverlay();
287                 mSharedElementTransitionStarted = true;
288                 showViews(mSharedElements, true);
289                 mSharedElements.clear();
290                 sharedElementTransitionComplete();
291             }
292             if (!mIsViewsTransitionStarted) {
293                 mIsViewsTransitionStarted = true;
294                 showViews(mTransitioningViews, true);
295                 setTransitioningViewsVisiblity(View.VISIBLE, true);
296                 mTransitioningViews.clear();
297                 viewsTransitionComplete();
298             }
299             cancelPendingTransitions();
300         }
301         mAreViewsReady = true;
302         if (mResultReceiver != null) {
303             mResultReceiver.send(MSG_CANCEL, null);
304             mResultReceiver = null;
305         }
306     }
307 
cancel()308     private void cancel() {
309         if (!mIsCanceled) {
310             mIsCanceled = true;
311             if (getViewsTransition() == null || mIsViewsTransitionStarted) {
312                 showViews(mSharedElements, true);
313             } else if (mTransitioningViews != null) {
314                 mTransitioningViews.addAll(mSharedElements);
315             }
316             moveSharedElementsFromOverlay();
317             mSharedElementNames.clear();
318             mSharedElements.clear();
319             mAllSharedElementNames.clear();
320             startSharedElementTransition(null);
321             onRemoteExitTransitionComplete();
322         }
323     }
324 
isReturning()325     public boolean isReturning() {
326         return mIsReturning;
327     }
328 
prepareEnter()329     protected void prepareEnter() {
330         ViewGroup decorView = getDecor();
331         if (mActivity == null || decorView == null) {
332             return;
333         }
334         if (!isCrossTask()) {
335             mActivity.overridePendingTransition(0, 0);
336         }
337         if (!mIsReturning) {
338             mWasOpaque = mActivity.convertToTranslucent(null, null);
339             Drawable background = decorView.getBackground();
340             if (background != null) {
341                 getWindow().setBackgroundDrawable(null);
342                 background = background.mutate();
343                 background.setAlpha(0);
344                 getWindow().setBackgroundDrawable(background);
345             }
346         } else {
347             mActivity = null; // all done with it now.
348         }
349     }
350 
351     @Override
getViewsTransition()352     protected Transition getViewsTransition() {
353         Window window = getWindow();
354         if (window == null) {
355             return null;
356         }
357         if (mIsReturning) {
358             return window.getReenterTransition();
359         } else {
360             return window.getEnterTransition();
361         }
362     }
363 
getSharedElementTransition()364     protected Transition getSharedElementTransition() {
365         Window window = getWindow();
366         if (window == null) {
367             return null;
368         }
369         if (mIsReturning) {
370             return window.getSharedElementReenterTransition();
371         } else {
372             return window.getSharedElementEnterTransition();
373         }
374     }
375 
startSharedElementTransition(Bundle sharedElementState)376     private void startSharedElementTransition(Bundle sharedElementState) {
377         ViewGroup decorView = getDecor();
378         if (decorView == null) {
379             return;
380         }
381         // Remove rejected shared elements
382         ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
383         rejectedNames.removeAll(mSharedElementNames);
384         ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames);
385         if (mListener != null) {
386             mListener.onRejectSharedElements(rejectedSnapshots);
387         }
388         removeNullViews(rejectedSnapshots);
389         startRejectedAnimations(rejectedSnapshots);
390 
391         // Now start shared element transition
392         ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
393                 mSharedElementNames);
394         showViews(mSharedElements, true);
395         scheduleSetSharedElementEnd(sharedElementSnapshots);
396         ArrayList<SharedElementOriginalState> originalImageViewState =
397                 setSharedElementState(sharedElementState, sharedElementSnapshots);
398         requestLayoutForSharedElements();
399 
400         boolean startEnterTransition = allowOverlappingTransitions() && !mIsReturning;
401         boolean startSharedElementTransition = true;
402         setGhostVisibility(View.INVISIBLE);
403         scheduleGhostVisibilityChange(View.INVISIBLE);
404         pauseInput();
405         Transition transition = beginTransition(decorView, startEnterTransition,
406                 startSharedElementTransition);
407         scheduleGhostVisibilityChange(View.VISIBLE);
408         setGhostVisibility(View.VISIBLE);
409 
410         if (startEnterTransition) {
411             startEnterTransition(transition);
412         }
413 
414         setOriginalSharedElementState(mSharedElements, originalImageViewState);
415 
416         if (mResultReceiver != null) {
417             // We can't trust that the view will disappear on the same frame that the shared
418             // element appears here. Assure that we get at least 2 frames for double-buffering.
419             decorView.postOnAnimation(new Runnable() {
420                 int mAnimations;
421 
422                 @Override
423                 public void run() {
424                     if (mAnimations++ < MIN_ANIMATION_FRAMES) {
425                         View decorView = getDecor();
426                         if (decorView != null) {
427                             decorView.postOnAnimation(this);
428                         }
429                     } else if (mResultReceiver != null) {
430                         mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
431                         mResultReceiver = null; // all done sending messages.
432                     }
433                 }
434             });
435         }
436     }
437 
removeNullViews(ArrayList<View> views)438     private static void removeNullViews(ArrayList<View> views) {
439         if (views != null) {
440             for (int i = views.size() - 1; i >= 0; i--) {
441                 if (views.get(i) == null) {
442                     views.remove(i);
443                 }
444             }
445         }
446     }
447 
onTakeSharedElements()448     private void onTakeSharedElements() {
449         if (!mIsReadyForTransition || mSharedElementsBundle == null) {
450             return;
451         }
452         final Bundle sharedElementState = mSharedElementsBundle;
453         mSharedElementsBundle = null;
454         OnSharedElementsReadyListener listener = new OnSharedElementsReadyListener() {
455             @Override
456             public void onSharedElementsReady() {
457                 final View decorView = getDecor();
458                 if (decorView != null) {
459                     decorView.getViewTreeObserver()
460                             .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
461                                 @Override
462                                 public boolean onPreDraw() {
463                                     decorView.getViewTreeObserver().removeOnPreDrawListener(this);
464                                     startTransition(new Runnable() {
465                                         @Override
466                                         public void run() {
467                                             startSharedElementTransition(sharedElementState);
468                                         }
469                                     });
470                                     return false;
471                                 }
472                             });
473                     decorView.invalidate();
474                 }
475             }
476         };
477         if (mListener == null) {
478             listener.onSharedElementsReady();
479         } else {
480             mListener.onSharedElementsArrived(mSharedElementNames, mSharedElements, listener);
481         }
482     }
483 
requestLayoutForSharedElements()484     private void requestLayoutForSharedElements() {
485         int numSharedElements = mSharedElements.size();
486         for (int i = 0; i < numSharedElements; i++) {
487             mSharedElements.get(i).requestLayout();
488         }
489     }
490 
beginTransition(ViewGroup decorView, boolean startEnterTransition, boolean startSharedElementTransition)491     private Transition beginTransition(ViewGroup decorView, boolean startEnterTransition,
492             boolean startSharedElementTransition) {
493         Transition sharedElementTransition = null;
494         if (startSharedElementTransition) {
495             if (!mSharedElementNames.isEmpty()) {
496                 sharedElementTransition = configureTransition(getSharedElementTransition(), false);
497             }
498             if (sharedElementTransition == null) {
499                 sharedElementTransitionStarted();
500                 sharedElementTransitionComplete();
501             } else {
502                 sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
503                     @Override
504                     public void onTransitionStart(Transition transition) {
505                         sharedElementTransitionStarted();
506                     }
507 
508                     @Override
509                     public void onTransitionEnd(Transition transition) {
510                         transition.removeListener(this);
511                         sharedElementTransitionComplete();
512                     }
513                 });
514             }
515         }
516         Transition viewsTransition = null;
517         if (startEnterTransition) {
518             mIsViewsTransitionStarted = true;
519             if (mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
520                 viewsTransition = configureTransition(getViewsTransition(), true);
521                 if (viewsTransition != null && !mIsReturning) {
522                     stripOffscreenViews();
523                 }
524             }
525             if (viewsTransition == null) {
526                 viewsTransitionComplete();
527             } else {
528                 final ArrayList<View> transitioningViews = mTransitioningViews;
529                 viewsTransition.addListener(new ContinueTransitionListener() {
530                     @Override
531                     public void onTransitionStart(Transition transition) {
532                         mEnterViewsTransition = transition;
533                         if (transitioningViews != null) {
534                             showViews(transitioningViews, false);
535                         }
536                         super.onTransitionStart(transition);
537                     }
538 
539                     @Override
540                     public void onTransitionEnd(Transition transition) {
541                         mEnterViewsTransition = null;
542                         transition.removeListener(this);
543                         viewsTransitionComplete();
544                         super.onTransitionEnd(transition);
545                     }
546                 });
547             }
548         }
549 
550         Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
551         if (transition != null) {
552             transition.addListener(new ContinueTransitionListener());
553             if (startEnterTransition) {
554                 setTransitioningViewsVisiblity(View.INVISIBLE, false);
555             }
556             TransitionManager.beginDelayedTransition(decorView, transition);
557             if (startEnterTransition) {
558                 setTransitioningViewsVisiblity(View.VISIBLE, false);
559             }
560             decorView.invalidate();
561         } else {
562             transitionStarted();
563         }
564         return transition;
565     }
566 
567     @Override
onTransitionsComplete()568     protected void onTransitionsComplete() {
569         moveSharedElementsFromOverlay();
570         final ViewGroup decorView = getDecor();
571         if (decorView != null) {
572             decorView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
573         }
574     }
575 
sharedElementTransitionStarted()576     private void sharedElementTransitionStarted() {
577         mSharedElementTransitionStarted = true;
578         if (mIsExitTransitionComplete) {
579             send(MSG_EXIT_TRANSITION_COMPLETE, null);
580         }
581     }
582 
startEnterTransition(Transition transition)583     private void startEnterTransition(Transition transition) {
584         ViewGroup decorView = getDecor();
585         if (!mIsReturning && decorView != null) {
586             Drawable background = decorView.getBackground();
587             if (background != null) {
588                 background = background.mutate();
589                 getWindow().setBackgroundDrawable(background);
590                 mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 255);
591                 mBackgroundAnimator.setDuration(getFadeDuration());
592                 mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
593                     @Override
594                     public void onAnimationEnd(Animator animation) {
595                         makeOpaque();
596                     }
597                 });
598                 mBackgroundAnimator.start();
599             } else if (transition != null) {
600                 transition.addListener(new Transition.TransitionListenerAdapter() {
601                     @Override
602                     public void onTransitionEnd(Transition transition) {
603                         transition.removeListener(this);
604                         makeOpaque();
605                     }
606                 });
607             } else {
608                 makeOpaque();
609             }
610         }
611     }
612 
stop()613     public void stop() {
614         // Restore the background to its previous state since the
615         // Activity is stopping.
616         if (mBackgroundAnimator != null) {
617             mBackgroundAnimator.end();
618             mBackgroundAnimator = null;
619         } else if (mWasOpaque) {
620             ViewGroup decorView = getDecor();
621             if (decorView != null) {
622                 Drawable drawable = decorView.getBackground();
623                 if (drawable != null) {
624                     drawable.setAlpha(1);
625                 }
626             }
627         }
628         makeOpaque();
629         mIsCanceled = true;
630         mResultReceiver = null;
631         mActivity = null;
632         moveSharedElementsFromOverlay();
633         if (mTransitioningViews != null) {
634             showViews(mTransitioningViews, true);
635             setTransitioningViewsVisiblity(View.VISIBLE, true);
636         }
637         showViews(mSharedElements, true);
638         clearState();
639     }
640 
641     /**
642      * Cancels the enter transition.
643      * @return True if the enter transition is still pending capturing the target state. If so,
644      * any transition started on the decor will do nothing.
645      */
cancelEnter()646     public boolean cancelEnter() {
647         setGhostVisibility(View.INVISIBLE);
648         mHasStopped = true;
649         mIsCanceled = true;
650         clearState();
651         return super.cancelPendingTransitions();
652     }
653 
654     @Override
clearState()655     protected void clearState() {
656         mSharedElementsBundle = null;
657         mEnterViewsTransition = null;
658         mResultReceiver = null;
659         if (mBackgroundAnimator != null) {
660             mBackgroundAnimator.cancel();
661             mBackgroundAnimator = null;
662         }
663         super.clearState();
664     }
665 
makeOpaque()666     private void makeOpaque() {
667         if (!mHasStopped && mActivity != null) {
668             if (mWasOpaque) {
669                 mActivity.convertFromTranslucent();
670             }
671             mActivity = null;
672         }
673     }
674 
allowOverlappingTransitions()675     private boolean allowOverlappingTransitions() {
676         return mIsReturning ? getWindow().getAllowReturnTransitionOverlap()
677                 : getWindow().getAllowEnterTransitionOverlap();
678     }
679 
startRejectedAnimations(final ArrayList<View> rejectedSnapshots)680     private void startRejectedAnimations(final ArrayList<View> rejectedSnapshots) {
681         if (rejectedSnapshots == null || rejectedSnapshots.isEmpty()) {
682             return;
683         }
684         final ViewGroup decorView = getDecor();
685         if (decorView != null) {
686             ViewGroupOverlay overlay = decorView.getOverlay();
687             ObjectAnimator animator = null;
688             int numRejected = rejectedSnapshots.size();
689             for (int i = 0; i < numRejected; i++) {
690                 View snapshot = rejectedSnapshots.get(i);
691                 overlay.add(snapshot);
692                 animator = ObjectAnimator.ofFloat(snapshot, View.ALPHA, 1, 0);
693                 animator.start();
694             }
695             animator.addListener(new AnimatorListenerAdapter() {
696                 @Override
697                 public void onAnimationEnd(Animator animation) {
698                     ViewGroupOverlay overlay = decorView.getOverlay();
699                     int numRejected = rejectedSnapshots.size();
700                     for (int i = 0; i < numRejected; i++) {
701                         overlay.remove(rejectedSnapshots.get(i));
702                     }
703                 }
704             });
705         }
706     }
707 
onRemoteExitTransitionComplete()708     protected void onRemoteExitTransitionComplete() {
709         if (!allowOverlappingTransitions()) {
710             startEnterTransitionOnly();
711         }
712     }
713 
startEnterTransitionOnly()714     private void startEnterTransitionOnly() {
715         startTransition(new Runnable() {
716             @Override
717             public void run() {
718                 boolean startEnterTransition = true;
719                 boolean startSharedElementTransition = false;
720                 ViewGroup decorView = getDecor();
721                 if (decorView != null) {
722                     Transition transition = beginTransition(decorView, startEnterTransition,
723                             startSharedElementTransition);
724                     startEnterTransition(transition);
725                 }
726             }
727         });
728     }
729 }
730