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.Color;
23import android.graphics.drawable.ColorDrawable;
24import android.graphics.drawable.Drawable;
25import android.os.Bundle;
26import android.os.ResultReceiver;
27import android.text.TextUtils;
28import android.transition.Transition;
29import android.transition.TransitionListenerAdapter;
30import android.transition.TransitionManager;
31import android.util.ArrayMap;
32import android.view.View;
33import android.view.ViewGroup;
34import android.view.ViewGroupOverlay;
35import android.view.ViewTreeObserver;
36import android.view.Window;
37import android.view.accessibility.AccessibilityEvent;
38
39import com.android.internal.view.OneShotPreDrawListener;
40
41import 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 */
48class 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 mHasStopped;
56    private boolean mIsCanceled;
57    private ObjectAnimator mBackgroundAnimator;
58    private boolean mIsExitTransitionComplete;
59    private boolean mIsReadyForTransition;
60    private Bundle mSharedElementsBundle;
61    private boolean mWasOpaque;
62    private boolean mAreViewsReady;
63    private boolean mIsViewsTransitionStarted;
64    private Transition mEnterViewsTransition;
65    private OneShotPreDrawListener mViewsReadyListener;
66    private final boolean mIsCrossTask;
67    private Drawable mReplacedBackground;
68
69    public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
70            ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask) {
71        super(activity.getWindow(), sharedElementNames,
72                getListener(activity, isReturning && !isCrossTask), isReturning);
73        mActivity = activity;
74        mIsCrossTask = isCrossTask;
75        setResultReceiver(resultReceiver);
76        prepareEnter();
77        Bundle resultReceiverBundle = new Bundle();
78        resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this);
79        mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
80        final View decorView = getDecor();
81        if (decorView != null) {
82            final ViewTreeObserver viewTreeObserver = decorView.getViewTreeObserver();
83            viewTreeObserver.addOnPreDrawListener(
84                    new ViewTreeObserver.OnPreDrawListener() {
85                        @Override
86                        public boolean onPreDraw() {
87                            if (mIsReadyForTransition) {
88                                if (viewTreeObserver.isAlive()) {
89                                    viewTreeObserver.removeOnPreDrawListener(this);
90                                } else {
91                                    decorView.getViewTreeObserver().removeOnPreDrawListener(this);
92                                }
93                            }
94                            return false;
95                        }
96                    });
97        }
98    }
99
100    boolean isCrossTask() {
101        return mIsCrossTask;
102    }
103
104    public void viewInstancesReady(ArrayList<String> accepted, ArrayList<String> localNames,
105            ArrayList<View> localViews) {
106        boolean remap = false;
107        for (int i = 0; i < localViews.size(); i++) {
108            View view = localViews.get(i);
109            if (!TextUtils.equals(view.getTransitionName(), localNames.get(i))
110                    || !view.isAttachedToWindow()) {
111                remap = true;
112                break;
113            }
114        }
115        if (remap) {
116            triggerViewsReady(mapNamedElements(accepted, localNames));
117        } else {
118            triggerViewsReady(mapSharedElements(accepted, localViews));
119        }
120    }
121
122    public void namedViewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
123        triggerViewsReady(mapNamedElements(accepted, localNames));
124    }
125
126    public Transition getEnterViewsTransition() {
127        return mEnterViewsTransition;
128    }
129
130    @Override
131    protected void viewsReady(ArrayMap<String, View> sharedElements) {
132        super.viewsReady(sharedElements);
133        mIsReadyForTransition = true;
134        hideViews(mSharedElements);
135        Transition viewsTransition = getViewsTransition();
136        if (viewsTransition != null && mTransitioningViews != null) {
137            removeExcludedViews(viewsTransition, mTransitioningViews);
138            stripOffscreenViews();
139            hideViews(mTransitioningViews);
140        }
141        if (mIsReturning) {
142            sendSharedElementDestination();
143        } else {
144            moveSharedElementsToOverlay();
145        }
146        if (mSharedElementsBundle != null) {
147            onTakeSharedElements();
148        }
149    }
150
151    private void triggerViewsReady(final ArrayMap<String, View> sharedElements) {
152        if (mAreViewsReady) {
153            return;
154        }
155        mAreViewsReady = true;
156        final ViewGroup decor = getDecor();
157        // Ensure the views have been laid out before capturing the views -- we need the epicenter.
158        if (decor == null || (decor.isAttachedToWindow() &&
159                (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) {
160            viewsReady(sharedElements);
161        } else {
162            mViewsReadyListener = OneShotPreDrawListener.add(decor, () -> {
163                mViewsReadyListener = null;
164                viewsReady(sharedElements);
165            });
166            decor.invalidate();
167        }
168    }
169
170    private ArrayMap<String, View> mapNamedElements(ArrayList<String> accepted,
171            ArrayList<String> localNames) {
172        ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
173        ViewGroup decorView = getDecor();
174        if (decorView != null) {
175            decorView.findNamedViews(sharedElements);
176        }
177        if (accepted != null) {
178            for (int i = 0; i < localNames.size(); i++) {
179                String localName = localNames.get(i);
180                String acceptedName = accepted.get(i);
181                if (localName != null && !localName.equals(acceptedName)) {
182                    View view = sharedElements.get(localName);
183                    if (view != null) {
184                        sharedElements.put(acceptedName, view);
185                    }
186                }
187            }
188        }
189        return sharedElements;
190    }
191
192    private void sendSharedElementDestination() {
193        boolean allReady;
194        final View decorView = getDecor();
195        if (allowOverlappingTransitions() && getEnterViewsTransition() != null) {
196            allReady = false;
197        } else if (decorView == null) {
198            allReady = true;
199        } else {
200            allReady = !decorView.isLayoutRequested();
201            if (allReady) {
202                for (int i = 0; i < mSharedElements.size(); i++) {
203                    if (mSharedElements.get(i).isLayoutRequested()) {
204                        allReady = false;
205                        break;
206                    }
207                }
208            }
209        }
210        if (allReady) {
211            Bundle state = captureSharedElementState();
212            moveSharedElementsToOverlay();
213            mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
214        } else if (decorView != null) {
215            OneShotPreDrawListener.add(decorView, () -> {
216                if (mResultReceiver != null) {
217                    Bundle state = captureSharedElementState();
218                    moveSharedElementsToOverlay();
219                    mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
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                mViewsReadyListener.removeListener();
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                background = new ColorDrawable(Color.TRANSPARENT);
343                mReplacedBackground = background;
344            } else {
345                getWindow().setBackgroundDrawable(null);
346                background = background.mutate();
347                background.setAlpha(0);
348            }
349            getWindow().setBackgroundDrawable(background);
350        } else {
351            mActivity = null; // all done with it now.
352        }
353    }
354
355    @Override
356    protected Transition getViewsTransition() {
357        Window window = getWindow();
358        if (window == null) {
359            return null;
360        }
361        if (mIsReturning) {
362            return window.getReenterTransition();
363        } else {
364            return window.getEnterTransition();
365        }
366    }
367
368    protected Transition getSharedElementTransition() {
369        Window window = getWindow();
370        if (window == null) {
371            return null;
372        }
373        if (mIsReturning) {
374            return window.getSharedElementReenterTransition();
375        } else {
376            return window.getSharedElementEnterTransition();
377        }
378    }
379
380    private void startSharedElementTransition(Bundle sharedElementState) {
381        ViewGroup decorView = getDecor();
382        if (decorView == null) {
383            return;
384        }
385        // Remove rejected shared elements
386        ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
387        rejectedNames.removeAll(mSharedElementNames);
388        ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames);
389        if (mListener != null) {
390            mListener.onRejectSharedElements(rejectedSnapshots);
391        }
392        removeNullViews(rejectedSnapshots);
393        startRejectedAnimations(rejectedSnapshots);
394
395        // Now start shared element transition
396        ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
397                mSharedElementNames);
398        showViews(mSharedElements, true);
399        scheduleSetSharedElementEnd(sharedElementSnapshots);
400        ArrayList<SharedElementOriginalState> originalImageViewState =
401                setSharedElementState(sharedElementState, sharedElementSnapshots);
402        requestLayoutForSharedElements();
403
404        boolean startEnterTransition = allowOverlappingTransitions() && !mIsReturning;
405        boolean startSharedElementTransition = true;
406        setGhostVisibility(View.INVISIBLE);
407        scheduleGhostVisibilityChange(View.INVISIBLE);
408        pauseInput();
409        Transition transition = beginTransition(decorView, startEnterTransition,
410                startSharedElementTransition);
411        scheduleGhostVisibilityChange(View.VISIBLE);
412        setGhostVisibility(View.VISIBLE);
413
414        if (startEnterTransition) {
415            startEnterTransition(transition);
416        }
417
418        setOriginalSharedElementState(mSharedElements, originalImageViewState);
419
420        if (mResultReceiver != null) {
421            // We can't trust that the view will disappear on the same frame that the shared
422            // element appears here. Assure that we get at least 2 frames for double-buffering.
423            decorView.postOnAnimation(new Runnable() {
424                int mAnimations;
425
426                @Override
427                public void run() {
428                    if (mAnimations++ < MIN_ANIMATION_FRAMES) {
429                        View decorView = getDecor();
430                        if (decorView != null) {
431                            decorView.postOnAnimation(this);
432                        }
433                    } else if (mResultReceiver != null) {
434                        mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
435                        mResultReceiver = null; // all done sending messages.
436                    }
437                }
438            });
439        }
440    }
441
442    private static void removeNullViews(ArrayList<View> views) {
443        if (views != null) {
444            for (int i = views.size() - 1; i >= 0; i--) {
445                if (views.get(i) == null) {
446                    views.remove(i);
447                }
448            }
449        }
450    }
451
452    private void onTakeSharedElements() {
453        if (!mIsReadyForTransition || mSharedElementsBundle == null) {
454            return;
455        }
456        final Bundle sharedElementState = mSharedElementsBundle;
457        mSharedElementsBundle = null;
458        OnSharedElementsReadyListener listener = new OnSharedElementsReadyListener() {
459            @Override
460            public void onSharedElementsReady() {
461                final View decorView = getDecor();
462                if (decorView != null) {
463                    OneShotPreDrawListener.add(decorView, false, () -> {
464                        startTransition(() -> {
465                                startSharedElementTransition(sharedElementState);
466                        });
467                    });
468                    decorView.invalidate();
469                }
470            }
471        };
472        if (mListener == null) {
473            listener.onSharedElementsReady();
474        } else {
475            mListener.onSharedElementsArrived(mSharedElementNames, mSharedElements, listener);
476        }
477    }
478
479    private void requestLayoutForSharedElements() {
480        int numSharedElements = mSharedElements.size();
481        for (int i = 0; i < numSharedElements; i++) {
482            mSharedElements.get(i).requestLayout();
483        }
484    }
485
486    private Transition beginTransition(ViewGroup decorView, boolean startEnterTransition,
487            boolean startSharedElementTransition) {
488        Transition sharedElementTransition = null;
489        if (startSharedElementTransition) {
490            if (!mSharedElementNames.isEmpty()) {
491                sharedElementTransition = configureTransition(getSharedElementTransition(), false);
492            }
493            if (sharedElementTransition == null) {
494                sharedElementTransitionStarted();
495                sharedElementTransitionComplete();
496            } else {
497                sharedElementTransition.addListener(new TransitionListenerAdapter() {
498                    @Override
499                    public void onTransitionStart(Transition transition) {
500                        sharedElementTransitionStarted();
501                    }
502
503                    @Override
504                    public void onTransitionEnd(Transition transition) {
505                        transition.removeListener(this);
506                        sharedElementTransitionComplete();
507                    }
508                });
509            }
510        }
511        Transition viewsTransition = null;
512        if (startEnterTransition) {
513            mIsViewsTransitionStarted = true;
514            if (mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
515                viewsTransition = configureTransition(getViewsTransition(), true);
516            }
517            if (viewsTransition == null) {
518                viewsTransitionComplete();
519            } else {
520                final ArrayList<View> transitioningViews = mTransitioningViews;
521                viewsTransition.addListener(new ContinueTransitionListener() {
522                    @Override
523                    public void onTransitionStart(Transition transition) {
524                        mEnterViewsTransition = transition;
525                        if (transitioningViews != null) {
526                            showViews(transitioningViews, false);
527                        }
528                        super.onTransitionStart(transition);
529                    }
530
531                    @Override
532                    public void onTransitionEnd(Transition transition) {
533                        mEnterViewsTransition = null;
534                        transition.removeListener(this);
535                        viewsTransitionComplete();
536                        super.onTransitionEnd(transition);
537                    }
538                });
539            }
540        }
541
542        Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
543        if (transition != null) {
544            transition.addListener(new ContinueTransitionListener());
545            if (startEnterTransition) {
546                setTransitioningViewsVisiblity(View.INVISIBLE, false);
547            }
548            TransitionManager.beginDelayedTransition(decorView, transition);
549            if (startEnterTransition) {
550                setTransitioningViewsVisiblity(View.VISIBLE, false);
551            }
552            decorView.invalidate();
553        } else {
554            transitionStarted();
555        }
556        return transition;
557    }
558
559    @Override
560    protected void onTransitionsComplete() {
561        moveSharedElementsFromOverlay();
562        final ViewGroup decorView = getDecor();
563        if (decorView != null) {
564            decorView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
565
566            Window window = getWindow();
567            if (window != null && mReplacedBackground == decorView.getBackground()) {
568                window.setBackgroundDrawable(null);
569            }
570        }
571    }
572
573    private void sharedElementTransitionStarted() {
574        mSharedElementTransitionStarted = true;
575        if (mIsExitTransitionComplete) {
576            send(MSG_EXIT_TRANSITION_COMPLETE, null);
577        }
578    }
579
580    private void startEnterTransition(Transition transition) {
581        ViewGroup decorView = getDecor();
582        if (!mIsReturning && decorView != null) {
583            Drawable background = decorView.getBackground();
584            if (background != null) {
585                background = background.mutate();
586                getWindow().setBackgroundDrawable(background);
587                mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 255);
588                mBackgroundAnimator.setDuration(getFadeDuration());
589                mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
590                    @Override
591                    public void onAnimationEnd(Animator animation) {
592                        makeOpaque();
593                        backgroundAnimatorComplete();
594                    }
595                });
596                mBackgroundAnimator.start();
597            } else if (transition != null) {
598                transition.addListener(new TransitionListenerAdapter() {
599                    @Override
600                    public void onTransitionEnd(Transition transition) {
601                        transition.removeListener(this);
602                        makeOpaque();
603                    }
604                });
605                backgroundAnimatorComplete();
606            } else {
607                makeOpaque();
608                backgroundAnimatorComplete();
609            }
610        } else {
611            backgroundAnimatorComplete();
612        }
613    }
614
615    public void stop() {
616        // Restore the background to its previous state since the
617        // Activity is stopping.
618        if (mBackgroundAnimator != null) {
619            mBackgroundAnimator.end();
620            mBackgroundAnimator = null;
621        } else if (mWasOpaque) {
622            ViewGroup decorView = getDecor();
623            if (decorView != null) {
624                Drawable drawable = decorView.getBackground();
625                if (drawable != null) {
626                    drawable.setAlpha(1);
627                }
628            }
629        }
630        makeOpaque();
631        mIsCanceled = true;
632        mResultReceiver = null;
633        mActivity = null;
634        moveSharedElementsFromOverlay();
635        if (mTransitioningViews != null) {
636            showViews(mTransitioningViews, true);
637            setTransitioningViewsVisiblity(View.VISIBLE, true);
638        }
639        showViews(mSharedElements, true);
640        clearState();
641    }
642
643    /**
644     * Cancels the enter transition.
645     * @return True if the enter transition is still pending capturing the target state. If so,
646     * any transition started on the decor will do nothing.
647     */
648    public boolean cancelEnter() {
649        setGhostVisibility(View.INVISIBLE);
650        mHasStopped = true;
651        mIsCanceled = true;
652        clearState();
653        return super.cancelPendingTransitions();
654    }
655
656    @Override
657    protected void clearState() {
658        mSharedElementsBundle = null;
659        mEnterViewsTransition = null;
660        mResultReceiver = null;
661        if (mBackgroundAnimator != null) {
662            mBackgroundAnimator.cancel();
663            mBackgroundAnimator = null;
664        }
665        super.clearState();
666    }
667
668    private void makeOpaque() {
669        if (!mHasStopped && mActivity != null) {
670            if (mWasOpaque) {
671                mActivity.convertFromTranslucent();
672            }
673            mActivity = null;
674        }
675    }
676
677    private boolean allowOverlappingTransitions() {
678        return mIsReturning ? getWindow().getAllowReturnTransitionOverlap()
679                : getWindow().getAllowEnterTransitionOverlap();
680    }
681
682    private void startRejectedAnimations(final ArrayList<View> rejectedSnapshots) {
683        if (rejectedSnapshots == null || rejectedSnapshots.isEmpty()) {
684            return;
685        }
686        final ViewGroup decorView = getDecor();
687        if (decorView != null) {
688            ViewGroupOverlay overlay = decorView.getOverlay();
689            ObjectAnimator animator = null;
690            int numRejected = rejectedSnapshots.size();
691            for (int i = 0; i < numRejected; i++) {
692                View snapshot = rejectedSnapshots.get(i);
693                overlay.add(snapshot);
694                animator = ObjectAnimator.ofFloat(snapshot, View.ALPHA, 1, 0);
695                animator.start();
696            }
697            animator.addListener(new AnimatorListenerAdapter() {
698                @Override
699                public void onAnimationEnd(Animator animation) {
700                    ViewGroupOverlay overlay = decorView.getOverlay();
701                    int numRejected = rejectedSnapshots.size();
702                    for (int i = 0; i < numRejected; i++) {
703                        overlay.remove(rejectedSnapshots.get(i));
704                    }
705                }
706            });
707        }
708    }
709
710    protected void onRemoteExitTransitionComplete() {
711        if (!allowOverlappingTransitions()) {
712            startEnterTransitionOnly();
713        }
714    }
715
716    private void startEnterTransitionOnly() {
717        startTransition(new Runnable() {
718            @Override
719            public void run() {
720                boolean startEnterTransition = true;
721                boolean startSharedElementTransition = false;
722                ViewGroup decorView = getDecor();
723                if (decorView != null) {
724                    Transition transition = beginTransition(decorView, startEnterTransition,
725                            startSharedElementTransition);
726                    startEnterTransition(transition);
727                }
728            }
729        });
730    }
731}
732