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