blob: a71d6495045e1f6b1269eca36d5b4e0ed21468b6 [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
George Mount62ab9b72014-05-02 13:51:17 -070018import android.animation.Animator;
19import android.animation.AnimatorListenerAdapter;
20import android.animation.ObjectAnimator;
21import android.content.Intent;
George Mount62ab9b72014-05-02 13:51:17 -070022import android.graphics.drawable.ColorDrawable;
George Mount8cab50a2014-05-15 09:57:17 -070023import android.graphics.drawable.Drawable;
George Mount31a21722014-03-24 17:44:36 -070024import android.os.Bundle;
George Mount62ab9b72014-05-02 13:51:17 -070025import android.os.Handler;
26import android.os.Message;
George Mount31a21722014-03-24 17:44:36 -070027import android.transition.Transition;
George Mount62ab9b72014-05-02 13:51:17 -070028import android.transition.TransitionManager;
George Mount31a21722014-03-24 17:44:36 -070029import android.view.View;
George Mountc93ca162014-05-23 19:21:36 -070030import android.view.ViewTreeObserver;
George Mount31a21722014-03-24 17:44:36 -070031
32import java.util.ArrayList;
33
34/**
35 * This ActivityTransitionCoordinator is created in ActivityOptions#makeSceneTransitionAnimation
36 * to govern the exit of the Scene and the shared elements when calling an Activity as well as
37 * the reentry of the Scene when coming back from the called Activity.
38 */
39class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
40 private static final String TAG = "ExitTransitionCoordinator";
George Mount8cab50a2014-05-15 09:57:17 -070041 private static final long MAX_WAIT_MS = 1000;
George Mount31a21722014-03-24 17:44:36 -070042
George Mount31a21722014-03-24 17:44:36 -070043 private boolean mExitComplete;
44
George Mount62ab9b72014-05-02 13:51:17 -070045 private Bundle mSharedElementBundle;
George Mount31a21722014-03-24 17:44:36 -070046
George Mount62ab9b72014-05-02 13:51:17 -070047 private boolean mExitNotified;
George Mount31a21722014-03-24 17:44:36 -070048
George Mount62ab9b72014-05-02 13:51:17 -070049 private boolean mSharedElementNotified;
50
51 private Activity mActivity;
52
53 private boolean mIsBackgroundReady;
54
George Mount62ab9b72014-05-02 13:51:17 -070055 private boolean mIsCanceled;
56
57 private Handler mHandler;
58
George Mount8cab50a2014-05-15 09:57:17 -070059 private ObjectAnimator mBackgroundAnimator;
60
61 private boolean mIsHidden;
62
George Mountc93ca162014-05-23 19:21:36 -070063 private boolean mExitTransitionStarted;
64
65 private Bundle mExitSharedElementBundle;
66
George Mount62ab9b72014-05-02 13:51:17 -070067 public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
68 ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
George Mountc9b6df82014-05-21 15:07:04 -070069 super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
70 isReturning);
George Mounta712e8c2014-05-20 15:10:20 -070071 mIsBackgroundReady = !isReturning;
George Mount62ab9b72014-05-02 13:51:17 -070072 mActivity = activity;
George Mount31a21722014-03-24 17:44:36 -070073 }
74
George Mount800d72b2014-05-19 07:09:00 -070075 private static SharedElementListener getListener(Activity activity, boolean isReturning) {
George Mountd80154f2014-05-21 08:40:03 -070076 return isReturning ? activity.mEnterTransitionListener : activity.mExitTransitionListener;
George Mount800d72b2014-05-19 07:09:00 -070077 }
78
George Mount31a21722014-03-24 17:44:36 -070079 @Override
George Mount62ab9b72014-05-02 13:51:17 -070080 protected void onReceiveResult(int resultCode, Bundle resultData) {
81 switch (resultCode) {
82 case MSG_SET_REMOTE_RECEIVER:
83 mResultReceiver = resultData.getParcelable(KEY_REMOTE_RECEIVER);
84 if (mIsCanceled) {
85 mResultReceiver.send(MSG_CANCEL, null);
86 mResultReceiver = null;
87 } else {
88 if (mHandler != null) {
89 mHandler.removeMessages(MSG_CANCEL);
90 }
91 notifyComplete();
92 }
93 break;
94 case MSG_HIDE_SHARED_ELEMENTS:
95 if (!mIsCanceled) {
96 hideSharedElements();
97 }
98 break;
99 case MSG_START_EXIT_TRANSITION:
100 startExit();
101 break;
102 case MSG_ACTIVITY_STOPPED:
103 setViewVisibility(mTransitioningViews, View.VISIBLE);
104 setViewVisibility(mSharedElements, View.VISIBLE);
George Mount8cab50a2014-05-15 09:57:17 -0700105 mIsHidden = true;
George Mount62ab9b72014-05-02 13:51:17 -0700106 break;
George Mountc93ca162014-05-23 19:21:36 -0700107 case MSG_SHARED_ELEMENT_DESTINATION:
108 mExitSharedElementBundle = resultData;
109 if (mExitTransitionStarted) {
110 startSharedElementExit();
111 }
112 break;
113 }
114 }
115
116 private void startSharedElementExit() {
117 if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) {
118 Transition transition = getSharedElementExitTransition();
119 TransitionManager.beginDelayedTransition(getDecor(), transition);
120 ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
121 mSharedElementNames);
122 setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
George Mount31a21722014-03-24 17:44:36 -0700123 }
124 }
125
George Mount62ab9b72014-05-02 13:51:17 -0700126 private void hideSharedElements() {
127 setViewVisibility(mSharedElements, View.INVISIBLE);
George Mountc93ca162014-05-23 19:21:36 -0700128 finishIfNecessary();
George Mount62ab9b72014-05-02 13:51:17 -0700129 }
130
George Mount31a21722014-03-24 17:44:36 -0700131 public void startExit() {
George Mountc93ca162014-05-23 19:21:36 -0700132 beginTransitions();
George Mount62ab9b72014-05-02 13:51:17 -0700133 setViewVisibility(mTransitioningViews, View.INVISIBLE);
134 }
135
136 public void startExit(int resultCode, Intent data) {
137 mHandler = new Handler() {
138 @Override
139 public void handleMessage(Message msg) {
140 mIsCanceled = true;
141 mActivity.finish();
142 mActivity = null;
143 }
144 };
145 mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
146 if (getDecor().getBackground() == null) {
147 ColorDrawable black = new ColorDrawable(0xFF000000);
148 black.setAlpha(0);
149 getWindow().setBackgroundDrawable(black);
150 black.setAlpha(255);
George Mount31a21722014-03-24 17:44:36 -0700151 }
George Mount62ab9b72014-05-02 13:51:17 -0700152 ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
153 mAllSharedElementNames, resultCode, data);
154 mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
155 @Override
156 public void onTranslucentConversionComplete(boolean drawComplete) {
157 if (!mIsCanceled) {
158 fadeOutBackground();
159 }
160 }
161 }, options);
George Mountc93ca162014-05-23 19:21:36 -0700162 Transition sharedElementTransition = mSharedElements.isEmpty()
163 ? null : getSharedElementTransition();
164 if (sharedElementTransition == null) {
165 sharedElementTransitionComplete();
166 }
167 Transition transition = mergeTransitions(sharedElementTransition, getExitTransition());
168 if (transition == null) {
169 mExitTransitionStarted = true;
170 } else {
171 TransitionManager.beginDelayedTransition(getDecor(), transition);
172 setViewVisibility(mTransitioningViews, View.INVISIBLE);
173 getDecor().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
174 @Override
175 public boolean onPreDraw() {
176 getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
177 mExitTransitionStarted = true;
178 if (mExitSharedElementBundle != null) {
179 startSharedElementExit();
180 }
181 notifyComplete();
182 return true;
183 }
184 });
185 }
George Mount62ab9b72014-05-02 13:51:17 -0700186 }
187
188 private void fadeOutBackground() {
George Mount8cab50a2014-05-15 09:57:17 -0700189 if (mBackgroundAnimator == null) {
190 Drawable background = getDecor().getBackground();
191 mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 0);
192 mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
193 @Override
194 public void onAnimationEnd(Animator animation) {
195 mBackgroundAnimator = null;
196 if (!mIsCanceled) {
197 mIsBackgroundReady = true;
198 notifyComplete();
199 }
200 }
201 });
202 mBackgroundAnimator.setDuration(FADE_BACKGROUND_DURATION_MS);
203 mBackgroundAnimator.start();
204 }
George Mount62ab9b72014-05-02 13:51:17 -0700205 }
206
George Mountc93ca162014-05-23 19:21:36 -0700207 private Transition getExitTransition() {
208 Transition viewsTransition = null;
209 if (!mTransitioningViews.isEmpty()) {
210 viewsTransition = configureTransition(getViewsTransition());
George Mount62ab9b72014-05-02 13:51:17 -0700211 }
George Mountc93ca162014-05-23 19:21:36 -0700212 if (viewsTransition == null) {
George Mount62ab9b72014-05-02 13:51:17 -0700213 exitTransitionComplete();
214 } else {
215 viewsTransition.addListener(new Transition.TransitionListenerAdapter() {
216 @Override
217 public void onTransitionEnd(Transition transition) {
218 exitTransitionComplete();
George Mount8cab50a2014-05-15 09:57:17 -0700219 if (mIsHidden) {
220 setViewVisibility(mTransitioningViews, View.VISIBLE);
221 }
George Mount62ab9b72014-05-02 13:51:17 -0700222 }
George Mountc93ca162014-05-23 19:21:36 -0700223
224 @Override
225 public void onTransitionCancel(Transition transition) {
226 super.onTransitionCancel(transition);
227 }
George Mount62ab9b72014-05-02 13:51:17 -0700228 });
229 }
George Mountc93ca162014-05-23 19:21:36 -0700230 return viewsTransition;
231 }
232
233 private Transition getSharedElementExitTransition() {
234 Transition sharedElementTransition = null;
235 if (!mSharedElements.isEmpty()) {
236 sharedElementTransition = configureTransition(getSharedElementTransition());
237 }
238 if (sharedElementTransition == null) {
239 sharedElementTransitionComplete();
240 } else {
241 sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
242 @Override
243 public void onTransitionEnd(Transition transition) {
244 sharedElementTransitionComplete();
245 if (mIsHidden) {
246 setViewVisibility(mSharedElements, View.VISIBLE);
247 }
248 }
249 });
250 mSharedElements.get(0).invalidate();
251 }
252 return sharedElementTransition;
253 }
254
255 private void beginTransitions() {
256 Transition sharedElementTransition = getSharedElementExitTransition();
257 Transition viewsTransition = getExitTransition();
George Mount62ab9b72014-05-02 13:51:17 -0700258
259 Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
George Mountc93ca162014-05-23 19:21:36 -0700260 mExitTransitionStarted = true;
261 if (transition != null) {
262 TransitionManager.beginDelayedTransition(getDecor(), transition);
George Mounta712e8c2014-05-20 15:10:20 -0700263 }
George Mount62ab9b72014-05-02 13:51:17 -0700264 }
265
266 private void exitTransitionComplete() {
267 mExitComplete = true;
268 notifyComplete();
269 }
270
271 protected boolean isReadyToNotify() {
George Mountc93ca162014-05-23 19:21:36 -0700272 return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady
273 && mExitTransitionStarted;
George Mount62ab9b72014-05-02 13:51:17 -0700274 }
275
276 private void sharedElementTransitionComplete() {
George Mountc93ca162014-05-23 19:21:36 -0700277 mSharedElementBundle = captureSharedElementState();
George Mount62ab9b72014-05-02 13:51:17 -0700278 notifyComplete();
279 }
280
281 protected void notifyComplete() {
282 if (isReadyToNotify()) {
283 if (!mSharedElementNotified) {
284 mSharedElementNotified = true;
285 mResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, mSharedElementBundle);
286 }
287 if (!mExitNotified && mExitComplete) {
288 mExitNotified = true;
289 mResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null);
290 mResultReceiver = null; // done talking
George Mountc93ca162014-05-23 19:21:36 -0700291 finishIfNecessary();
George Mount62ab9b72014-05-02 13:51:17 -0700292 }
293 }
294 }
295
George Mountc93ca162014-05-23 19:21:36 -0700296 private void finishIfNecessary() {
297 if (mIsReturning && mExitNotified && (mSharedElements.isEmpty()
298 || mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
299 mActivity.finish();
300 mActivity.overridePendingTransition(0, 0);
301 mActivity = null;
302 }
303 if (!mIsReturning && mExitNotified) {
304 mActivity = null; // don't need it anymore
305 }
306 }
307
George Mounta712e8c2014-05-20 15:10:20 -0700308 @Override
309 protected Transition getViewsTransition() {
310 if (mIsReturning) {
311 return getWindow().getEnterTransition();
312 } else {
313 return getWindow().getExitTransition();
314 }
315 }
316
317 protected Transition getSharedElementTransition() {
318 if (mIsReturning) {
319 return getWindow().getSharedElementEnterTransition();
320 } else {
321 return getWindow().getSharedElementExitTransition();
322 }
323 }
George Mount31a21722014-03-24 17:44:36 -0700324}