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