George Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2014 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | package android.app; |
| 17 | |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 18 | import android.animation.Animator; |
| 19 | import android.animation.AnimatorListenerAdapter; |
| 20 | import android.animation.ObjectAnimator; |
| 21 | import android.content.Intent; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 22 | import android.graphics.drawable.ColorDrawable; |
George Mount | 8cab50a | 2014-05-15 09:57:17 -0700 | [diff] [blame] | 23 | import android.graphics.drawable.Drawable; |
George Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 24 | import android.os.Bundle; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 25 | import android.os.Handler; |
| 26 | import android.os.Message; |
George Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 27 | import android.transition.Transition; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 28 | import android.transition.TransitionManager; |
George Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 29 | import android.view.View; |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 30 | import android.view.ViewTreeObserver; |
George Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 31 | |
| 32 | import 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 | */ |
| 39 | class ExitTransitionCoordinator extends ActivityTransitionCoordinator { |
| 40 | private static final String TAG = "ExitTransitionCoordinator"; |
George Mount | 8cab50a | 2014-05-15 09:57:17 -0700 | [diff] [blame] | 41 | private static final long MAX_WAIT_MS = 1000; |
George Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 42 | |
George Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 43 | private boolean mExitComplete; |
| 44 | |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 45 | private Bundle mSharedElementBundle; |
George Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 46 | |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 47 | private boolean mExitNotified; |
George Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 48 | |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 49 | private boolean mSharedElementNotified; |
| 50 | |
| 51 | private Activity mActivity; |
| 52 | |
| 53 | private boolean mIsBackgroundReady; |
| 54 | |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 55 | private boolean mIsCanceled; |
| 56 | |
| 57 | private Handler mHandler; |
| 58 | |
George Mount | 8cab50a | 2014-05-15 09:57:17 -0700 | [diff] [blame] | 59 | private ObjectAnimator mBackgroundAnimator; |
| 60 | |
| 61 | private boolean mIsHidden; |
| 62 | |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 63 | private boolean mExitTransitionStarted; |
| 64 | |
| 65 | private Bundle mExitSharedElementBundle; |
| 66 | |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 67 | public ExitTransitionCoordinator(Activity activity, ArrayList<String> names, |
| 68 | ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) { |
George Mount | c9b6df8 | 2014-05-21 15:07:04 -0700 | [diff] [blame] | 69 | super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning), |
| 70 | isReturning); |
George Mount | a712e8c | 2014-05-20 15:10:20 -0700 | [diff] [blame] | 71 | mIsBackgroundReady = !isReturning; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 72 | mActivity = activity; |
George Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 73 | } |
| 74 | |
George Mount | 800d72b | 2014-05-19 07:09:00 -0700 | [diff] [blame] | 75 | private static SharedElementListener getListener(Activity activity, boolean isReturning) { |
George Mount | d80154f | 2014-05-21 08:40:03 -0700 | [diff] [blame] | 76 | return isReturning ? activity.mEnterTransitionListener : activity.mExitTransitionListener; |
George Mount | 800d72b | 2014-05-19 07:09:00 -0700 | [diff] [blame] | 77 | } |
| 78 | |
George Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 79 | @Override |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 80 | 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 Mount | 8cab50a | 2014-05-15 09:57:17 -0700 | [diff] [blame] | 105 | mIsHidden = true; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 106 | break; |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 107 | 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 Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 123 | } |
| 124 | } |
| 125 | |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 126 | private void hideSharedElements() { |
| 127 | setViewVisibility(mSharedElements, View.INVISIBLE); |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 128 | finishIfNecessary(); |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 129 | } |
| 130 | |
George Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 131 | public void startExit() { |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 132 | beginTransitions(); |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 133 | 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 Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 151 | } |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 152 | 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 Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 162 | 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 Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | private void fadeOutBackground() { |
George Mount | 8cab50a | 2014-05-15 09:57:17 -0700 | [diff] [blame] | 189 | 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 Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 205 | } |
| 206 | |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 207 | private Transition getExitTransition() { |
| 208 | Transition viewsTransition = null; |
| 209 | if (!mTransitioningViews.isEmpty()) { |
| 210 | viewsTransition = configureTransition(getViewsTransition()); |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 211 | } |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 212 | if (viewsTransition == null) { |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 213 | exitTransitionComplete(); |
| 214 | } else { |
| 215 | viewsTransition.addListener(new Transition.TransitionListenerAdapter() { |
| 216 | @Override |
| 217 | public void onTransitionEnd(Transition transition) { |
| 218 | exitTransitionComplete(); |
George Mount | 8cab50a | 2014-05-15 09:57:17 -0700 | [diff] [blame] | 219 | if (mIsHidden) { |
| 220 | setViewVisibility(mTransitioningViews, View.VISIBLE); |
| 221 | } |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 222 | } |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 223 | |
| 224 | @Override |
| 225 | public void onTransitionCancel(Transition transition) { |
| 226 | super.onTransitionCancel(transition); |
| 227 | } |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 228 | }); |
| 229 | } |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 230 | 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 Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 258 | |
| 259 | Transition transition = mergeTransitions(sharedElementTransition, viewsTransition); |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 260 | mExitTransitionStarted = true; |
| 261 | if (transition != null) { |
| 262 | TransitionManager.beginDelayedTransition(getDecor(), transition); |
George Mount | a712e8c | 2014-05-20 15:10:20 -0700 | [diff] [blame] | 263 | } |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 264 | } |
| 265 | |
| 266 | private void exitTransitionComplete() { |
| 267 | mExitComplete = true; |
| 268 | notifyComplete(); |
| 269 | } |
| 270 | |
| 271 | protected boolean isReadyToNotify() { |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 272 | return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady |
| 273 | && mExitTransitionStarted; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 274 | } |
| 275 | |
| 276 | private void sharedElementTransitionComplete() { |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 277 | mSharedElementBundle = captureSharedElementState(); |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 278 | 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 Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 291 | finishIfNecessary(); |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 292 | } |
| 293 | } |
| 294 | } |
| 295 | |
George Mount | c93ca16 | 2014-05-23 19:21:36 -0700 | [diff] [blame^] | 296 | 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 Mount | a712e8c | 2014-05-20 15:10:20 -0700 | [diff] [blame] | 308 | @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 Mount | 31a2172 | 2014-03-24 17:44:36 -0700 | [diff] [blame] | 324 | } |