• 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(255);
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          final Window window = getWindow();
710          if (window == null) {
711              return false;
712          }
713          return mIsReturning ? window.getAllowReturnTransitionOverlap()
714                  : window.getAllowEnterTransitionOverlap();
715      }
716  
startRejectedAnimations(final ArrayList<View> rejectedSnapshots)717      private void startRejectedAnimations(final ArrayList<View> rejectedSnapshots) {
718          if (rejectedSnapshots == null || rejectedSnapshots.isEmpty()) {
719              return;
720          }
721          final ViewGroup decorView = getDecor();
722          if (decorView != null) {
723              ViewGroupOverlay overlay = decorView.getOverlay();
724              ObjectAnimator animator = null;
725              int numRejected = rejectedSnapshots.size();
726              for (int i = 0; i < numRejected; i++) {
727                  View snapshot = rejectedSnapshots.get(i);
728                  overlay.add(snapshot);
729                  animator = ObjectAnimator.ofFloat(snapshot, View.ALPHA, 1, 0);
730                  animator.start();
731              }
732              animator.addListener(new AnimatorListenerAdapter() {
733                  @Override
734                  public void onAnimationEnd(Animator animation) {
735                      ViewGroupOverlay overlay = decorView.getOverlay();
736                      int numRejected = rejectedSnapshots.size();
737                      for (int i = 0; i < numRejected; i++) {
738                          overlay.remove(rejectedSnapshots.get(i));
739                      }
740                  }
741              });
742          }
743      }
744  
onRemoteExitTransitionComplete()745      protected void onRemoteExitTransitionComplete() {
746          if (!allowOverlappingTransitions()) {
747              startEnterTransitionOnly();
748          }
749      }
750  
startEnterTransitionOnly()751      private void startEnterTransitionOnly() {
752          startTransition(new Runnable() {
753              @Override
754              public void run() {
755                  boolean startEnterTransition = true;
756                  boolean startSharedElementTransition = false;
757                  ViewGroup decorView = getDecor();
758                  if (decorView != null) {
759                      Transition transition = beginTransition(decorView, startEnterTransition,
760                              startSharedElementTransition);
761                      startEnterTransition(transition);
762                  }
763              }
764          });
765      }
766  }
767