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