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