blob: ddd0ae90fc5cbe792b134267a6aaea47026eb822 [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 Mount31a21722014-03-24 17:44:36 -070062
George Mount62ab9b72014-05-02 13:51:17 -070063 public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
George Mount8c2614c2014-06-10 11:12:01 -070064 ArrayList<String> sharedElementNames, boolean isReturning) {
65 super(activity.getWindow(), sharedElementNames,
66 getListener(activity, isReturning), isReturning);
George Mount31a21722014-03-24 17:44:36 -070067 mActivity = activity;
George Mount62ab9b72014-05-02 13:51:17 -070068 setResultReceiver(resultReceiver);
69 prepareEnter();
70 Bundle resultReceiverBundle = new Bundle();
71 resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this);
72 mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
George Mount6e7fb602014-09-04 16:20:20 -070073 final View decorView = getDecor();
George Mount48bd13c2014-09-12 10:54:54 -070074 if (decorView != null) {
75 decorView.getViewTreeObserver().addOnPreDrawListener(
76 new ViewTreeObserver.OnPreDrawListener() {
77 @Override
78 public boolean onPreDraw() {
79 if (mIsReadyForTransition) {
80 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
81 }
82 return mIsReadyForTransition;
George Mounted1e01d2014-06-05 13:49:12 -070083 }
George Mount48bd13c2014-09-12 10:54:54 -070084 });
85 }
George Mount8c2614c2014-06-10 11:12:01 -070086 }
87
Dake Gub04b36e2014-08-08 11:22:30 -070088 public void viewInstancesReady(ArrayList<String> accepted, ArrayList<String> localNames,
89 ArrayList<View> localViews) {
90 boolean remap = false;
91 for (int i = 0; i < localViews.size(); i++) {
92 View view = localViews.get(i);
93 if (!TextUtils.equals(view.getTransitionName(), localNames.get(i))
94 || !view.isAttachedToWindow()) {
95 remap = true;
96 break;
97 }
98 }
99 if (remap) {
100 triggerViewsReady(mapNamedElements(accepted, localNames));
101 } else {
102 triggerViewsReady(mapSharedElements(accepted, localViews));
103 }
George Mount1fecfb22014-06-18 14:55:55 -0700104 }
George Mount8c2614c2014-06-10 11:12:01 -0700105
George Mount1fecfb22014-06-18 14:55:55 -0700106 public void namedViewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
George Mount82ffc142014-06-30 16:47:34 -0700107 triggerViewsReady(mapNamedElements(accepted, localNames));
George Mount1fecfb22014-06-18 14:55:55 -0700108 }
109
George Mounta2bbbb32014-08-12 10:16:20 -0700110 public Transition getEnterViewsTransition() {
111 return mEnterViewsTransition;
112 }
113
George Mount1fecfb22014-06-18 14:55:55 -0700114 @Override
115 protected void viewsReady(ArrayMap<String, View> sharedElements) {
116 super.viewsReady(sharedElements);
George Mount8c2614c2014-06-10 11:12:01 -0700117 mIsReadyForTransition = true;
George Mount0f0c4732014-09-05 13:47:47 -0700118 hideViews(mSharedElements);
George Mountb694e082014-09-12 07:34:52 -0700119 if (getViewsTransition() != null && mTransitioningViews != null) {
George Mount0f0c4732014-09-05 13:47:47 -0700120 hideViews(mTransitioningViews);
George Mount8c2614c2014-06-10 11:12:01 -0700121 }
George Mountfe361d22014-07-08 17:25:25 -0700122 if (mIsReturning) {
123 sendSharedElementDestination();
124 } else {
George Mountfe361d22014-07-08 17:25:25 -0700125 moveSharedElementsToOverlay();
126 }
George Mount8c2614c2014-06-10 11:12:01 -0700127 if (mSharedElementsBundle != null) {
128 onTakeSharedElements();
129 }
George Mountd265bd72014-06-02 07:22:59 -0700130 }
131
George Mount82ffc142014-06-30 16:47:34 -0700132 private void triggerViewsReady(final ArrayMap<String, View> sharedElements) {
133 if (mAreViewsReady) {
134 return;
135 }
136 mAreViewsReady = true;
George Mount1fb941d2014-10-30 09:15:32 -0700137 final ViewGroup decor = getDecor();
George Mount82ffc142014-06-30 16:47:34 -0700138 // Ensure the views have been laid out before capturing the views -- we need the epicenter.
George Mount1fb941d2014-10-30 09:15:32 -0700139 if (decor == null || (decor.isAttachedToWindow() &&
140 (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) {
George Mount82ffc142014-06-30 16:47:34 -0700141 viewsReady(sharedElements);
142 } else {
George Mountd7d0fb32016-01-04 14:56:08 -0800143 mViewsReadyListener = new ViewTreeObserver.OnPreDrawListener() {
144 @Override
145 public boolean onPreDraw() {
146 mViewsReadyListener = null;
147 decor.getViewTreeObserver().removeOnPreDrawListener(this);
148 viewsReady(sharedElements);
149 return true;
150 }
151 };
152 decor.getViewTreeObserver().addOnPreDrawListener(mViewsReadyListener);
George Mount82ffc142014-06-30 16:47:34 -0700153 }
154 }
155
George Mount1fecfb22014-06-18 14:55:55 -0700156 private ArrayMap<String, View> mapNamedElements(ArrayList<String> accepted,
157 ArrayList<String> localNames) {
158 ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
George Mount48bd13c2014-09-12 10:54:54 -0700159 ViewGroup decorView = getDecor();
160 if (decorView != null) {
161 decorView.findNamedViews(sharedElements);
162 }
George Mount1fecfb22014-06-18 14:55:55 -0700163 if (accepted != null) {
164 for (int i = 0; i < localNames.size(); i++) {
165 String localName = localNames.get(i);
166 String acceptedName = accepted.get(i);
167 if (localName != null && !localName.equals(acceptedName)) {
168 View view = sharedElements.remove(localName);
169 if (view != null) {
170 sharedElements.put(acceptedName, view);
171 }
172 }
173 }
174 }
175 return sharedElements;
176 }
177
George Mountd265bd72014-06-02 07:22:59 -0700178 private void sendSharedElementDestination() {
George Mountfe361d22014-07-08 17:25:25 -0700179 boolean allReady;
George Mount48bd13c2014-09-12 10:54:54 -0700180 final View decorView = getDecor();
Dake Gu872efe42014-08-29 15:24:58 -0700181 if (allowOverlappingTransitions() && getEnterViewsTransition() != null) {
George Mountfe361d22014-07-08 17:25:25 -0700182 allReady = false;
George Mount48bd13c2014-09-12 10:54:54 -0700183 } else if (decorView == null) {
184 allReady = true;
George Mountfe361d22014-07-08 17:25:25 -0700185 } else {
George Mount48bd13c2014-09-12 10:54:54 -0700186 allReady = !decorView.isLayoutRequested();
George Mountfe361d22014-07-08 17:25:25 -0700187 if (allReady) {
188 for (int i = 0; i < mSharedElements.size(); i++) {
189 if (mSharedElements.get(i).isLayoutRequested()) {
190 allReady = false;
191 break;
192 }
George Mount67d92432014-06-06 13:34:20 -0700193 }
194 }
195 }
196 if (allReady) {
George Mountc93ca162014-05-23 19:21:36 -0700197 Bundle state = captureSharedElementState();
George Mountfe361d22014-07-08 17:25:25 -0700198 moveSharedElementsToOverlay();
George Mountc93ca162014-05-23 19:21:36 -0700199 mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
George Mount48bd13c2014-09-12 10:54:54 -0700200 } else if (decorView != null) {
George Mount6e7fb602014-09-04 16:20:20 -0700201 decorView.getViewTreeObserver()
George Mountd265bd72014-06-02 07:22:59 -0700202 .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
203 @Override
204 public boolean onPreDraw() {
George Mount6e7fb602014-09-04 16:20:20 -0700205 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
George Mountf31d83c2014-08-29 13:32:20 -0700206 if (mResultReceiver != null) {
207 Bundle state = captureSharedElementState();
George Mountf31d83c2014-08-29 13:32:20 -0700208 moveSharedElementsToOverlay();
209 mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
210 }
George Mountd265bd72014-06-02 07:22:59 -0700211 return true;
212 }
213 });
George Mount31a21722014-03-24 17:44:36 -0700214 }
George Mount13ccb792014-06-06 17:02:20 -0700215 if (allowOverlappingTransitions()) {
216 startEnterTransitionOnly();
217 }
George Mount31a21722014-03-24 17:44:36 -0700218 }
219
George Mount65580562014-08-29 08:15:48 -0700220 private static SharedElementCallback getListener(Activity activity, boolean isReturning) {
George Mount800d72b2014-05-19 07:09:00 -0700221 return isReturning ? activity.mExitTransitionListener : activity.mEnterTransitionListener;
222 }
223
George Mount31a21722014-03-24 17:44:36 -0700224 @Override
George Mount62ab9b72014-05-02 13:51:17 -0700225 protected void onReceiveResult(int resultCode, Bundle resultData) {
226 switch (resultCode) {
227 case MSG_TAKE_SHARED_ELEMENTS:
228 if (!mIsCanceled) {
George Mount8c2614c2014-06-10 11:12:01 -0700229 mSharedElementsBundle = resultData;
230 onTakeSharedElements();
George Mount62ab9b72014-05-02 13:51:17 -0700231 }
232 break;
233 case MSG_EXIT_TRANSITION_COMPLETE:
234 if (!mIsCanceled) {
George Mountc93ca162014-05-23 19:21:36 -0700235 mIsExitTransitionComplete = true;
236 if (mSharedElementTransitionStarted) {
George Mount62ab9b72014-05-02 13:51:17 -0700237 onRemoteExitTransitionComplete();
238 }
239 }
240 break;
241 case MSG_CANCEL:
242 cancel();
243 break;
244 }
George Mount31a21722014-03-24 17:44:36 -0700245 }
246
George Mountd7d0fb32016-01-04 14:56:08 -0800247 /**
248 * This is called onResume. If an Activity is resuming and the transitions
249 * haven't started yet, force the views to appear. This is likely to be
250 * caused by the top Activity finishing before the transitions started.
251 * In that case, we can finish any transition that was started, but we
252 * should cancel any pending transition and just bring those Views visible.
253 */
254 public void forceViewsToAppear() {
255 if (!mIsReturning) {
256 return;
257 }
258 if (!mIsReadyForTransition) {
259 mIsReadyForTransition = true;
260 final ViewGroup decor = getDecor();
261 if (decor != null && mViewsReadyListener != null) {
262 decor.getViewTreeObserver().removeOnPreDrawListener(mViewsReadyListener);
263 mViewsReadyListener = null;
264 }
265 showViews(mTransitioningViews, true);
George Mount62976722016-02-04 16:45:53 -0800266 setTransitioningViewsVisiblity(View.VISIBLE, true);
George Mountd7d0fb32016-01-04 14:56:08 -0800267 mSharedElements.clear();
268 mAllSharedElementNames.clear();
269 mTransitioningViews.clear();
270 mIsReadyForTransition = true;
271 viewsTransitionComplete();
272 sharedElementTransitionComplete();
273 } else {
274 if (!mSharedElementTransitionStarted) {
275 moveSharedElementsFromOverlay();
276 mSharedElementTransitionStarted = true;
277 showViews(mSharedElements, true);
278 mSharedElements.clear();
279 sharedElementTransitionComplete();
280 }
281 if (!mIsViewsTransitionStarted) {
282 mIsViewsTransitionStarted = true;
283 showViews(mTransitioningViews, true);
George Mount62976722016-02-04 16:45:53 -0800284 setTransitioningViewsVisiblity(View.VISIBLE, true);
George Mountd7d0fb32016-01-04 14:56:08 -0800285 mTransitioningViews.clear();
286 viewsTransitionComplete();
287 }
288 cancelPendingTransitions();
289 }
290 mAreViewsReady = true;
291 }
292
George Mount62ab9b72014-05-02 13:51:17 -0700293 private void cancel() {
294 if (!mIsCanceled) {
295 mIsCanceled = true;
George Mount8d3cd2c2014-07-08 11:07:33 -0700296 if (getViewsTransition() == null || mIsViewsTransitionStarted) {
George Mountce2ee3d2014-09-09 15:04:31 -0700297 showViews(mSharedElements, true);
George Mountb694e082014-09-12 07:34:52 -0700298 } else if (mTransitioningViews != null) {
George Mount62ab9b72014-05-02 13:51:17 -0700299 mTransitioningViews.addAll(mSharedElements);
300 }
George Mounta228d7e2015-11-17 12:49:22 -0800301 moveSharedElementsFromOverlay();
George Mount62ab9b72014-05-02 13:51:17 -0700302 mSharedElementNames.clear();
303 mSharedElements.clear();
304 mAllSharedElementNames.clear();
George Mount8c2614c2014-06-10 11:12:01 -0700305 startSharedElementTransition(null);
George Mount62ab9b72014-05-02 13:51:17 -0700306 onRemoteExitTransitionComplete();
307 }
George Mount31a21722014-03-24 17:44:36 -0700308 }
309
George Mount62ab9b72014-05-02 13:51:17 -0700310 public boolean isReturning() {
311 return mIsReturning;
George Mount31a21722014-03-24 17:44:36 -0700312 }
313
George Mount62ab9b72014-05-02 13:51:17 -0700314 protected void prepareEnter() {
George Mountf1abef62014-09-22 13:58:21 -0700315 ViewGroup decorView = getDecor();
316 if (mActivity == null || decorView == null) {
317 return;
318 }
George Mount62ab9b72014-05-02 13:51:17 -0700319 mActivity.overridePendingTransition(0, 0);
320 if (!mIsReturning) {
George Mount3cc716c2014-06-12 16:35:35 -0700321 mWasOpaque = mActivity.convertToTranslucent(null, null);
George Mountf1abef62014-09-22 13:58:21 -0700322 Drawable background = decorView.getBackground();
George Mount62ab9b72014-05-02 13:51:17 -0700323 if (background != null) {
324 getWindow().setBackgroundDrawable(null);
325 background = background.mutate();
326 background.setAlpha(0);
327 getWindow().setBackgroundDrawable(background);
328 }
329 } else {
330 mActivity = null; // all done with it now.
331 }
332 }
333
George Mounta712e8c2014-05-20 15:10:20 -0700334 @Override
335 protected Transition getViewsTransition() {
George Mountf1abef62014-09-22 13:58:21 -0700336 Window window = getWindow();
337 if (window == null) {
338 return null;
339 }
George Mounta712e8c2014-05-20 15:10:20 -0700340 if (mIsReturning) {
George Mountf1abef62014-09-22 13:58:21 -0700341 return window.getReenterTransition();
George Mounta712e8c2014-05-20 15:10:20 -0700342 } else {
George Mountf1abef62014-09-22 13:58:21 -0700343 return window.getEnterTransition();
George Mounta712e8c2014-05-20 15:10:20 -0700344 }
345 }
346
347 protected Transition getSharedElementTransition() {
George Mountf1abef62014-09-22 13:58:21 -0700348 Window window = getWindow();
349 if (window == null) {
350 return null;
351 }
George Mounta712e8c2014-05-20 15:10:20 -0700352 if (mIsReturning) {
George Mountf1abef62014-09-22 13:58:21 -0700353 return window.getSharedElementReenterTransition();
George Mounta712e8c2014-05-20 15:10:20 -0700354 } else {
George Mountf1abef62014-09-22 13:58:21 -0700355 return window.getSharedElementEnterTransition();
George Mounta712e8c2014-05-20 15:10:20 -0700356 }
357 }
358
George Mount8c2614c2014-06-10 11:12:01 -0700359 private void startSharedElementTransition(Bundle sharedElementState) {
George Mount48bd13c2014-09-12 10:54:54 -0700360 ViewGroup decorView = getDecor();
361 if (decorView == null) {
362 return;
363 }
George Mount62ab9b72014-05-02 13:51:17 -0700364 // Remove rejected shared elements
365 ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
366 rejectedNames.removeAll(mSharedElementNames);
367 ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames);
George Mount1732f522014-09-17 16:59:36 -0700368 if (mListener != null) {
369 mListener.onRejectSharedElements(rejectedSnapshots);
370 }
George Mount83c692e2014-10-23 11:11:17 -0700371 removeNullViews(rejectedSnapshots);
George Mount62ab9b72014-05-02 13:51:17 -0700372 startRejectedAnimations(rejectedSnapshots);
373
374 // Now start shared element transition
375 ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
376 mSharedElementNames);
George Mountce2ee3d2014-09-09 15:04:31 -0700377 showViews(mSharedElements, true);
George Mountfe361d22014-07-08 17:25:25 -0700378 scheduleSetSharedElementEnd(sharedElementSnapshots);
Dake Guc18f4cc2014-07-11 17:48:37 -0700379 ArrayList<SharedElementOriginalState> originalImageViewState =
George Mount62ab9b72014-05-02 13:51:17 -0700380 setSharedElementState(sharedElementState, sharedElementSnapshots);
George Mountc93ca162014-05-23 19:21:36 -0700381 requestLayoutForSharedElements();
George Mount62ab9b72014-05-02 13:51:17 -0700382
George Mount13ccb792014-06-06 17:02:20 -0700383 boolean startEnterTransition = allowOverlappingTransitions() && !mIsReturning;
George Mount62ab9b72014-05-02 13:51:17 -0700384 boolean startSharedElementTransition = true;
George Mountfe361d22014-07-08 17:25:25 -0700385 setGhostVisibility(View.INVISIBLE);
386 scheduleGhostVisibilityChange(View.INVISIBLE);
George Mount80141d12015-07-14 10:03:06 -0700387 pauseInput();
George Mount48bd13c2014-09-12 10:54:54 -0700388 Transition transition = beginTransition(decorView, startEnterTransition,
389 startSharedElementTransition);
George Mountfe361d22014-07-08 17:25:25 -0700390 scheduleGhostVisibilityChange(View.VISIBLE);
391 setGhostVisibility(View.VISIBLE);
George Mount62ab9b72014-05-02 13:51:17 -0700392
393 if (startEnterTransition) {
394 startEnterTransition(transition);
395 }
396
Dake Guc18f4cc2014-07-11 17:48:37 -0700397 setOriginalSharedElementState(mSharedElements, originalImageViewState);
George Mount62ab9b72014-05-02 13:51:17 -0700398
399 if (mResultReceiver != null) {
George Mounte678ab62014-06-30 15:28:28 -0700400 // We can't trust that the view will disappear on the same frame that the shared
401 // element appears here. Assure that we get at least 2 frames for double-buffering.
George Mount48bd13c2014-09-12 10:54:54 -0700402 decorView.postOnAnimation(new Runnable() {
George Mounte678ab62014-06-30 15:28:28 -0700403 int mAnimations;
George Mount48bd13c2014-09-12 10:54:54 -0700404
George Mounte678ab62014-06-30 15:28:28 -0700405 @Override
406 public void run() {
407 if (mAnimations++ < MIN_ANIMATION_FRAMES) {
George Mount4188d0d2014-09-11 17:47:11 -0700408 View decorView = getDecor();
409 if (decorView != null) {
410 decorView.postOnAnimation(this);
411 }
George Mount407d1fa2014-08-28 16:11:49 -0700412 } else if (mResultReceiver != null) {
George Mounte678ab62014-06-30 15:28:28 -0700413 mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
414 mResultReceiver = null; // all done sending messages.
415 }
416 }
417 });
George Mount62ab9b72014-05-02 13:51:17 -0700418 }
George Mount62ab9b72014-05-02 13:51:17 -0700419 }
420
George Mount83c692e2014-10-23 11:11:17 -0700421 private static void removeNullViews(ArrayList<View> views) {
422 if (views != null) {
423 for (int i = views.size() - 1; i >= 0; i--) {
424 if (views.get(i) == null) {
425 views.remove(i);
426 }
427 }
428 }
429 }
430
George Mount13ccb792014-06-06 17:02:20 -0700431 private void onTakeSharedElements() {
432 if (!mIsReadyForTransition || mSharedElementsBundle == null) {
433 return;
434 }
435 final Bundle sharedElementState = mSharedElementsBundle;
436 mSharedElementsBundle = null;
George Mount4dc668c2015-04-09 20:55:59 +0000437 OnSharedElementsReadyListener listener = new OnSharedElementsReadyListener() {
438 @Override
439 public void onSharedElementsReady() {
440 final View decorView = getDecor();
441 if (decorView != null) {
442 decorView.getViewTreeObserver()
443 .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
George Mount48bd13c2014-09-12 10:54:54 -0700444 @Override
George Mount4dc668c2015-04-09 20:55:59 +0000445 public boolean onPreDraw() {
446 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
447 startTransition(new Runnable() {
448 @Override
449 public void run() {
450 startSharedElementTransition(sharedElementState);
451 }
452 });
453 return false;
George Mount48bd13c2014-09-12 10:54:54 -0700454 }
455 });
George Mount4dc668c2015-04-09 20:55:59 +0000456 decorView.invalidate();
457 }
458 }
459 };
460 if (mListener == null) {
461 listener.onSharedElementsReady();
462 } else {
463 mListener.onSharedElementsArrived(mSharedElementNames, mSharedElements, listener);
George Mount48bd13c2014-09-12 10:54:54 -0700464 }
George Mount13ccb792014-06-06 17:02:20 -0700465 }
466
George Mountc93ca162014-05-23 19:21:36 -0700467 private void requestLayoutForSharedElements() {
468 int numSharedElements = mSharedElements.size();
469 for (int i = 0; i < numSharedElements; i++) {
470 mSharedElements.get(i).requestLayout();
471 }
472 }
473
George Mount48bd13c2014-09-12 10:54:54 -0700474 private Transition beginTransition(ViewGroup decorView, boolean startEnterTransition,
George Mount62ab9b72014-05-02 13:51:17 -0700475 boolean startSharedElementTransition) {
476 Transition sharedElementTransition = null;
George Mountc93ca162014-05-23 19:21:36 -0700477 if (startSharedElementTransition) {
George Mountfe361d22014-07-08 17:25:25 -0700478 if (!mSharedElementNames.isEmpty()) {
479 sharedElementTransition = configureTransition(getSharedElementTransition(), false);
480 }
481 if (sharedElementTransition == null) {
George Mountc93ca162014-05-23 19:21:36 -0700482 sharedElementTransitionStarted();
George Mountfe361d22014-07-08 17:25:25 -0700483 sharedElementTransitionComplete();
George Mountc93ca162014-05-23 19:21:36 -0700484 } else {
George Mountfe361d22014-07-08 17:25:25 -0700485 sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
George Mountc93ca162014-05-23 19:21:36 -0700486 @Override
487 public void onTransitionStart(Transition transition) {
George Mountc93ca162014-05-23 19:21:36 -0700488 sharedElementTransitionStarted();
489 }
George Mountfe361d22014-07-08 17:25:25 -0700490
491 @Override
492 public void onTransitionEnd(Transition transition) {
493 transition.removeListener(this);
494 sharedElementTransitionComplete();
495 }
George Mountc93ca162014-05-23 19:21:36 -0700496 });
497 }
498 }
George Mountfe361d22014-07-08 17:25:25 -0700499 Transition viewsTransition = null;
500 if (startEnterTransition) {
501 mIsViewsTransitionStarted = true;
George Mountb694e082014-09-12 07:34:52 -0700502 if (mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
George Mountfe361d22014-07-08 17:25:25 -0700503 viewsTransition = configureTransition(getViewsTransition(), true);
504 if (viewsTransition != null && !mIsReturning) {
505 stripOffscreenViews();
506 }
George Mount13ccb792014-06-06 17:02:20 -0700507 }
George Mountfe361d22014-07-08 17:25:25 -0700508 if (viewsTransition == null) {
George Mount41725de2015-04-09 08:23:05 -0700509 viewsTransitionComplete();
George Mountfe361d22014-07-08 17:25:25 -0700510 } else {
George Mountb694e082014-09-12 07:34:52 -0700511 final ArrayList<View> transitioningViews = mTransitioningViews;
George Mountfe361d22014-07-08 17:25:25 -0700512 viewsTransition.addListener(new ContinueTransitionListener() {
513 @Override
George Mounta2bbbb32014-08-12 10:16:20 -0700514 public void onTransitionStart(Transition transition) {
515 mEnterViewsTransition = transition;
George Mountb694e082014-09-12 07:34:52 -0700516 if (transitioningViews != null) {
517 showViews(transitioningViews, false);
518 }
George Mounta2bbbb32014-08-12 10:16:20 -0700519 super.onTransitionStart(transition);
520 }
521
522 @Override
George Mountfe361d22014-07-08 17:25:25 -0700523 public void onTransitionEnd(Transition transition) {
George Mounta2bbbb32014-08-12 10:16:20 -0700524 mEnterViewsTransition = null;
George Mountfe361d22014-07-08 17:25:25 -0700525 transition.removeListener(this);
George Mount41725de2015-04-09 08:23:05 -0700526 viewsTransitionComplete();
George Mountfe361d22014-07-08 17:25:25 -0700527 super.onTransitionEnd(transition);
528 }
529 });
530 }
531 }
532
533 Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
534 if (transition != null) {
535 transition.addListener(new ContinueTransitionListener());
George Mount62976722016-02-04 16:45:53 -0800536 if (startEnterTransition) {
537 setTransitioningViewsVisiblity(View.INVISIBLE, false);
538 }
George Mount48bd13c2014-09-12 10:54:54 -0700539 TransitionManager.beginDelayedTransition(decorView, transition);
George Mount62ab9b72014-05-02 13:51:17 -0700540 if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
541 mSharedElements.get(0).invalidate();
George Mount62976722016-02-04 16:45:53 -0800542 }
543 if (startEnterTransition) {
544 setTransitioningViewsVisiblity(View.VISIBLE, true);
George Mount62ab9b72014-05-02 13:51:17 -0700545 }
George Mount13ccb792014-06-06 17:02:20 -0700546 } else {
547 transitionStarted();
George Mount62ab9b72014-05-02 13:51:17 -0700548 }
549 return transition;
550 }
551
George Mount41725de2015-04-09 08:23:05 -0700552 @Override
553 protected void onTransitionsComplete() {
554 moveSharedElementsFromOverlay();
George Mount10d312b2015-12-16 14:00:35 -0800555 final ViewGroup decorView = getDecor();
556 if (decorView != null) {
557 decorView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
558 }
George Mountfe361d22014-07-08 17:25:25 -0700559 }
560
George Mountc93ca162014-05-23 19:21:36 -0700561 private void sharedElementTransitionStarted() {
562 mSharedElementTransitionStarted = true;
563 if (mIsExitTransitionComplete) {
564 send(MSG_EXIT_TRANSITION_COMPLETE, null);
565 }
566 }
567
George Mount62ab9b72014-05-02 13:51:17 -0700568 private void startEnterTransition(Transition transition) {
George Mount48bd13c2014-09-12 10:54:54 -0700569 ViewGroup decorView = getDecor();
570 if (!mIsReturning && decorView != null) {
571 Drawable background = decorView.getBackground();
George Mount62ab9b72014-05-02 13:51:17 -0700572 if (background != null) {
573 background = background.mutate();
George Mount99c82fd2014-09-03 07:27:47 -0700574 getWindow().setBackgroundDrawable(background);
George Mount8cab50a2014-05-15 09:57:17 -0700575 mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 255);
George Mounted1e01d2014-06-05 13:49:12 -0700576 mBackgroundAnimator.setDuration(getFadeDuration());
George Mount8cab50a2014-05-15 09:57:17 -0700577 mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
George Mount62ab9b72014-05-02 13:51:17 -0700578 @Override
579 public void onAnimationEnd(Animator animation) {
580 makeOpaque();
581 }
582 });
George Mount8cab50a2014-05-15 09:57:17 -0700583 mBackgroundAnimator.start();
George Mount62ab9b72014-05-02 13:51:17 -0700584 } else if (transition != null) {
585 transition.addListener(new Transition.TransitionListenerAdapter() {
586 @Override
587 public void onTransitionEnd(Transition transition) {
588 transition.removeListener(this);
589 makeOpaque();
590 }
591 });
592 } else {
593 makeOpaque();
594 }
595 }
596 }
597
598 public void stop() {
George Mountf1abef62014-09-22 13:58:21 -0700599 // Restore the background to its previous state since the
600 // Activity is stopping.
George Mount700db2a2014-07-07 17:17:49 -0700601 if (mBackgroundAnimator != null) {
602 mBackgroundAnimator.end();
603 mBackgroundAnimator = null;
George Mountf1abef62014-09-22 13:58:21 -0700604 } else if (mWasOpaque) {
605 ViewGroup decorView = getDecor();
606 if (decorView != null) {
607 Drawable drawable = decorView.getBackground();
608 if (drawable != null) {
609 drawable.setAlpha(1);
610 }
611 }
George Mount700db2a2014-07-07 17:17:49 -0700612 }
George Mountf1abef62014-09-22 13:58:21 -0700613 makeOpaque();
614 mIsCanceled = true;
615 mResultReceiver = null;
George Mount700db2a2014-07-07 17:17:49 -0700616 mActivity = null;
George Mountfe361d22014-07-08 17:25:25 -0700617 moveSharedElementsFromOverlay();
George Mountf1abef62014-09-22 13:58:21 -0700618 if (mTransitioningViews != null) {
619 showViews(mTransitioningViews, true);
George Mount62976722016-02-04 16:45:53 -0800620 setTransitioningViewsVisiblity(View.VISIBLE, true);
George Mountf1abef62014-09-22 13:58:21 -0700621 }
622 showViews(mSharedElements, true);
George Mount700db2a2014-07-07 17:17:49 -0700623 clearState();
624 }
625
George Mountfbd45962015-01-26 14:38:19 -0800626 /**
627 * Cancels the enter transition.
628 * @return True if the enter transition is still pending capturing the target state. If so,
629 * any transition started on the decor will do nothing.
630 */
631 public boolean cancelEnter() {
George Mountfe361d22014-07-08 17:25:25 -0700632 setGhostVisibility(View.INVISIBLE);
George Mount62ab9b72014-05-02 13:51:17 -0700633 mHasStopped = true;
George Mount62ab9b72014-05-02 13:51:17 -0700634 mIsCanceled = true;
George Mount3bbc2ed2015-11-18 11:10:55 -0800635 clearState();
636 return super.cancelPendingTransitions();
637 }
638
639 @Override
640 protected void clearState() {
641 mSharedElementsBundle = null;
642 mEnterViewsTransition = null;
George Mount62ab9b72014-05-02 13:51:17 -0700643 mResultReceiver = null;
George Mount8cab50a2014-05-15 09:57:17 -0700644 if (mBackgroundAnimator != null) {
645 mBackgroundAnimator.cancel();
646 mBackgroundAnimator = null;
647 }
George Mount3bbc2ed2015-11-18 11:10:55 -0800648 super.clearState();
George Mount62ab9b72014-05-02 13:51:17 -0700649 }
650
651 private void makeOpaque() {
George Mount8c2614c2014-06-10 11:12:01 -0700652 if (!mHasStopped && mActivity != null) {
George Mount3cc716c2014-06-12 16:35:35 -0700653 if (mWasOpaque) {
654 mActivity.convertFromTranslucent();
655 }
George Mount62ab9b72014-05-02 13:51:17 -0700656 mActivity = null;
657 }
658 }
659
660 private boolean allowOverlappingTransitions() {
George Mount5aec62e2015-05-08 14:21:20 -0700661 return mIsReturning ? getWindow().getAllowReturnTransitionOverlap()
George Mount62ab9b72014-05-02 13:51:17 -0700662 : getWindow().getAllowEnterTransitionOverlap();
663 }
664
665 private void startRejectedAnimations(final ArrayList<View> rejectedSnapshots) {
666 if (rejectedSnapshots == null || rejectedSnapshots.isEmpty()) {
667 return;
668 }
George Mount48bd13c2014-09-12 10:54:54 -0700669 final ViewGroup decorView = getDecor();
670 if (decorView != null) {
671 ViewGroupOverlay overlay = decorView.getOverlay();
672 ObjectAnimator animator = null;
673 int numRejected = rejectedSnapshots.size();
674 for (int i = 0; i < numRejected; i++) {
675 View snapshot = rejectedSnapshots.get(i);
676 overlay.add(snapshot);
677 animator = ObjectAnimator.ofFloat(snapshot, View.ALPHA, 1, 0);
678 animator.start();
George Mount62ab9b72014-05-02 13:51:17 -0700679 }
George Mount48bd13c2014-09-12 10:54:54 -0700680 animator.addListener(new AnimatorListenerAdapter() {
681 @Override
682 public void onAnimationEnd(Animator animation) {
683 ViewGroupOverlay overlay = decorView.getOverlay();
684 int numRejected = rejectedSnapshots.size();
685 for (int i = 0; i < numRejected; i++) {
686 overlay.remove(rejectedSnapshots.get(i));
687 }
688 }
689 });
690 }
George Mount62ab9b72014-05-02 13:51:17 -0700691 }
692
693 protected void onRemoteExitTransitionComplete() {
694 if (!allowOverlappingTransitions()) {
George Mount13ccb792014-06-06 17:02:20 -0700695 startEnterTransitionOnly();
George Mount62ab9b72014-05-02 13:51:17 -0700696 }
697 }
George Mount13ccb792014-06-06 17:02:20 -0700698
699 private void startEnterTransitionOnly() {
700 startTransition(new Runnable() {
701 @Override
702 public void run() {
George Mount13ccb792014-06-06 17:02:20 -0700703 boolean startEnterTransition = true;
704 boolean startSharedElementTransition = false;
George Mount48bd13c2014-09-12 10:54:54 -0700705 ViewGroup decorView = getDecor();
706 if (decorView != null) {
707 Transition transition = beginTransition(decorView, startEnterTransition,
708 startSharedElementTransition);
709 startEnterTransition(transition);
710 }
George Mount13ccb792014-06-06 17:02:20 -0700711 }
712 });
713 }
George Mount31a21722014-03-24 17:44:36 -0700714}