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