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