blob: c6736eefbc71638a574d8dc32be2a0936ad19a7a [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 Mountd7d0fb32016-01-04 14:56:08 -080033import android.view.ViewTreeObserver.OnPreDrawListener;
George Mountf1abef62014-09-22 13:58:21 -070034import android.view.Window;
George Mount10d312b2015-12-16 14:00:35 -080035import android.view.accessibility.AccessibilityEvent;
George Mount31a21722014-03-24 17:44:36 -070036
37import java.util.ArrayList;
38
39/**
40 * This ActivityTransitionCoordinator is created by the Activity to manage
George Mount62ab9b72014-05-02 13:51:17 -070041 * the enter scene and shared element transfer into the Scene, either during
42 * launch of an Activity or returning from a launched Activity.
George Mount31a21722014-03-24 17:44:36 -070043 */
George Mount62ab9b72014-05-02 13:51:17 -070044class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
George Mount31a21722014-03-24 17:44:36 -070045 private static final String TAG = "EnterTransitionCoordinator";
46
George Mounte678ab62014-06-30 15:28:28 -070047 private static final int MIN_ANIMATION_FRAMES = 2;
George Mount31a21722014-03-24 17:44:36 -070048
George Mount62ab9b72014-05-02 13:51:17 -070049 private boolean mSharedElementTransitionStarted;
George Mount31a21722014-03-24 17:44:36 -070050 private Activity mActivity;
George Mount62ab9b72014-05-02 13:51:17 -070051 private boolean mHasStopped;
George Mount62ab9b72014-05-02 13:51:17 -070052 private boolean mIsCanceled;
George Mount8cab50a2014-05-15 09:57:17 -070053 private ObjectAnimator mBackgroundAnimator;
George Mountc93ca162014-05-23 19:21:36 -070054 private boolean mIsExitTransitionComplete;
George Mount8c2614c2014-06-10 11:12:01 -070055 private boolean mIsReadyForTransition;
56 private Bundle mSharedElementsBundle;
George Mount3cc716c2014-06-12 16:35:35 -070057 private boolean mWasOpaque;
George Mount82ffc142014-06-30 16:47:34 -070058 private boolean mAreViewsReady;
George Mount8d3cd2c2014-07-08 11:07:33 -070059 private boolean mIsViewsTransitionStarted;
George Mounta2bbbb32014-08-12 10:16:20 -070060 private Transition mEnterViewsTransition;
George Mountd7d0fb32016-01-04 14:56:08 -080061 private OnPreDrawListener mViewsReadyListener;
George Mount413739e2016-06-08 07:13:37 -070062 private final boolean mIsCrossTask;
George Mount31a21722014-03-24 17:44:36 -070063
George Mount62ab9b72014-05-02 13:51:17 -070064 public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
George Mount413739e2016-06-08 07:13:37 -070065 ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask) {
George Mount8c2614c2014-06-10 11:12:01 -070066 super(activity.getWindow(), sharedElementNames,
George Mount413739e2016-06-08 07:13:37 -070067 getListener(activity, isReturning && !isCrossTask), isReturning);
George Mount31a21722014-03-24 17:44:36 -070068 mActivity = activity;
George Mount413739e2016-06-08 07:13:37 -070069 mIsCrossTask = isCrossTask;
George Mount62ab9b72014-05-02 13:51:17 -070070 setResultReceiver(resultReceiver);
71 prepareEnter();
72 Bundle resultReceiverBundle = new Bundle();
73 resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this);
74 mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
George Mount6e7fb602014-09-04 16:20:20 -070075 final View decorView = getDecor();
George Mount48bd13c2014-09-12 10:54:54 -070076 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;
George Mounted1e01d2014-06-05 13:49:12 -070085 }
George Mount48bd13c2014-09-12 10:54:54 -070086 });
87 }
George Mount8c2614c2014-06-10 11:12:01 -070088 }
89
George Mount413739e2016-06-08 07:13:37 -070090 boolean isCrossTask() {
91 return mIsCrossTask;
92 }
93
Dake Gub04b36e2014-08-08 11:22:30 -070094 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 }
George Mount1fecfb22014-06-18 14:55:55 -0700110 }
George Mount8c2614c2014-06-10 11:12:01 -0700111
George Mount1fecfb22014-06-18 14:55:55 -0700112 public void namedViewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
George Mount82ffc142014-06-30 16:47:34 -0700113 triggerViewsReady(mapNamedElements(accepted, localNames));
George Mount1fecfb22014-06-18 14:55:55 -0700114 }
115
George Mounta2bbbb32014-08-12 10:16:20 -0700116 public Transition getEnterViewsTransition() {
117 return mEnterViewsTransition;
118 }
119
George Mount1fecfb22014-06-18 14:55:55 -0700120 @Override
121 protected void viewsReady(ArrayMap<String, View> sharedElements) {
122 super.viewsReady(sharedElements);
George Mount8c2614c2014-06-10 11:12:01 -0700123 mIsReadyForTransition = true;
George Mount0f0c4732014-09-05 13:47:47 -0700124 hideViews(mSharedElements);
George Mountb694e082014-09-12 07:34:52 -0700125 if (getViewsTransition() != null && mTransitioningViews != null) {
George Mount0f0c4732014-09-05 13:47:47 -0700126 hideViews(mTransitioningViews);
George Mount8c2614c2014-06-10 11:12:01 -0700127 }
George Mountfe361d22014-07-08 17:25:25 -0700128 if (mIsReturning) {
129 sendSharedElementDestination();
130 } else {
George Mountfe361d22014-07-08 17:25:25 -0700131 moveSharedElementsToOverlay();
132 }
George Mount8c2614c2014-06-10 11:12:01 -0700133 if (mSharedElementsBundle != null) {
134 onTakeSharedElements();
135 }
George Mountd265bd72014-06-02 07:22:59 -0700136 }
137
George Mount82ffc142014-06-30 16:47:34 -0700138 private void triggerViewsReady(final ArrayMap<String, View> sharedElements) {
139 if (mAreViewsReady) {
140 return;
141 }
142 mAreViewsReady = true;
George Mount1fb941d2014-10-30 09:15:32 -0700143 final ViewGroup decor = getDecor();
George Mount82ffc142014-06-30 16:47:34 -0700144 // Ensure the views have been laid out before capturing the views -- we need the epicenter.
George Mount1fb941d2014-10-30 09:15:32 -0700145 if (decor == null || (decor.isAttachedToWindow() &&
146 (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) {
George Mount82ffc142014-06-30 16:47:34 -0700147 viewsReady(sharedElements);
148 } else {
George Mountd7d0fb32016-01-04 14:56:08 -0800149 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);
George Mount5ae0b7a2016-05-31 12:58:52 -0700159 decor.invalidate();
George Mount82ffc142014-06-30 16:47:34 -0700160 }
161 }
162
George Mount1fecfb22014-06-18 14:55:55 -0700163 private ArrayMap<String, View> mapNamedElements(ArrayList<String> accepted,
164 ArrayList<String> localNames) {
165 ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
George Mount48bd13c2014-09-12 10:54:54 -0700166 ViewGroup decorView = getDecor();
167 if (decorView != null) {
168 decorView.findNamedViews(sharedElements);
169 }
George Mount1fecfb22014-06-18 14:55:55 -0700170 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)) {
George Mount35885632016-06-14 08:35:09 -0700175 View view = sharedElements.get(localName);
George Mount1fecfb22014-06-18 14:55:55 -0700176 if (view != null) {
177 sharedElements.put(acceptedName, view);
178 }
179 }
180 }
181 }
182 return sharedElements;
183 }
184
George Mountd265bd72014-06-02 07:22:59 -0700185 private void sendSharedElementDestination() {
George Mountfe361d22014-07-08 17:25:25 -0700186 boolean allReady;
George Mount48bd13c2014-09-12 10:54:54 -0700187 final View decorView = getDecor();
Dake Gu872efe42014-08-29 15:24:58 -0700188 if (allowOverlappingTransitions() && getEnterViewsTransition() != null) {
George Mountfe361d22014-07-08 17:25:25 -0700189 allReady = false;
George Mount48bd13c2014-09-12 10:54:54 -0700190 } else if (decorView == null) {
191 allReady = true;
George Mountfe361d22014-07-08 17:25:25 -0700192 } else {
George Mount48bd13c2014-09-12 10:54:54 -0700193 allReady = !decorView.isLayoutRequested();
George Mountfe361d22014-07-08 17:25:25 -0700194 if (allReady) {
195 for (int i = 0; i < mSharedElements.size(); i++) {
196 if (mSharedElements.get(i).isLayoutRequested()) {
197 allReady = false;
198 break;
199 }
George Mount67d92432014-06-06 13:34:20 -0700200 }
201 }
202 }
203 if (allReady) {
George Mountc93ca162014-05-23 19:21:36 -0700204 Bundle state = captureSharedElementState();
George Mountfe361d22014-07-08 17:25:25 -0700205 moveSharedElementsToOverlay();
George Mountc93ca162014-05-23 19:21:36 -0700206 mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
George Mount48bd13c2014-09-12 10:54:54 -0700207 } else if (decorView != null) {
George Mount6e7fb602014-09-04 16:20:20 -0700208 decorView.getViewTreeObserver()
George Mountd265bd72014-06-02 07:22:59 -0700209 .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
210 @Override
211 public boolean onPreDraw() {
George Mount6e7fb602014-09-04 16:20:20 -0700212 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
George Mountf31d83c2014-08-29 13:32:20 -0700213 if (mResultReceiver != null) {
214 Bundle state = captureSharedElementState();
George Mountf31d83c2014-08-29 13:32:20 -0700215 moveSharedElementsToOverlay();
216 mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
217 }
George Mountd265bd72014-06-02 07:22:59 -0700218 return true;
219 }
220 });
George Mount31a21722014-03-24 17:44:36 -0700221 }
George Mount13ccb792014-06-06 17:02:20 -0700222 if (allowOverlappingTransitions()) {
223 startEnterTransitionOnly();
224 }
George Mount31a21722014-03-24 17:44:36 -0700225 }
226
George Mount65580562014-08-29 08:15:48 -0700227 private static SharedElementCallback getListener(Activity activity, boolean isReturning) {
George Mount800d72b2014-05-19 07:09:00 -0700228 return isReturning ? activity.mExitTransitionListener : activity.mEnterTransitionListener;
229 }
230
George Mount31a21722014-03-24 17:44:36 -0700231 @Override
George Mount62ab9b72014-05-02 13:51:17 -0700232 protected void onReceiveResult(int resultCode, Bundle resultData) {
233 switch (resultCode) {
234 case MSG_TAKE_SHARED_ELEMENTS:
235 if (!mIsCanceled) {
George Mount8c2614c2014-06-10 11:12:01 -0700236 mSharedElementsBundle = resultData;
237 onTakeSharedElements();
George Mount62ab9b72014-05-02 13:51:17 -0700238 }
239 break;
240 case MSG_EXIT_TRANSITION_COMPLETE:
241 if (!mIsCanceled) {
George Mountc93ca162014-05-23 19:21:36 -0700242 mIsExitTransitionComplete = true;
243 if (mSharedElementTransitionStarted) {
George Mount62ab9b72014-05-02 13:51:17 -0700244 onRemoteExitTransitionComplete();
245 }
246 }
247 break;
248 case MSG_CANCEL:
249 cancel();
250 break;
251 }
George Mount31a21722014-03-24 17:44:36 -0700252 }
253
George Mountbdc4d8d2016-03-23 13:10:13 -0700254 public boolean isWaitingForRemoteExit() {
255 return mIsReturning && mResultReceiver != null;
256 }
257
George Mountd7d0fb32016-01-04 14:56:08 -0800258 /**
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);
George Mount62976722016-02-04 16:45:53 -0800277 setTransitioningViewsVisiblity(View.VISIBLE, true);
George Mountd7d0fb32016-01-04 14:56:08 -0800278 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);
George Mount62976722016-02-04 16:45:53 -0800295 setTransitioningViewsVisiblity(View.VISIBLE, true);
George Mountd7d0fb32016-01-04 14:56:08 -0800296 mTransitioningViews.clear();
297 viewsTransitionComplete();
298 }
299 cancelPendingTransitions();
300 }
301 mAreViewsReady = true;
George Mountbdc4d8d2016-03-23 13:10:13 -0700302 if (mResultReceiver != null) {
303 mResultReceiver.send(MSG_CANCEL, null);
304 mResultReceiver = null;
305 }
George Mountd7d0fb32016-01-04 14:56:08 -0800306 }
307
George Mount62ab9b72014-05-02 13:51:17 -0700308 private void cancel() {
309 if (!mIsCanceled) {
310 mIsCanceled = true;
George Mount8d3cd2c2014-07-08 11:07:33 -0700311 if (getViewsTransition() == null || mIsViewsTransitionStarted) {
George Mountce2ee3d2014-09-09 15:04:31 -0700312 showViews(mSharedElements, true);
George Mountb694e082014-09-12 07:34:52 -0700313 } else if (mTransitioningViews != null) {
George Mount62ab9b72014-05-02 13:51:17 -0700314 mTransitioningViews.addAll(mSharedElements);
315 }
George Mounta228d7e2015-11-17 12:49:22 -0800316 moveSharedElementsFromOverlay();
George Mount62ab9b72014-05-02 13:51:17 -0700317 mSharedElementNames.clear();
318 mSharedElements.clear();
319 mAllSharedElementNames.clear();
George Mount8c2614c2014-06-10 11:12:01 -0700320 startSharedElementTransition(null);
George Mount62ab9b72014-05-02 13:51:17 -0700321 onRemoteExitTransitionComplete();
322 }
George Mount31a21722014-03-24 17:44:36 -0700323 }
324
George Mount62ab9b72014-05-02 13:51:17 -0700325 public boolean isReturning() {
326 return mIsReturning;
George Mount31a21722014-03-24 17:44:36 -0700327 }
328
George Mount62ab9b72014-05-02 13:51:17 -0700329 protected void prepareEnter() {
George Mountf1abef62014-09-22 13:58:21 -0700330 ViewGroup decorView = getDecor();
331 if (mActivity == null || decorView == null) {
332 return;
333 }
George Mount413739e2016-06-08 07:13:37 -0700334 if (!isCrossTask()) {
335 mActivity.overridePendingTransition(0, 0);
336 }
George Mount62ab9b72014-05-02 13:51:17 -0700337 if (!mIsReturning) {
George Mount3cc716c2014-06-12 16:35:35 -0700338 mWasOpaque = mActivity.convertToTranslucent(null, null);
George Mountf1abef62014-09-22 13:58:21 -0700339 Drawable background = decorView.getBackground();
George Mount62ab9b72014-05-02 13:51:17 -0700340 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
George Mounta712e8c2014-05-20 15:10:20 -0700351 @Override
352 protected Transition getViewsTransition() {
George Mountf1abef62014-09-22 13:58:21 -0700353 Window window = getWindow();
354 if (window == null) {
355 return null;
356 }
George Mounta712e8c2014-05-20 15:10:20 -0700357 if (mIsReturning) {
George Mountf1abef62014-09-22 13:58:21 -0700358 return window.getReenterTransition();
George Mounta712e8c2014-05-20 15:10:20 -0700359 } else {
George Mountf1abef62014-09-22 13:58:21 -0700360 return window.getEnterTransition();
George Mounta712e8c2014-05-20 15:10:20 -0700361 }
362 }
363
364 protected Transition getSharedElementTransition() {
George Mountf1abef62014-09-22 13:58:21 -0700365 Window window = getWindow();
366 if (window == null) {
367 return null;
368 }
George Mounta712e8c2014-05-20 15:10:20 -0700369 if (mIsReturning) {
George Mountf1abef62014-09-22 13:58:21 -0700370 return window.getSharedElementReenterTransition();
George Mounta712e8c2014-05-20 15:10:20 -0700371 } else {
George Mountf1abef62014-09-22 13:58:21 -0700372 return window.getSharedElementEnterTransition();
George Mounta712e8c2014-05-20 15:10:20 -0700373 }
374 }
375
George Mount8c2614c2014-06-10 11:12:01 -0700376 private void startSharedElementTransition(Bundle sharedElementState) {
George Mount48bd13c2014-09-12 10:54:54 -0700377 ViewGroup decorView = getDecor();
378 if (decorView == null) {
379 return;
380 }
George Mount62ab9b72014-05-02 13:51:17 -0700381 // Remove rejected shared elements
382 ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
383 rejectedNames.removeAll(mSharedElementNames);
384 ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames);
George Mount1732f522014-09-17 16:59:36 -0700385 if (mListener != null) {
386 mListener.onRejectSharedElements(rejectedSnapshots);
387 }
George Mount83c692e2014-10-23 11:11:17 -0700388 removeNullViews(rejectedSnapshots);
George Mount62ab9b72014-05-02 13:51:17 -0700389 startRejectedAnimations(rejectedSnapshots);
390
391 // Now start shared element transition
392 ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
393 mSharedElementNames);
George Mountce2ee3d2014-09-09 15:04:31 -0700394 showViews(mSharedElements, true);
George Mountfe361d22014-07-08 17:25:25 -0700395 scheduleSetSharedElementEnd(sharedElementSnapshots);
Dake Guc18f4cc2014-07-11 17:48:37 -0700396 ArrayList<SharedElementOriginalState> originalImageViewState =
George Mount62ab9b72014-05-02 13:51:17 -0700397 setSharedElementState(sharedElementState, sharedElementSnapshots);
George Mountc93ca162014-05-23 19:21:36 -0700398 requestLayoutForSharedElements();
George Mount62ab9b72014-05-02 13:51:17 -0700399
George Mount13ccb792014-06-06 17:02:20 -0700400 boolean startEnterTransition = allowOverlappingTransitions() && !mIsReturning;
George Mount62ab9b72014-05-02 13:51:17 -0700401 boolean startSharedElementTransition = true;
George Mountfe361d22014-07-08 17:25:25 -0700402 setGhostVisibility(View.INVISIBLE);
403 scheduleGhostVisibilityChange(View.INVISIBLE);
George Mount80141d12015-07-14 10:03:06 -0700404 pauseInput();
George Mount48bd13c2014-09-12 10:54:54 -0700405 Transition transition = beginTransition(decorView, startEnterTransition,
406 startSharedElementTransition);
George Mountfe361d22014-07-08 17:25:25 -0700407 scheduleGhostVisibilityChange(View.VISIBLE);
408 setGhostVisibility(View.VISIBLE);
George Mount62ab9b72014-05-02 13:51:17 -0700409
410 if (startEnterTransition) {
411 startEnterTransition(transition);
412 }
413
Dake Guc18f4cc2014-07-11 17:48:37 -0700414 setOriginalSharedElementState(mSharedElements, originalImageViewState);
George Mount62ab9b72014-05-02 13:51:17 -0700415
416 if (mResultReceiver != null) {
George Mounte678ab62014-06-30 15:28:28 -0700417 // 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.
George Mount48bd13c2014-09-12 10:54:54 -0700419 decorView.postOnAnimation(new Runnable() {
George Mounte678ab62014-06-30 15:28:28 -0700420 int mAnimations;
George Mount48bd13c2014-09-12 10:54:54 -0700421
George Mounte678ab62014-06-30 15:28:28 -0700422 @Override
423 public void run() {
424 if (mAnimations++ < MIN_ANIMATION_FRAMES) {
George Mount4188d0d2014-09-11 17:47:11 -0700425 View decorView = getDecor();
426 if (decorView != null) {
427 decorView.postOnAnimation(this);
428 }
George Mount407d1fa2014-08-28 16:11:49 -0700429 } else if (mResultReceiver != null) {
George Mounte678ab62014-06-30 15:28:28 -0700430 mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
431 mResultReceiver = null; // all done sending messages.
432 }
433 }
434 });
George Mount62ab9b72014-05-02 13:51:17 -0700435 }
George Mount62ab9b72014-05-02 13:51:17 -0700436 }
437
George Mount83c692e2014-10-23 11:11:17 -0700438 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
George Mount13ccb792014-06-06 17:02:20 -0700448 private void onTakeSharedElements() {
449 if (!mIsReadyForTransition || mSharedElementsBundle == null) {
450 return;
451 }
452 final Bundle sharedElementState = mSharedElementsBundle;
453 mSharedElementsBundle = null;
George Mount4dc668c2015-04-09 20:55:59 +0000454 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() {
George Mount48bd13c2014-09-12 10:54:54 -0700461 @Override
George Mount4dc668c2015-04-09 20:55:59 +0000462 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;
George Mount48bd13c2014-09-12 10:54:54 -0700471 }
472 });
George Mount4dc668c2015-04-09 20:55:59 +0000473 decorView.invalidate();
474 }
475 }
476 };
477 if (mListener == null) {
478 listener.onSharedElementsReady();
479 } else {
480 mListener.onSharedElementsArrived(mSharedElementNames, mSharedElements, listener);
George Mount48bd13c2014-09-12 10:54:54 -0700481 }
George Mount13ccb792014-06-06 17:02:20 -0700482 }
483
George Mountc93ca162014-05-23 19:21:36 -0700484 private void requestLayoutForSharedElements() {
485 int numSharedElements = mSharedElements.size();
486 for (int i = 0; i < numSharedElements; i++) {
487 mSharedElements.get(i).requestLayout();
488 }
489 }
490
George Mount48bd13c2014-09-12 10:54:54 -0700491 private Transition beginTransition(ViewGroup decorView, boolean startEnterTransition,
George Mount62ab9b72014-05-02 13:51:17 -0700492 boolean startSharedElementTransition) {
493 Transition sharedElementTransition = null;
George Mountc93ca162014-05-23 19:21:36 -0700494 if (startSharedElementTransition) {
George Mountfe361d22014-07-08 17:25:25 -0700495 if (!mSharedElementNames.isEmpty()) {
496 sharedElementTransition = configureTransition(getSharedElementTransition(), false);
497 }
498 if (sharedElementTransition == null) {
George Mountc93ca162014-05-23 19:21:36 -0700499 sharedElementTransitionStarted();
George Mountfe361d22014-07-08 17:25:25 -0700500 sharedElementTransitionComplete();
George Mountc93ca162014-05-23 19:21:36 -0700501 } else {
George Mountfe361d22014-07-08 17:25:25 -0700502 sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
George Mountc93ca162014-05-23 19:21:36 -0700503 @Override
504 public void onTransitionStart(Transition transition) {
George Mountc93ca162014-05-23 19:21:36 -0700505 sharedElementTransitionStarted();
506 }
George Mountfe361d22014-07-08 17:25:25 -0700507
508 @Override
509 public void onTransitionEnd(Transition transition) {
510 transition.removeListener(this);
511 sharedElementTransitionComplete();
512 }
George Mountc93ca162014-05-23 19:21:36 -0700513 });
514 }
515 }
George Mountfe361d22014-07-08 17:25:25 -0700516 Transition viewsTransition = null;
517 if (startEnterTransition) {
518 mIsViewsTransitionStarted = true;
George Mountb694e082014-09-12 07:34:52 -0700519 if (mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
George Mountfe361d22014-07-08 17:25:25 -0700520 viewsTransition = configureTransition(getViewsTransition(), true);
521 if (viewsTransition != null && !mIsReturning) {
522 stripOffscreenViews();
523 }
George Mount13ccb792014-06-06 17:02:20 -0700524 }
George Mountfe361d22014-07-08 17:25:25 -0700525 if (viewsTransition == null) {
George Mount41725de2015-04-09 08:23:05 -0700526 viewsTransitionComplete();
George Mountfe361d22014-07-08 17:25:25 -0700527 } else {
George Mountb694e082014-09-12 07:34:52 -0700528 final ArrayList<View> transitioningViews = mTransitioningViews;
George Mountfe361d22014-07-08 17:25:25 -0700529 viewsTransition.addListener(new ContinueTransitionListener() {
530 @Override
George Mounta2bbbb32014-08-12 10:16:20 -0700531 public void onTransitionStart(Transition transition) {
532 mEnterViewsTransition = transition;
George Mountb694e082014-09-12 07:34:52 -0700533 if (transitioningViews != null) {
534 showViews(transitioningViews, false);
535 }
George Mounta2bbbb32014-08-12 10:16:20 -0700536 super.onTransitionStart(transition);
537 }
538
539 @Override
George Mountfe361d22014-07-08 17:25:25 -0700540 public void onTransitionEnd(Transition transition) {
George Mounta2bbbb32014-08-12 10:16:20 -0700541 mEnterViewsTransition = null;
George Mountfe361d22014-07-08 17:25:25 -0700542 transition.removeListener(this);
George Mount41725de2015-04-09 08:23:05 -0700543 viewsTransitionComplete();
George Mountfe361d22014-07-08 17:25:25 -0700544 super.onTransitionEnd(transition);
545 }
546 });
547 }
548 }
549
550 Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
551 if (transition != null) {
552 transition.addListener(new ContinueTransitionListener());
George Mount62976722016-02-04 16:45:53 -0800553 if (startEnterTransition) {
554 setTransitioningViewsVisiblity(View.INVISIBLE, false);
555 }
George Mount48bd13c2014-09-12 10:54:54 -0700556 TransitionManager.beginDelayedTransition(decorView, transition);
George Mount62976722016-02-04 16:45:53 -0800557 if (startEnterTransition) {
George Mount52ff2b72016-03-23 11:08:39 -0700558 setTransitioningViewsVisiblity(View.VISIBLE, false);
George Mount62ab9b72014-05-02 13:51:17 -0700559 }
George Mount52ff2b72016-03-23 11:08:39 -0700560 decorView.invalidate();
George Mount13ccb792014-06-06 17:02:20 -0700561 } else {
562 transitionStarted();
George Mount62ab9b72014-05-02 13:51:17 -0700563 }
564 return transition;
565 }
566
George Mount41725de2015-04-09 08:23:05 -0700567 @Override
568 protected void onTransitionsComplete() {
569 moveSharedElementsFromOverlay();
George Mount10d312b2015-12-16 14:00:35 -0800570 final ViewGroup decorView = getDecor();
571 if (decorView != null) {
572 decorView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
573 }
George Mountfe361d22014-07-08 17:25:25 -0700574 }
575
George Mountc93ca162014-05-23 19:21:36 -0700576 private void sharedElementTransitionStarted() {
577 mSharedElementTransitionStarted = true;
578 if (mIsExitTransitionComplete) {
579 send(MSG_EXIT_TRANSITION_COMPLETE, null);
580 }
581 }
582
George Mount62ab9b72014-05-02 13:51:17 -0700583 private void startEnterTransition(Transition transition) {
George Mount48bd13c2014-09-12 10:54:54 -0700584 ViewGroup decorView = getDecor();
585 if (!mIsReturning && decorView != null) {
586 Drawable background = decorView.getBackground();
George Mount62ab9b72014-05-02 13:51:17 -0700587 if (background != null) {
588 background = background.mutate();
George Mount99c82fd2014-09-03 07:27:47 -0700589 getWindow().setBackgroundDrawable(background);
George Mount8cab50a2014-05-15 09:57:17 -0700590 mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 255);
George Mounted1e01d2014-06-05 13:49:12 -0700591 mBackgroundAnimator.setDuration(getFadeDuration());
George Mount8cab50a2014-05-15 09:57:17 -0700592 mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
George Mount62ab9b72014-05-02 13:51:17 -0700593 @Override
594 public void onAnimationEnd(Animator animation) {
595 makeOpaque();
596 }
597 });
George Mount8cab50a2014-05-15 09:57:17 -0700598 mBackgroundAnimator.start();
George Mount62ab9b72014-05-02 13:51:17 -0700599 } 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() {
George Mountf1abef62014-09-22 13:58:21 -0700614 // Restore the background to its previous state since the
615 // Activity is stopping.
George Mount700db2a2014-07-07 17:17:49 -0700616 if (mBackgroundAnimator != null) {
617 mBackgroundAnimator.end();
618 mBackgroundAnimator = null;
George Mountf1abef62014-09-22 13:58:21 -0700619 } 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 }
George Mount700db2a2014-07-07 17:17:49 -0700627 }
George Mountf1abef62014-09-22 13:58:21 -0700628 makeOpaque();
629 mIsCanceled = true;
630 mResultReceiver = null;
George Mount700db2a2014-07-07 17:17:49 -0700631 mActivity = null;
George Mountfe361d22014-07-08 17:25:25 -0700632 moveSharedElementsFromOverlay();
George Mountf1abef62014-09-22 13:58:21 -0700633 if (mTransitioningViews != null) {
634 showViews(mTransitioningViews, true);
George Mount62976722016-02-04 16:45:53 -0800635 setTransitioningViewsVisiblity(View.VISIBLE, true);
George Mountf1abef62014-09-22 13:58:21 -0700636 }
637 showViews(mSharedElements, true);
George Mount700db2a2014-07-07 17:17:49 -0700638 clearState();
639 }
640
George Mountfbd45962015-01-26 14:38:19 -0800641 /**
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() {
George Mountfe361d22014-07-08 17:25:25 -0700647 setGhostVisibility(View.INVISIBLE);
George Mount62ab9b72014-05-02 13:51:17 -0700648 mHasStopped = true;
George Mount62ab9b72014-05-02 13:51:17 -0700649 mIsCanceled = true;
George Mount3bbc2ed2015-11-18 11:10:55 -0800650 clearState();
651 return super.cancelPendingTransitions();
652 }
653
654 @Override
655 protected void clearState() {
656 mSharedElementsBundle = null;
657 mEnterViewsTransition = null;
George Mount62ab9b72014-05-02 13:51:17 -0700658 mResultReceiver = null;
George Mount8cab50a2014-05-15 09:57:17 -0700659 if (mBackgroundAnimator != null) {
660 mBackgroundAnimator.cancel();
661 mBackgroundAnimator = null;
662 }
George Mount3bbc2ed2015-11-18 11:10:55 -0800663 super.clearState();
George Mount62ab9b72014-05-02 13:51:17 -0700664 }
665
666 private void makeOpaque() {
George Mount8c2614c2014-06-10 11:12:01 -0700667 if (!mHasStopped && mActivity != null) {
George Mount3cc716c2014-06-12 16:35:35 -0700668 if (mWasOpaque) {
669 mActivity.convertFromTranslucent();
670 }
George Mount62ab9b72014-05-02 13:51:17 -0700671 mActivity = null;
672 }
673 }
674
675 private boolean allowOverlappingTransitions() {
George Mount5aec62e2015-05-08 14:21:20 -0700676 return mIsReturning ? getWindow().getAllowReturnTransitionOverlap()
George Mount62ab9b72014-05-02 13:51:17 -0700677 : getWindow().getAllowEnterTransitionOverlap();
678 }
679
680 private void startRejectedAnimations(final ArrayList<View> rejectedSnapshots) {
681 if (rejectedSnapshots == null || rejectedSnapshots.isEmpty()) {
682 return;
683 }
George Mount48bd13c2014-09-12 10:54:54 -0700684 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();
George Mount62ab9b72014-05-02 13:51:17 -0700694 }
George Mount48bd13c2014-09-12 10:54:54 -0700695 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 }
George Mount62ab9b72014-05-02 13:51:17 -0700706 }
707
708 protected void onRemoteExitTransitionComplete() {
709 if (!allowOverlappingTransitions()) {
George Mount13ccb792014-06-06 17:02:20 -0700710 startEnterTransitionOnly();
George Mount62ab9b72014-05-02 13:51:17 -0700711 }
712 }
George Mount13ccb792014-06-06 17:02:20 -0700713
714 private void startEnterTransitionOnly() {
715 startTransition(new Runnable() {
716 @Override
717 public void run() {
George Mount13ccb792014-06-06 17:02:20 -0700718 boolean startEnterTransition = true;
719 boolean startSharedElementTransition = false;
George Mount48bd13c2014-09-12 10:54:54 -0700720 ViewGroup decorView = getDecor();
721 if (decorView != null) {
722 Transition transition = beginTransition(decorView, startEnterTransition,
723 startSharedElementTransition);
724 startEnterTransition(transition);
725 }
George Mount13ccb792014-06-06 17:02:20 -0700726 }
727 });
728 }
George Mount31a21722014-03-24 17:44:36 -0700729}