George Mount | 62ab9b7 | 2014-05-02 13:51:17 -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 | |
| 18 | import android.os.Bundle; |
| 19 | import android.os.ResultReceiver; |
George Mount | a2bbbb3 | 2014-08-12 10:16:20 -0700 | [diff] [blame] | 20 | import android.transition.Transition; |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 21 | import android.util.SparseArray; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 22 | import android.view.View; |
George Mount | a2bbbb3 | 2014-08-12 10:16:20 -0700 | [diff] [blame] | 23 | import android.view.ViewGroup; |
George Mount | fbd4596 | 2015-01-26 14:38:19 -0800 | [diff] [blame] | 24 | import android.view.ViewTreeObserver; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 25 | import android.view.Window; |
| 26 | |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 27 | import java.lang.ref.WeakReference; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 28 | import java.util.ArrayList; |
| 29 | |
| 30 | /** |
| 31 | * This class contains all persistence-related functionality for Activity Transitions. |
| 32 | * Activities start exit and enter Activity Transitions through this class. |
| 33 | */ |
| 34 | class ActivityTransitionState { |
| 35 | |
| 36 | private static final String ENTERING_SHARED_ELEMENTS = "android:enteringSharedElements"; |
| 37 | |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 38 | private static final String EXITING_MAPPED_FROM = "android:exitingMappedFrom"; |
| 39 | |
| 40 | private static final String EXITING_MAPPED_TO = "android:exitingMappedTo"; |
| 41 | |
| 42 | /** |
| 43 | * The shared elements that the calling Activity has said that they transferred to this |
| 44 | * Activity. |
| 45 | */ |
| 46 | private ArrayList<String> mEnteringNames; |
| 47 | |
| 48 | /** |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 49 | * The names of shared elements that were shared to the called Activity. |
| 50 | */ |
| 51 | private ArrayList<String> mExitingFrom; |
| 52 | |
| 53 | /** |
| 54 | * The names of local Views that were shared out, mapped to those elements in mExitingFrom. |
| 55 | */ |
| 56 | private ArrayList<String> mExitingTo; |
| 57 | |
| 58 | /** |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 59 | * The local Views that were shared out, mapped to those elements in mExitingFrom. |
| 60 | */ |
| 61 | private ArrayList<View> mExitingToView; |
| 62 | |
| 63 | /** |
| 64 | * The ExitTransitionCoordinator used to start an Activity. Used to make the elements restore |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 65 | * Visibility of exited Views. |
| 66 | */ |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 67 | private ExitTransitionCoordinator mCalledExitCoordinator; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 68 | |
| 69 | /** |
George Mount | f1abef6 | 2014-09-22 13:58:21 -0700 | [diff] [blame] | 70 | * The ExitTransitionCoordinator used to return to a previous Activity when called with |
| 71 | * {@link android.app.Activity#finishAfterTransition()}. |
| 72 | */ |
| 73 | private ExitTransitionCoordinator mReturnExitCoordinator; |
| 74 | |
| 75 | /** |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 76 | * We must be able to cancel entering transitions to stop changing the Window to |
| 77 | * opaque when we exit before making the Window opaque. |
| 78 | */ |
| 79 | private EnterTransitionCoordinator mEnterTransitionCoordinator; |
| 80 | |
| 81 | /** |
| 82 | * ActivityOptions used on entering this Activity. |
| 83 | */ |
| 84 | private ActivityOptions mEnterActivityOptions; |
| 85 | |
| 86 | /** |
| 87 | * Has an exit transition been started? If so, we don't want to double-exit. |
| 88 | */ |
| 89 | private boolean mHasExited; |
| 90 | |
George Mount | 8c2614c | 2014-06-10 11:12:01 -0700 | [diff] [blame] | 91 | /** |
| 92 | * Postpone painting and starting the enter transition until this is false. |
| 93 | */ |
| 94 | private boolean mIsEnterPostponed; |
| 95 | |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 96 | /** |
| 97 | * Potential exit transition coordinators. |
| 98 | */ |
| 99 | private SparseArray<WeakReference<ExitTransitionCoordinator>> mExitTransitionCoordinators; |
| 100 | |
| 101 | /** |
| 102 | * Next key for mExitTransitionCoordinator. |
| 103 | */ |
| 104 | private int mExitTransitionCoordinatorsKey = 1; |
| 105 | |
George Mount | 01e9a97 | 2014-07-09 15:39:23 -0700 | [diff] [blame] | 106 | private boolean mIsEnterTriggered; |
| 107 | |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 108 | public ActivityTransitionState() { |
| 109 | } |
| 110 | |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 111 | public int addExitTransitionCoordinator(ExitTransitionCoordinator exitTransitionCoordinator) { |
| 112 | if (mExitTransitionCoordinators == null) { |
| 113 | mExitTransitionCoordinators = |
| 114 | new SparseArray<WeakReference<ExitTransitionCoordinator>>(); |
| 115 | } |
| 116 | WeakReference<ExitTransitionCoordinator> ref = new WeakReference(exitTransitionCoordinator); |
| 117 | // clean up old references: |
| 118 | for (int i = mExitTransitionCoordinators.size() - 1; i >= 0; i--) { |
| 119 | WeakReference<ExitTransitionCoordinator> oldRef |
| 120 | = mExitTransitionCoordinators.valueAt(i); |
| 121 | if (oldRef.get() == null) { |
| 122 | mExitTransitionCoordinators.removeAt(i); |
| 123 | } |
| 124 | } |
| 125 | int newKey = mExitTransitionCoordinatorsKey++; |
| 126 | mExitTransitionCoordinators.append(newKey, ref); |
| 127 | return newKey; |
| 128 | } |
| 129 | |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 130 | public void readState(Bundle bundle) { |
| 131 | if (bundle != null) { |
| 132 | if (mEnterTransitionCoordinator == null || mEnterTransitionCoordinator.isReturning()) { |
| 133 | mEnteringNames = bundle.getStringArrayList(ENTERING_SHARED_ELEMENTS); |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 134 | } |
| 135 | if (mEnterTransitionCoordinator == null) { |
| 136 | mExitingFrom = bundle.getStringArrayList(EXITING_MAPPED_FROM); |
| 137 | mExitingTo = bundle.getStringArrayList(EXITING_MAPPED_TO); |
| 138 | } |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | public void saveState(Bundle bundle) { |
| 143 | if (mEnteringNames != null) { |
| 144 | bundle.putStringArrayList(ENTERING_SHARED_ELEMENTS, mEnteringNames); |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 145 | } |
| 146 | if (mExitingFrom != null) { |
| 147 | bundle.putStringArrayList(EXITING_MAPPED_FROM, mExitingFrom); |
| 148 | bundle.putStringArrayList(EXITING_MAPPED_TO, mExitingTo); |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | public void setEnterActivityOptions(Activity activity, ActivityOptions options) { |
George Mount | 52d77f2 | 2015-09-09 12:45:55 -0700 | [diff] [blame] | 153 | final Window window = activity.getWindow(); |
| 154 | if (window == null) { |
| 155 | return; |
| 156 | } |
| 157 | // ensure Decor View has been created so that the window features are activated |
| 158 | window.getDecorView(); |
| 159 | if (window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 160 | && options != null && mEnterActivityOptions == null |
George Mount | 01e9a97 | 2014-07-09 15:39:23 -0700 | [diff] [blame] | 161 | && mEnterTransitionCoordinator == null |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 162 | && options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { |
| 163 | mEnterActivityOptions = options; |
George Mount | 01e9a97 | 2014-07-09 15:39:23 -0700 | [diff] [blame] | 164 | mIsEnterTriggered = false; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 165 | if (mEnterActivityOptions.isReturning()) { |
George Mount | 99c82fd | 2014-09-03 07:27:47 -0700 | [diff] [blame] | 166 | restoreExitedViews(); |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 167 | int result = mEnterActivityOptions.getResultCode(); |
| 168 | if (result != 0) { |
| 169 | activity.onActivityReenter(result, mEnterActivityOptions.getResultData()); |
| 170 | } |
| 171 | } |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | public void enterReady(Activity activity) { |
George Mount | 01e9a97 | 2014-07-09 15:39:23 -0700 | [diff] [blame] | 176 | if (mEnterActivityOptions == null || mIsEnterTriggered) { |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 177 | return; |
| 178 | } |
George Mount | 01e9a97 | 2014-07-09 15:39:23 -0700 | [diff] [blame] | 179 | mIsEnterTriggered = true; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 180 | mHasExited = false; |
| 181 | ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames(); |
| 182 | ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver(); |
| 183 | if (mEnterActivityOptions.isReturning()) { |
George Mount | 8cab50a | 2014-05-15 09:57:17 -0700 | [diff] [blame] | 184 | restoreExitedViews(); |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 185 | activity.getWindow().getDecorView().setVisibility(View.VISIBLE); |
George Mount | 8c2614c | 2014-06-10 11:12:01 -0700 | [diff] [blame] | 186 | } |
| 187 | mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity, |
| 188 | resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning()); |
| 189 | |
| 190 | if (!mIsEnterPostponed) { |
| 191 | startEnter(); |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | public void postponeEnterTransition() { |
| 196 | mIsEnterPostponed = true; |
| 197 | } |
| 198 | |
| 199 | public void startPostponedEnterTransition() { |
| 200 | if (mIsEnterPostponed) { |
| 201 | mIsEnterPostponed = false; |
| 202 | if (mEnterTransitionCoordinator != null) { |
| 203 | startEnter(); |
| 204 | } |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | private void startEnter() { |
George Mount | d7d0fb3 | 2016-01-04 14:56:08 -0800 | [diff] [blame] | 209 | if (mEnterTransitionCoordinator.isReturning()) { |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 210 | if (mExitingToView != null) { |
Dake Gu | b04b36e | 2014-08-08 11:22:30 -0700 | [diff] [blame] | 211 | mEnterTransitionCoordinator.viewInstancesReady(mExitingFrom, mExitingTo, |
| 212 | mExitingToView); |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 213 | } else { |
| 214 | mEnterTransitionCoordinator.namedViewsReady(mExitingFrom, mExitingTo); |
| 215 | } |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 216 | } else { |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 217 | mEnterTransitionCoordinator.namedViewsReady(null, null); |
George Mount | 8c2614c | 2014-06-10 11:12:01 -0700 | [diff] [blame] | 218 | mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames(); |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 219 | } |
George Mount | 8c2614c | 2014-06-10 11:12:01 -0700 | [diff] [blame] | 220 | |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 221 | mExitingFrom = null; |
| 222 | mExitingTo = null; |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 223 | mExitingToView = null; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 224 | mEnterActivityOptions = null; |
| 225 | } |
| 226 | |
| 227 | public void onStop() { |
George Mount | 8cab50a | 2014-05-15 09:57:17 -0700 | [diff] [blame] | 228 | restoreExitedViews(); |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 229 | if (mEnterTransitionCoordinator != null) { |
| 230 | mEnterTransitionCoordinator.stop(); |
| 231 | mEnterTransitionCoordinator = null; |
| 232 | } |
George Mount | f1abef6 | 2014-09-22 13:58:21 -0700 | [diff] [blame] | 233 | if (mReturnExitCoordinator != null) { |
| 234 | mReturnExitCoordinator.stop(); |
| 235 | mReturnExitCoordinator = null; |
| 236 | } |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 237 | } |
| 238 | |
George Mount | 8cab50a | 2014-05-15 09:57:17 -0700 | [diff] [blame] | 239 | public void onResume() { |
| 240 | restoreExitedViews(); |
George Mount | d7d0fb3 | 2016-01-04 14:56:08 -0800 | [diff] [blame] | 241 | restoreReenteringViews(); |
George Mount | 8cab50a | 2014-05-15 09:57:17 -0700 | [diff] [blame] | 242 | } |
| 243 | |
George Mount | a0a0260 | 2014-06-20 18:22:26 -0700 | [diff] [blame] | 244 | public void clear() { |
| 245 | mEnteringNames = null; |
| 246 | mExitingFrom = null; |
| 247 | mExitingTo = null; |
| 248 | mExitingToView = null; |
| 249 | mCalledExitCoordinator = null; |
| 250 | mEnterTransitionCoordinator = null; |
| 251 | mEnterActivityOptions = null; |
| 252 | mExitTransitionCoordinators = null; |
| 253 | } |
| 254 | |
George Mount | 8cab50a | 2014-05-15 09:57:17 -0700 | [diff] [blame] | 255 | private void restoreExitedViews() { |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 256 | if (mCalledExitCoordinator != null) { |
| 257 | mCalledExitCoordinator.resetViews(); |
| 258 | mCalledExitCoordinator = null; |
George Mount | 8cab50a | 2014-05-15 09:57:17 -0700 | [diff] [blame] | 259 | } |
| 260 | } |
| 261 | |
George Mount | d7d0fb3 | 2016-01-04 14:56:08 -0800 | [diff] [blame] | 262 | private void restoreReenteringViews() { |
| 263 | if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning()) { |
| 264 | mEnterTransitionCoordinator.forceViewsToAppear(); |
| 265 | mExitingFrom = null; |
| 266 | mExitingTo = null; |
| 267 | mExitingToView = null; |
| 268 | } |
| 269 | } |
| 270 | |
George Mount | fbd4596 | 2015-01-26 14:38:19 -0800 | [diff] [blame] | 271 | public boolean startExitBackTransition(final Activity activity) { |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 272 | if (mEnteringNames == null) { |
| 273 | return false; |
| 274 | } else { |
| 275 | if (!mHasExited) { |
| 276 | mHasExited = true; |
George Mount | a2bbbb3 | 2014-08-12 10:16:20 -0700 | [diff] [blame] | 277 | Transition enterViewsTransition = null; |
| 278 | ViewGroup decor = null; |
George Mount | fbd4596 | 2015-01-26 14:38:19 -0800 | [diff] [blame] | 279 | boolean delayExitBack = false; |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 280 | if (mEnterTransitionCoordinator != null) { |
George Mount | a2bbbb3 | 2014-08-12 10:16:20 -0700 | [diff] [blame] | 281 | enterViewsTransition = mEnterTransitionCoordinator.getEnterViewsTransition(); |
| 282 | decor = mEnterTransitionCoordinator.getDecor(); |
George Mount | fbd4596 | 2015-01-26 14:38:19 -0800 | [diff] [blame] | 283 | delayExitBack = mEnterTransitionCoordinator.cancelEnter(); |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 284 | mEnterTransitionCoordinator = null; |
George Mount | a2bbbb3 | 2014-08-12 10:16:20 -0700 | [diff] [blame] | 285 | if (enterViewsTransition != null && decor != null) { |
| 286 | enterViewsTransition.pause(decor); |
| 287 | } |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 288 | } |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 289 | |
George Mount | f1abef6 | 2014-09-22 13:58:21 -0700 | [diff] [blame] | 290 | mReturnExitCoordinator = |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 291 | new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true); |
George Mount | a2bbbb3 | 2014-08-12 10:16:20 -0700 | [diff] [blame] | 292 | if (enterViewsTransition != null && decor != null) { |
| 293 | enterViewsTransition.resume(decor); |
| 294 | } |
George Mount | fbd4596 | 2015-01-26 14:38:19 -0800 | [diff] [blame] | 295 | if (delayExitBack && decor != null) { |
| 296 | final ViewGroup finalDecor = decor; |
| 297 | decor.getViewTreeObserver().addOnPreDrawListener( |
| 298 | new ViewTreeObserver.OnPreDrawListener() { |
| 299 | @Override |
| 300 | public boolean onPreDraw() { |
| 301 | finalDecor.getViewTreeObserver().removeOnPreDrawListener(this); |
| 302 | if (mReturnExitCoordinator != null) { |
| 303 | mReturnExitCoordinator.startExit(activity.mResultCode, |
| 304 | activity.mResultData); |
| 305 | } |
| 306 | return true; |
| 307 | } |
| 308 | }); |
| 309 | } else { |
| 310 | mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData); |
| 311 | } |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 312 | } |
| 313 | return true; |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | public void startExitOutTransition(Activity activity, Bundle options) { |
George Mount | 9826f63 | 2014-09-11 08:50:09 -0700 | [diff] [blame] | 318 | if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) { |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 319 | return; |
| 320 | } |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 321 | ActivityOptions activityOptions = new ActivityOptions(options); |
George Mount | 01e9a97 | 2014-07-09 15:39:23 -0700 | [diff] [blame] | 322 | mEnterTransitionCoordinator = null; |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 323 | if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { |
| 324 | int key = activityOptions.getExitCoordinatorKey(); |
| 325 | int index = mExitTransitionCoordinators.indexOfKey(key); |
| 326 | if (index >= 0) { |
| 327 | mCalledExitCoordinator = mExitTransitionCoordinators.valueAt(index).get(); |
| 328 | mExitTransitionCoordinators.removeAt(index); |
| 329 | if (mCalledExitCoordinator != null) { |
| 330 | mExitingFrom = mCalledExitCoordinator.getAcceptedNames(); |
| 331 | mExitingTo = mCalledExitCoordinator.getMappedNames(); |
George Mount | 700db2a | 2014-07-07 17:17:49 -0700 | [diff] [blame] | 332 | mExitingToView = mCalledExitCoordinator.copyMappedViews(); |
George Mount | 1fecfb2 | 2014-06-18 14:55:55 -0700 | [diff] [blame] | 333 | mCalledExitCoordinator.startExit(); |
| 334 | } |
| 335 | } |
George Mount | 62ab9b7 | 2014-05-02 13:51:17 -0700 | [diff] [blame] | 336 | } |
| 337 | } |
| 338 | } |