blob: 4db4be0c53ac73e37543dbfa081adbfdc6189b8d [file] [log] [blame]
George Mount31a21722014-03-24 17:44:36 -07001/*
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;
George Mount4dc668c2015-04-09 20:55:59 +000021import android.app.SharedElementCallback.OnSharedElementsReadyListener;
George Mount31a21722014-03-24 17:44:36 -070022import android.graphics.drawable.Drawable;
23import android.os.Bundle;
24import android.os.ResultReceiver;
Dake Gub04b36e2014-08-08 11:22:30 -070025import android.text.TextUtils;
George Mount31a21722014-03-24 17:44:36 -070026import android.transition.Transition;
George Mount62ab9b72014-05-02 13:51:17 -070027import android.transition.TransitionManager;
28import android.util.ArrayMap;
George Mount31a21722014-03-24 17:44:36 -070029import android.view.View;
George Mountd265bd72014-06-02 07:22:59 -070030import android.view.ViewGroup;
George Mount62ab9b72014-05-02 13:51:17 -070031import android.view.ViewGroupOverlay;
George Mount31a21722014-03-24 17:44:36 -070032import android.view.ViewTreeObserver;
George Mountf1abef62014-09-22 13:58:21 -070033import android.view.Window;
George Mount31a21722014-03-24 17:44:36 -070034
35import java.util.ArrayList;
36
37/**
38 * This ActivityTransitionCoordinator is created by the Activity to manage
George Mount62ab9b72014-05-02 13:51:17 -070039 * the enter scene and shared element transfer into the Scene, either during
40 * launch of an Activity or returning from a launched Activity.
George Mount31a21722014-03-24 17:44:36 -070041 */
George Mount62ab9b72014-05-02 13:51:17 -070042class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
George Mount31a21722014-03-24 17:44:36 -070043 private static final String TAG = "EnterTransitionCoordinator";
44
George Mounte678ab62014-06-30 15:28:28 -070045 private static final int MIN_ANIMATION_FRAMES = 2;
George Mount31a21722014-03-24 17:44:36 -070046
George Mount62ab9b72014-05-02 13:51:17 -070047 private boolean mSharedElementTransitionStarted;
George Mount31a21722014-03-24 17:44:36 -070048 private Activity mActivity;
George Mount62ab9b72014-05-02 13:51:17 -070049 private boolean mHasStopped;
George Mount62ab9b72014-05-02 13:51:17 -070050 private boolean mIsCanceled;
George Mount8cab50a2014-05-15 09:57:17 -070051 private ObjectAnimator mBackgroundAnimator;
George Mountc93ca162014-05-23 19:21:36 -070052 private boolean mIsExitTransitionComplete;
George Mount8c2614c2014-06-10 11:12:01 -070053 private boolean mIsReadyForTransition;
54 private Bundle mSharedElementsBundle;
George Mount3cc716c2014-06-12 16:35:35 -070055 private boolean mWasOpaque;
George Mount82ffc142014-06-30 16:47:34 -070056 private boolean mAreViewsReady;
George Mount8d3cd2c2014-07-08 11:07:33 -070057 private boolean mIsViewsTransitionStarted;
George Mounta2bbbb32014-08-12 10:16:20 -070058 private Transition mEnterViewsTransition;
George Mount31a21722014-03-24 17:44:36 -070059
George Mount62ab9b72014-05-02 13:51:17 -070060 public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
George Mount8c2614c2014-06-10 11:12:01 -070061 ArrayList<String> sharedElementNames, boolean isReturning) {
62 super(activity.getWindow(), sharedElementNames,
63 getListener(activity, isReturning), isReturning);
George Mount31a21722014-03-24 17:44:36 -070064 mActivity = activity;
George Mount62ab9b72014-05-02 13:51:17 -070065 setResultReceiver(resultReceiver);
66 prepareEnter();
67 Bundle resultReceiverBundle = new Bundle();
68 resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this);
69 mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
George Mount6e7fb602014-09-04 16:20:20 -070070 final View decorView = getDecor();
George Mount48bd13c2014-09-12 10:54:54 -070071 if (decorView != null) {
72 decorView.getViewTreeObserver().addOnPreDrawListener(
73 new ViewTreeObserver.OnPreDrawListener() {
74 @Override
75 public boolean onPreDraw() {
76 if (mIsReadyForTransition) {
77 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
78 }
79 return mIsReadyForTransition;
George Mounted1e01d2014-06-05 13:49:12 -070080 }
George Mount48bd13c2014-09-12 10:54:54 -070081 });
82 }
George Mount8c2614c2014-06-10 11:12:01 -070083 }
84
Dake Gub04b36e2014-08-08 11:22:30 -070085 public void viewInstancesReady(ArrayList<String> accepted, ArrayList<String> localNames,
86 ArrayList<View> localViews) {
87 boolean remap = false;
88 for (int i = 0; i < localViews.size(); i++) {
89 View view = localViews.get(i);
90 if (!TextUtils.equals(view.getTransitionName(), localNames.get(i))
91 || !view.isAttachedToWindow()) {
92 remap = true;
93 break;
94 }
95 }
96 if (remap) {
97 triggerViewsReady(mapNamedElements(accepted, localNames));
98 } else {
99 triggerViewsReady(mapSharedElements(accepted, localViews));
100 }
George Mount1fecfb22014-06-18 14:55:55 -0700101 }
George Mount8c2614c2014-06-10 11:12:01 -0700102
George Mount1fecfb22014-06-18 14:55:55 -0700103 public void namedViewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
George Mount82ffc142014-06-30 16:47:34 -0700104 triggerViewsReady(mapNamedElements(accepted, localNames));
George Mount1fecfb22014-06-18 14:55:55 -0700105 }
106
George Mounta2bbbb32014-08-12 10:16:20 -0700107 public Transition getEnterViewsTransition() {
108 return mEnterViewsTransition;
109 }
110
George Mount1fecfb22014-06-18 14:55:55 -0700111 @Override
112 protected void viewsReady(ArrayMap<String, View> sharedElements) {
113 super.viewsReady(sharedElements);
George Mount8c2614c2014-06-10 11:12:01 -0700114 mIsReadyForTransition = true;
George Mount0f0c4732014-09-05 13:47:47 -0700115 hideViews(mSharedElements);
George Mountb694e082014-09-12 07:34:52 -0700116 if (getViewsTransition() != null && mTransitioningViews != null) {
George Mount0f0c4732014-09-05 13:47:47 -0700117 hideViews(mTransitioningViews);
George Mount8c2614c2014-06-10 11:12:01 -0700118 }
George Mountfe361d22014-07-08 17:25:25 -0700119 if (mIsReturning) {
120 sendSharedElementDestination();
121 } else {
George Mountfe361d22014-07-08 17:25:25 -0700122 moveSharedElementsToOverlay();
123 }
George Mount8c2614c2014-06-10 11:12:01 -0700124 if (mSharedElementsBundle != null) {
125 onTakeSharedElements();
126 }
George Mountd265bd72014-06-02 07:22:59 -0700127 }
128
George Mount82ffc142014-06-30 16:47:34 -0700129 private void triggerViewsReady(final ArrayMap<String, View> sharedElements) {
130 if (mAreViewsReady) {
131 return;
132 }
133 mAreViewsReady = true;
George Mount1fb941d2014-10-30 09:15:32 -0700134 final ViewGroup decor = getDecor();
George Mount82ffc142014-06-30 16:47:34 -0700135 // Ensure the views have been laid out before capturing the views -- we need the epicenter.
George Mount1fb941d2014-10-30 09:15:32 -0700136 if (decor == null || (decor.isAttachedToWindow() &&
137 (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) {
George Mount82ffc142014-06-30 16:47:34 -0700138 viewsReady(sharedElements);
139 } else {
George Mount1fb941d2014-10-30 09:15:32 -0700140 decor.getViewTreeObserver()
George Mount82ffc142014-06-30 16:47:34 -0700141 .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
George Mount4dc668c2015-04-09 20:55:59 +0000142 @Override
143 public boolean onPreDraw() {
144 decor.getViewTreeObserver().removeOnPreDrawListener(this);
145 viewsReady(sharedElements);
146 return true;
147 }
148 });
George Mount82ffc142014-06-30 16:47:34 -0700149 }
150 }
151
George Mount1fecfb22014-06-18 14:55:55 -0700152 private ArrayMap<String, View> mapNamedElements(ArrayList<String> accepted,
153 ArrayList<String> localNames) {
154 ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
George Mount48bd13c2014-09-12 10:54:54 -0700155 ViewGroup decorView = getDecor();
156 if (decorView != null) {
157 decorView.findNamedViews(sharedElements);
158 }
George Mount1fecfb22014-06-18 14:55:55 -0700159 if (accepted != null) {
160 for (int i = 0; i < localNames.size(); i++) {
161 String localName = localNames.get(i);
162 String acceptedName = accepted.get(i);
163 if (localName != null && !localName.equals(acceptedName)) {
164 View view = sharedElements.remove(localName);
165 if (view != null) {
166 sharedElements.put(acceptedName, view);
167 }
168 }
169 }
170 }
171 return sharedElements;
172 }
173
George Mountd265bd72014-06-02 07:22:59 -0700174 private void sendSharedElementDestination() {
George Mountfe361d22014-07-08 17:25:25 -0700175 boolean allReady;
George Mount48bd13c2014-09-12 10:54:54 -0700176 final View decorView = getDecor();
Dake Gu872efe42014-08-29 15:24:58 -0700177 if (allowOverlappingTransitions() && getEnterViewsTransition() != null) {
George Mountfe361d22014-07-08 17:25:25 -0700178 allReady = false;
George Mount48bd13c2014-09-12 10:54:54 -0700179 } else if (decorView == null) {
180 allReady = true;
George Mountfe361d22014-07-08 17:25:25 -0700181 } else {
George Mount48bd13c2014-09-12 10:54:54 -0700182 allReady = !decorView.isLayoutRequested();
George Mountfe361d22014-07-08 17:25:25 -0700183 if (allReady) {
184 for (int i = 0; i < mSharedElements.size(); i++) {
185 if (mSharedElements.get(i).isLayoutRequested()) {
186 allReady = false;
187 break;
188 }
George Mount67d92432014-06-06 13:34:20 -0700189 }
190 }
191 }
192 if (allReady) {
George Mountc93ca162014-05-23 19:21:36 -0700193 Bundle state = captureSharedElementState();
George Mountfe361d22014-07-08 17:25:25 -0700194 moveSharedElementsToOverlay();
George Mountc93ca162014-05-23 19:21:36 -0700195 mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
George Mount48bd13c2014-09-12 10:54:54 -0700196 } else if (decorView != null) {
George Mount6e7fb602014-09-04 16:20:20 -0700197 decorView.getViewTreeObserver()
George Mountd265bd72014-06-02 07:22:59 -0700198 .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
199 @Override
200 public boolean onPreDraw() {
George Mount6e7fb602014-09-04 16:20:20 -0700201 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
George Mountf31d83c2014-08-29 13:32:20 -0700202 if (mResultReceiver != null) {
203 Bundle state = captureSharedElementState();
George Mountf31d83c2014-08-29 13:32:20 -0700204 moveSharedElementsToOverlay();
205 mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
206 }
George Mountd265bd72014-06-02 07:22:59 -0700207 return true;
208 }
209 });
George Mount31a21722014-03-24 17:44:36 -0700210 }
George Mount13ccb792014-06-06 17:02:20 -0700211 if (allowOverlappingTransitions()) {
212 startEnterTransitionOnly();
213 }
George Mount31a21722014-03-24 17:44:36 -0700214 }
215
George Mount65580562014-08-29 08:15:48 -0700216 private static SharedElementCallback getListener(Activity activity, boolean isReturning) {
George Mount800d72b2014-05-19 07:09:00 -0700217 return isReturning ? activity.mExitTransitionListener : activity.mEnterTransitionListener;
218 }
219
George Mount31a21722014-03-24 17:44:36 -0700220 @Override
George Mount62ab9b72014-05-02 13:51:17 -0700221 protected void onReceiveResult(int resultCode, Bundle resultData) {
222 switch (resultCode) {
223 case MSG_TAKE_SHARED_ELEMENTS:
224 if (!mIsCanceled) {
George Mount8c2614c2014-06-10 11:12:01 -0700225 mSharedElementsBundle = resultData;
226 onTakeSharedElements();
George Mount62ab9b72014-05-02 13:51:17 -0700227 }
228 break;
229 case MSG_EXIT_TRANSITION_COMPLETE:
230 if (!mIsCanceled) {
George Mountc93ca162014-05-23 19:21:36 -0700231 mIsExitTransitionComplete = true;
232 if (mSharedElementTransitionStarted) {
George Mount62ab9b72014-05-02 13:51:17 -0700233 onRemoteExitTransitionComplete();
234 }
235 }
236 break;
237 case MSG_CANCEL:
238 cancel();
239 break;
240 }
George Mount31a21722014-03-24 17:44:36 -0700241 }
242
George Mount62ab9b72014-05-02 13:51:17 -0700243 private void cancel() {
244 if (!mIsCanceled) {
245 mIsCanceled = true;
George Mount8d3cd2c2014-07-08 11:07:33 -0700246 if (getViewsTransition() == null || mIsViewsTransitionStarted) {
George Mountce2ee3d2014-09-09 15:04:31 -0700247 showViews(mSharedElements, true);
George Mountb694e082014-09-12 07:34:52 -0700248 } else if (mTransitioningViews != null) {
George Mount62ab9b72014-05-02 13:51:17 -0700249 mTransitioningViews.addAll(mSharedElements);
250 }
251 mSharedElementNames.clear();
252 mSharedElements.clear();
253 mAllSharedElementNames.clear();
George Mount8c2614c2014-06-10 11:12:01 -0700254 startSharedElementTransition(null);
George Mount62ab9b72014-05-02 13:51:17 -0700255 onRemoteExitTransitionComplete();
256 }
George Mount31a21722014-03-24 17:44:36 -0700257 }
258
George Mount62ab9b72014-05-02 13:51:17 -0700259 public boolean isReturning() {
260 return mIsReturning;
George Mount31a21722014-03-24 17:44:36 -0700261 }
262
George Mount62ab9b72014-05-02 13:51:17 -0700263 protected void prepareEnter() {
George Mountf1abef62014-09-22 13:58:21 -0700264 ViewGroup decorView = getDecor();
265 if (mActivity == null || decorView == null) {
266 return;
267 }
George Mount62ab9b72014-05-02 13:51:17 -0700268 mActivity.overridePendingTransition(0, 0);
269 if (!mIsReturning) {
George Mount3cc716c2014-06-12 16:35:35 -0700270 mWasOpaque = mActivity.convertToTranslucent(null, null);
George Mountf1abef62014-09-22 13:58:21 -0700271 Drawable background = decorView.getBackground();
George Mount62ab9b72014-05-02 13:51:17 -0700272 if (background != null) {
273 getWindow().setBackgroundDrawable(null);
274 background = background.mutate();
275 background.setAlpha(0);
276 getWindow().setBackgroundDrawable(background);
277 }
278 } else {
279 mActivity = null; // all done with it now.
280 }
281 }
282
George Mounta712e8c2014-05-20 15:10:20 -0700283 @Override
284 protected Transition getViewsTransition() {
George Mountf1abef62014-09-22 13:58:21 -0700285 Window window = getWindow();
286 if (window == null) {
287 return null;
288 }
George Mounta712e8c2014-05-20 15:10:20 -0700289 if (mIsReturning) {
George Mountf1abef62014-09-22 13:58:21 -0700290 return window.getReenterTransition();
George Mounta712e8c2014-05-20 15:10:20 -0700291 } else {
George Mountf1abef62014-09-22 13:58:21 -0700292 return window.getEnterTransition();
George Mounta712e8c2014-05-20 15:10:20 -0700293 }
294 }
295
296 protected Transition getSharedElementTransition() {
George Mountf1abef62014-09-22 13:58:21 -0700297 Window window = getWindow();
298 if (window == null) {
299 return null;
300 }
George Mounta712e8c2014-05-20 15:10:20 -0700301 if (mIsReturning) {
George Mountf1abef62014-09-22 13:58:21 -0700302 return window.getSharedElementReenterTransition();
George Mounta712e8c2014-05-20 15:10:20 -0700303 } else {
George Mountf1abef62014-09-22 13:58:21 -0700304 return window.getSharedElementEnterTransition();
George Mounta712e8c2014-05-20 15:10:20 -0700305 }
306 }
307
George Mount8c2614c2014-06-10 11:12:01 -0700308 private void startSharedElementTransition(Bundle sharedElementState) {
George Mount48bd13c2014-09-12 10:54:54 -0700309 ViewGroup decorView = getDecor();
310 if (decorView == null) {
311 return;
312 }
George Mount62ab9b72014-05-02 13:51:17 -0700313 // Remove rejected shared elements
314 ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
315 rejectedNames.removeAll(mSharedElementNames);
316 ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames);
George Mount1732f522014-09-17 16:59:36 -0700317 if (mListener != null) {
318 mListener.onRejectSharedElements(rejectedSnapshots);
319 }
George Mount83c692e2014-10-23 11:11:17 -0700320 removeNullViews(rejectedSnapshots);
George Mount62ab9b72014-05-02 13:51:17 -0700321 startRejectedAnimations(rejectedSnapshots);
322
323 // Now start shared element transition
324 ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
325 mSharedElementNames);
George Mountce2ee3d2014-09-09 15:04:31 -0700326 showViews(mSharedElements, true);
George Mountfe361d22014-07-08 17:25:25 -0700327 scheduleSetSharedElementEnd(sharedElementSnapshots);
Dake Guc18f4cc2014-07-11 17:48:37 -0700328 ArrayList<SharedElementOriginalState> originalImageViewState =
George Mount62ab9b72014-05-02 13:51:17 -0700329 setSharedElementState(sharedElementState, sharedElementSnapshots);
George Mountc93ca162014-05-23 19:21:36 -0700330 requestLayoutForSharedElements();
George Mount62ab9b72014-05-02 13:51:17 -0700331
George Mount13ccb792014-06-06 17:02:20 -0700332 boolean startEnterTransition = allowOverlappingTransitions() && !mIsReturning;
George Mount62ab9b72014-05-02 13:51:17 -0700333 boolean startSharedElementTransition = true;
George Mountfe361d22014-07-08 17:25:25 -0700334 setGhostVisibility(View.INVISIBLE);
335 scheduleGhostVisibilityChange(View.INVISIBLE);
George Mount48bd13c2014-09-12 10:54:54 -0700336 Transition transition = beginTransition(decorView, startEnterTransition,
337 startSharedElementTransition);
George Mountfe361d22014-07-08 17:25:25 -0700338 scheduleGhostVisibilityChange(View.VISIBLE);
339 setGhostVisibility(View.VISIBLE);
George Mount62ab9b72014-05-02 13:51:17 -0700340
341 if (startEnterTransition) {
342 startEnterTransition(transition);
343 }
344
Dake Guc18f4cc2014-07-11 17:48:37 -0700345 setOriginalSharedElementState(mSharedElements, originalImageViewState);
George Mount62ab9b72014-05-02 13:51:17 -0700346
347 if (mResultReceiver != null) {
George Mounte678ab62014-06-30 15:28:28 -0700348 // We can't trust that the view will disappear on the same frame that the shared
349 // element appears here. Assure that we get at least 2 frames for double-buffering.
George Mount48bd13c2014-09-12 10:54:54 -0700350 decorView.postOnAnimation(new Runnable() {
George Mounte678ab62014-06-30 15:28:28 -0700351 int mAnimations;
George Mount48bd13c2014-09-12 10:54:54 -0700352
George Mounte678ab62014-06-30 15:28:28 -0700353 @Override
354 public void run() {
355 if (mAnimations++ < MIN_ANIMATION_FRAMES) {
George Mount4188d0d2014-09-11 17:47:11 -0700356 View decorView = getDecor();
357 if (decorView != null) {
358 decorView.postOnAnimation(this);
359 }
George Mount407d1fa2014-08-28 16:11:49 -0700360 } else if (mResultReceiver != null) {
George Mounte678ab62014-06-30 15:28:28 -0700361 mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
362 mResultReceiver = null; // all done sending messages.
363 }
364 }
365 });
George Mount62ab9b72014-05-02 13:51:17 -0700366 }
George Mount62ab9b72014-05-02 13:51:17 -0700367 }
368
George Mount83c692e2014-10-23 11:11:17 -0700369 private static void removeNullViews(ArrayList<View> views) {
370 if (views != null) {
371 for (int i = views.size() - 1; i >= 0; i--) {
372 if (views.get(i) == null) {
373 views.remove(i);
374 }
375 }
376 }
377 }
378
George Mount13ccb792014-06-06 17:02:20 -0700379 private void onTakeSharedElements() {
380 if (!mIsReadyForTransition || mSharedElementsBundle == null) {
381 return;
382 }
383 final Bundle sharedElementState = mSharedElementsBundle;
384 mSharedElementsBundle = null;
George Mount4dc668c2015-04-09 20:55:59 +0000385 OnSharedElementsReadyListener listener = new OnSharedElementsReadyListener() {
386 @Override
387 public void onSharedElementsReady() {
388 final View decorView = getDecor();
389 if (decorView != null) {
390 decorView.getViewTreeObserver()
391 .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
George Mount48bd13c2014-09-12 10:54:54 -0700392 @Override
George Mount4dc668c2015-04-09 20:55:59 +0000393 public boolean onPreDraw() {
394 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
395 startTransition(new Runnable() {
396 @Override
397 public void run() {
398 startSharedElementTransition(sharedElementState);
399 }
400 });
401 return false;
George Mount48bd13c2014-09-12 10:54:54 -0700402 }
403 });
George Mount4dc668c2015-04-09 20:55:59 +0000404 decorView.invalidate();
405 }
406 }
407 };
408 if (mListener == null) {
409 listener.onSharedElementsReady();
410 } else {
411 mListener.onSharedElementsArrived(mSharedElementNames, mSharedElements, listener);
George Mount48bd13c2014-09-12 10:54:54 -0700412 }
George Mount13ccb792014-06-06 17:02:20 -0700413 }
414
George Mountc93ca162014-05-23 19:21:36 -0700415 private void requestLayoutForSharedElements() {
416 int numSharedElements = mSharedElements.size();
417 for (int i = 0; i < numSharedElements; i++) {
418 mSharedElements.get(i).requestLayout();
419 }
420 }
421
George Mount48bd13c2014-09-12 10:54:54 -0700422 private Transition beginTransition(ViewGroup decorView, boolean startEnterTransition,
George Mount62ab9b72014-05-02 13:51:17 -0700423 boolean startSharedElementTransition) {
424 Transition sharedElementTransition = null;
George Mountc93ca162014-05-23 19:21:36 -0700425 if (startSharedElementTransition) {
George Mountfe361d22014-07-08 17:25:25 -0700426 if (!mSharedElementNames.isEmpty()) {
427 sharedElementTransition = configureTransition(getSharedElementTransition(), false);
428 }
429 if (sharedElementTransition == null) {
George Mountc93ca162014-05-23 19:21:36 -0700430 sharedElementTransitionStarted();
George Mountfe361d22014-07-08 17:25:25 -0700431 sharedElementTransitionComplete();
George Mountc93ca162014-05-23 19:21:36 -0700432 } else {
George Mountfe361d22014-07-08 17:25:25 -0700433 sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
George Mountc93ca162014-05-23 19:21:36 -0700434 @Override
435 public void onTransitionStart(Transition transition) {
George Mountc93ca162014-05-23 19:21:36 -0700436 sharedElementTransitionStarted();
437 }
George Mountfe361d22014-07-08 17:25:25 -0700438
439 @Override
440 public void onTransitionEnd(Transition transition) {
441 transition.removeListener(this);
442 sharedElementTransitionComplete();
443 }
George Mountc93ca162014-05-23 19:21:36 -0700444 });
445 }
446 }
George Mountfe361d22014-07-08 17:25:25 -0700447 Transition viewsTransition = null;
448 if (startEnterTransition) {
449 mIsViewsTransitionStarted = true;
George Mountb694e082014-09-12 07:34:52 -0700450 if (mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
George Mountfe361d22014-07-08 17:25:25 -0700451 viewsTransition = configureTransition(getViewsTransition(), true);
452 if (viewsTransition != null && !mIsReturning) {
453 stripOffscreenViews();
454 }
George Mount13ccb792014-06-06 17:02:20 -0700455 }
George Mountfe361d22014-07-08 17:25:25 -0700456 if (viewsTransition == null) {
George Mount41725de2015-04-09 08:23:05 -0700457 viewsTransitionComplete();
George Mountfe361d22014-07-08 17:25:25 -0700458 } else {
459 viewsTransition.forceVisibility(View.INVISIBLE, true);
George Mountb694e082014-09-12 07:34:52 -0700460 final ArrayList<View> transitioningViews = mTransitioningViews;
George Mountfe361d22014-07-08 17:25:25 -0700461 viewsTransition.addListener(new ContinueTransitionListener() {
462 @Override
George Mounta2bbbb32014-08-12 10:16:20 -0700463 public void onTransitionStart(Transition transition) {
464 mEnterViewsTransition = transition;
George Mountb694e082014-09-12 07:34:52 -0700465 if (transitioningViews != null) {
466 showViews(transitioningViews, false);
467 }
George Mounta2bbbb32014-08-12 10:16:20 -0700468 super.onTransitionStart(transition);
469 }
470
471 @Override
George Mountfe361d22014-07-08 17:25:25 -0700472 public void onTransitionEnd(Transition transition) {
George Mounta2bbbb32014-08-12 10:16:20 -0700473 mEnterViewsTransition = null;
George Mountfe361d22014-07-08 17:25:25 -0700474 transition.removeListener(this);
George Mount41725de2015-04-09 08:23:05 -0700475 viewsTransitionComplete();
George Mountfe361d22014-07-08 17:25:25 -0700476 super.onTransitionEnd(transition);
477 }
478 });
479 }
480 }
481
482 Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
483 if (transition != null) {
484 transition.addListener(new ContinueTransitionListener());
George Mount48bd13c2014-09-12 10:54:54 -0700485 TransitionManager.beginDelayedTransition(decorView, transition);
George Mount62ab9b72014-05-02 13:51:17 -0700486 if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
487 mSharedElements.get(0).invalidate();
George Mountb694e082014-09-12 07:34:52 -0700488 } else if (startEnterTransition && mTransitioningViews != null &&
489 !mTransitioningViews.isEmpty()) {
George Mount62ab9b72014-05-02 13:51:17 -0700490 mTransitioningViews.get(0).invalidate();
491 }
George Mount13ccb792014-06-06 17:02:20 -0700492 } else {
493 transitionStarted();
George Mount62ab9b72014-05-02 13:51:17 -0700494 }
495 return transition;
496 }
497
George Mount41725de2015-04-09 08:23:05 -0700498 @Override
499 protected void onTransitionsComplete() {
500 moveSharedElementsFromOverlay();
George Mountfe361d22014-07-08 17:25:25 -0700501 }
502
George Mountc93ca162014-05-23 19:21:36 -0700503 private void sharedElementTransitionStarted() {
504 mSharedElementTransitionStarted = true;
505 if (mIsExitTransitionComplete) {
506 send(MSG_EXIT_TRANSITION_COMPLETE, null);
507 }
508 }
509
George Mount62ab9b72014-05-02 13:51:17 -0700510 private void startEnterTransition(Transition transition) {
George Mount48bd13c2014-09-12 10:54:54 -0700511 ViewGroup decorView = getDecor();
512 if (!mIsReturning && decorView != null) {
513 Drawable background = decorView.getBackground();
George Mount62ab9b72014-05-02 13:51:17 -0700514 if (background != null) {
515 background = background.mutate();
George Mount99c82fd2014-09-03 07:27:47 -0700516 getWindow().setBackgroundDrawable(background);
George Mount8cab50a2014-05-15 09:57:17 -0700517 mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 255);
George Mounted1e01d2014-06-05 13:49:12 -0700518 mBackgroundAnimator.setDuration(getFadeDuration());
George Mount8cab50a2014-05-15 09:57:17 -0700519 mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
George Mount62ab9b72014-05-02 13:51:17 -0700520 @Override
521 public void onAnimationEnd(Animator animation) {
522 makeOpaque();
523 }
524 });
George Mount8cab50a2014-05-15 09:57:17 -0700525 mBackgroundAnimator.start();
George Mount62ab9b72014-05-02 13:51:17 -0700526 } else if (transition != null) {
527 transition.addListener(new Transition.TransitionListenerAdapter() {
528 @Override
529 public void onTransitionEnd(Transition transition) {
530 transition.removeListener(this);
531 makeOpaque();
532 }
533 });
534 } else {
535 makeOpaque();
536 }
537 }
538 }
539
540 public void stop() {
George Mountf1abef62014-09-22 13:58:21 -0700541 // Restore the background to its previous state since the
542 // Activity is stopping.
George Mount700db2a2014-07-07 17:17:49 -0700543 if (mBackgroundAnimator != null) {
544 mBackgroundAnimator.end();
545 mBackgroundAnimator = null;
George Mountf1abef62014-09-22 13:58:21 -0700546 } else if (mWasOpaque) {
547 ViewGroup decorView = getDecor();
548 if (decorView != null) {
549 Drawable drawable = decorView.getBackground();
550 if (drawable != null) {
551 drawable.setAlpha(1);
552 }
553 }
George Mount700db2a2014-07-07 17:17:49 -0700554 }
George Mountf1abef62014-09-22 13:58:21 -0700555 makeOpaque();
556 mIsCanceled = true;
557 mResultReceiver = null;
George Mount700db2a2014-07-07 17:17:49 -0700558 mActivity = null;
George Mountfe361d22014-07-08 17:25:25 -0700559 moveSharedElementsFromOverlay();
George Mountf1abef62014-09-22 13:58:21 -0700560 if (mTransitioningViews != null) {
561 showViews(mTransitioningViews, true);
562 }
563 showViews(mSharedElements, true);
George Mount700db2a2014-07-07 17:17:49 -0700564 clearState();
565 }
566
George Mountfbd45962015-01-26 14:38:19 -0800567 /**
568 * Cancels the enter transition.
569 * @return True if the enter transition is still pending capturing the target state. If so,
570 * any transition started on the decor will do nothing.
571 */
572 public boolean cancelEnter() {
George Mountfe361d22014-07-08 17:25:25 -0700573 setGhostVisibility(View.INVISIBLE);
George Mount62ab9b72014-05-02 13:51:17 -0700574 mHasStopped = true;
George Mount62ab9b72014-05-02 13:51:17 -0700575 mIsCanceled = true;
576 mResultReceiver = null;
George Mount8cab50a2014-05-15 09:57:17 -0700577 if (mBackgroundAnimator != null) {
578 mBackgroundAnimator.cancel();
579 mBackgroundAnimator = null;
580 }
George Mount700db2a2014-07-07 17:17:49 -0700581 mActivity = null;
582 clearState();
George Mountfbd45962015-01-26 14:38:19 -0800583 return super.cancelPendingTransitions();
George Mount62ab9b72014-05-02 13:51:17 -0700584 }
585
586 private void makeOpaque() {
George Mount8c2614c2014-06-10 11:12:01 -0700587 if (!mHasStopped && mActivity != null) {
George Mount3cc716c2014-06-12 16:35:35 -0700588 if (mWasOpaque) {
589 mActivity.convertFromTranslucent();
590 }
George Mount62ab9b72014-05-02 13:51:17 -0700591 mActivity = null;
592 }
593 }
594
595 private boolean allowOverlappingTransitions() {
596 return mIsReturning ? getWindow().getAllowExitTransitionOverlap()
597 : getWindow().getAllowEnterTransitionOverlap();
598 }
599
600 private void startRejectedAnimations(final ArrayList<View> rejectedSnapshots) {
601 if (rejectedSnapshots == null || rejectedSnapshots.isEmpty()) {
602 return;
603 }
George Mount48bd13c2014-09-12 10:54:54 -0700604 final ViewGroup decorView = getDecor();
605 if (decorView != null) {
606 ViewGroupOverlay overlay = decorView.getOverlay();
607 ObjectAnimator animator = null;
608 int numRejected = rejectedSnapshots.size();
609 for (int i = 0; i < numRejected; i++) {
610 View snapshot = rejectedSnapshots.get(i);
611 overlay.add(snapshot);
612 animator = ObjectAnimator.ofFloat(snapshot, View.ALPHA, 1, 0);
613 animator.start();
George Mount62ab9b72014-05-02 13:51:17 -0700614 }
George Mount48bd13c2014-09-12 10:54:54 -0700615 animator.addListener(new AnimatorListenerAdapter() {
616 @Override
617 public void onAnimationEnd(Animator animation) {
618 ViewGroupOverlay overlay = decorView.getOverlay();
619 int numRejected = rejectedSnapshots.size();
620 for (int i = 0; i < numRejected; i++) {
621 overlay.remove(rejectedSnapshots.get(i));
622 }
623 }
624 });
625 }
George Mount62ab9b72014-05-02 13:51:17 -0700626 }
627
628 protected void onRemoteExitTransitionComplete() {
629 if (!allowOverlappingTransitions()) {
George Mount13ccb792014-06-06 17:02:20 -0700630 startEnterTransitionOnly();
George Mount62ab9b72014-05-02 13:51:17 -0700631 }
632 }
George Mount13ccb792014-06-06 17:02:20 -0700633
634 private void startEnterTransitionOnly() {
635 startTransition(new Runnable() {
636 @Override
637 public void run() {
George Mount13ccb792014-06-06 17:02:20 -0700638 boolean startEnterTransition = true;
639 boolean startSharedElementTransition = false;
George Mount48bd13c2014-09-12 10:54:54 -0700640 ViewGroup decorView = getDecor();
641 if (decorView != null) {
642 Transition transition = beginTransition(decorView, startEnterTransition,
643 startSharedElementTransition);
644 startEnterTransition(transition);
645 }
George Mount13ccb792014-06-06 17:02:20 -0700646 }
647 });
648 }
George Mountfe361d22014-07-08 17:25:25 -0700649
George Mount31a21722014-03-24 17:44:36 -0700650}