blob: d3ca7eebdfcd4367a294be4d5ba669435cf2d701 [file] [log] [blame]
George Mount62ab9b72014-05-02 13:51:17 -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.os.Bundle;
19import android.os.ResultReceiver;
George Mounta2bbbb32014-08-12 10:16:20 -070020import android.transition.Transition;
George Mount1fecfb22014-06-18 14:55:55 -070021import android.util.SparseArray;
George Mount62ab9b72014-05-02 13:51:17 -070022import android.view.View;
George Mounta2bbbb32014-08-12 10:16:20 -070023import android.view.ViewGroup;
George Mountfbd45962015-01-26 14:38:19 -080024import android.view.ViewTreeObserver;
George Mount62ab9b72014-05-02 13:51:17 -070025import android.view.Window;
26
George Mount1fecfb22014-06-18 14:55:55 -070027import java.lang.ref.WeakReference;
George Mount62ab9b72014-05-02 13:51:17 -070028import 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 */
34class ActivityTransitionState {
35
36 private static final String ENTERING_SHARED_ELEMENTS = "android:enteringSharedElements";
37
George Mount62ab9b72014-05-02 13:51:17 -070038 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 Mount62ab9b72014-05-02 13:51:17 -070049 * 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 Mount1fecfb22014-06-18 14:55:55 -070059 * 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 Mount62ab9b72014-05-02 13:51:17 -070065 * Visibility of exited Views.
66 */
George Mount1fecfb22014-06-18 14:55:55 -070067 private ExitTransitionCoordinator mCalledExitCoordinator;
George Mount62ab9b72014-05-02 13:51:17 -070068
69 /**
George Mountf1abef62014-09-22 13:58:21 -070070 * 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 Mount62ab9b72014-05-02 13:51:17 -070076 * 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 Mount8c2614c2014-06-10 11:12:01 -070091 /**
92 * Postpone painting and starting the enter transition until this is false.
93 */
94 private boolean mIsEnterPostponed;
95
George Mount1fecfb22014-06-18 14:55:55 -070096 /**
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 Mount01e9a972014-07-09 15:39:23 -0700106 private boolean mIsEnterTriggered;
107
George Mount62ab9b72014-05-02 13:51:17 -0700108 public ActivityTransitionState() {
109 }
110
George Mount1fecfb22014-06-18 14:55:55 -0700111 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 Mount62ab9b72014-05-02 13:51:17 -0700130 public void readState(Bundle bundle) {
131 if (bundle != null) {
132 if (mEnterTransitionCoordinator == null || mEnterTransitionCoordinator.isReturning()) {
133 mEnteringNames = bundle.getStringArrayList(ENTERING_SHARED_ELEMENTS);
George Mount62ab9b72014-05-02 13:51:17 -0700134 }
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 Mount62ab9b72014-05-02 13:51:17 -0700145 }
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 Mount52d77f22015-09-09 12:45:55 -0700153 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 Mount62ab9b72014-05-02 13:51:17 -0700160 && options != null && mEnterActivityOptions == null
George Mount01e9a972014-07-09 15:39:23 -0700161 && mEnterTransitionCoordinator == null
George Mount62ab9b72014-05-02 13:51:17 -0700162 && options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
163 mEnterActivityOptions = options;
George Mount01e9a972014-07-09 15:39:23 -0700164 mIsEnterTriggered = false;
George Mount62ab9b72014-05-02 13:51:17 -0700165 if (mEnterActivityOptions.isReturning()) {
George Mount99c82fd2014-09-03 07:27:47 -0700166 restoreExitedViews();
George Mount62ab9b72014-05-02 13:51:17 -0700167 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 Mount01e9a972014-07-09 15:39:23 -0700176 if (mEnterActivityOptions == null || mIsEnterTriggered) {
George Mount62ab9b72014-05-02 13:51:17 -0700177 return;
178 }
George Mount01e9a972014-07-09 15:39:23 -0700179 mIsEnterTriggered = true;
George Mount62ab9b72014-05-02 13:51:17 -0700180 mHasExited = false;
181 ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
182 ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
183 if (mEnterActivityOptions.isReturning()) {
George Mount8cab50a2014-05-15 09:57:17 -0700184 restoreExitedViews();
George Mount62ab9b72014-05-02 13:51:17 -0700185 activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
George Mount8c2614c2014-06-10 11:12:01 -0700186 }
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 Mountd7d0fb32016-01-04 14:56:08 -0800209 if (mEnterTransitionCoordinator.isReturning()) {
George Mount1fecfb22014-06-18 14:55:55 -0700210 if (mExitingToView != null) {
Dake Gub04b36e2014-08-08 11:22:30 -0700211 mEnterTransitionCoordinator.viewInstancesReady(mExitingFrom, mExitingTo,
212 mExitingToView);
George Mount1fecfb22014-06-18 14:55:55 -0700213 } else {
214 mEnterTransitionCoordinator.namedViewsReady(mExitingFrom, mExitingTo);
215 }
George Mount62ab9b72014-05-02 13:51:17 -0700216 } else {
George Mount1fecfb22014-06-18 14:55:55 -0700217 mEnterTransitionCoordinator.namedViewsReady(null, null);
George Mount8c2614c2014-06-10 11:12:01 -0700218 mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames();
George Mount62ab9b72014-05-02 13:51:17 -0700219 }
George Mount8c2614c2014-06-10 11:12:01 -0700220
George Mount62ab9b72014-05-02 13:51:17 -0700221 mExitingFrom = null;
222 mExitingTo = null;
George Mount1fecfb22014-06-18 14:55:55 -0700223 mExitingToView = null;
George Mount62ab9b72014-05-02 13:51:17 -0700224 mEnterActivityOptions = null;
225 }
226
227 public void onStop() {
George Mount8cab50a2014-05-15 09:57:17 -0700228 restoreExitedViews();
George Mount62ab9b72014-05-02 13:51:17 -0700229 if (mEnterTransitionCoordinator != null) {
230 mEnterTransitionCoordinator.stop();
231 mEnterTransitionCoordinator = null;
232 }
George Mountf1abef62014-09-22 13:58:21 -0700233 if (mReturnExitCoordinator != null) {
234 mReturnExitCoordinator.stop();
235 mReturnExitCoordinator = null;
236 }
George Mount62ab9b72014-05-02 13:51:17 -0700237 }
238
George Mountbdc4d8d2016-03-23 13:10:13 -0700239 public void onResume(Activity activity, boolean isTopOfTask) {
240 // After orientation change, the onResume can come in before the top Activity has
241 // left, so if the Activity is not top, wait a second for the top Activity to exit.
242 if (mCalledExitCoordinator == null) {
243 return; // This is the called activity
244 }
245 if (isTopOfTask || mEnterTransitionCoordinator == null) {
246 restoreExitedViews();
247 restoreReenteringViews();
248 } else {
249 activity.mHandler.postDelayed(new Runnable() {
250 @Override
251 public void run() {
252 if (mEnterTransitionCoordinator == null ||
253 mEnterTransitionCoordinator.isWaitingForRemoteExit()) {
254 restoreExitedViews();
255 restoreReenteringViews();
256 }
257 }
258 }, 1000);
259 }
George Mount8cab50a2014-05-15 09:57:17 -0700260 }
261
George Mounta0a02602014-06-20 18:22:26 -0700262 public void clear() {
263 mEnteringNames = null;
264 mExitingFrom = null;
265 mExitingTo = null;
266 mExitingToView = null;
267 mCalledExitCoordinator = null;
268 mEnterTransitionCoordinator = null;
269 mEnterActivityOptions = null;
270 mExitTransitionCoordinators = null;
271 }
272
George Mount8cab50a2014-05-15 09:57:17 -0700273 private void restoreExitedViews() {
George Mount1fecfb22014-06-18 14:55:55 -0700274 if (mCalledExitCoordinator != null) {
275 mCalledExitCoordinator.resetViews();
276 mCalledExitCoordinator = null;
George Mount8cab50a2014-05-15 09:57:17 -0700277 }
278 }
279
George Mountd7d0fb32016-01-04 14:56:08 -0800280 private void restoreReenteringViews() {
281 if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning()) {
282 mEnterTransitionCoordinator.forceViewsToAppear();
283 mExitingFrom = null;
284 mExitingTo = null;
285 mExitingToView = null;
286 }
287 }
288
George Mountfbd45962015-01-26 14:38:19 -0800289 public boolean startExitBackTransition(final Activity activity) {
George Mount62ab9b72014-05-02 13:51:17 -0700290 if (mEnteringNames == null) {
291 return false;
292 } else {
293 if (!mHasExited) {
294 mHasExited = true;
George Mounta2bbbb32014-08-12 10:16:20 -0700295 Transition enterViewsTransition = null;
296 ViewGroup decor = null;
George Mountfbd45962015-01-26 14:38:19 -0800297 boolean delayExitBack = false;
George Mount62ab9b72014-05-02 13:51:17 -0700298 if (mEnterTransitionCoordinator != null) {
George Mounta2bbbb32014-08-12 10:16:20 -0700299 enterViewsTransition = mEnterTransitionCoordinator.getEnterViewsTransition();
300 decor = mEnterTransitionCoordinator.getDecor();
George Mountfbd45962015-01-26 14:38:19 -0800301 delayExitBack = mEnterTransitionCoordinator.cancelEnter();
George Mount62ab9b72014-05-02 13:51:17 -0700302 mEnterTransitionCoordinator = null;
George Mounta2bbbb32014-08-12 10:16:20 -0700303 if (enterViewsTransition != null && decor != null) {
304 enterViewsTransition.pause(decor);
305 }
George Mount62ab9b72014-05-02 13:51:17 -0700306 }
George Mount62ab9b72014-05-02 13:51:17 -0700307
George Mountf1abef62014-09-22 13:58:21 -0700308 mReturnExitCoordinator =
George Mount1fecfb22014-06-18 14:55:55 -0700309 new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true);
George Mounta2bbbb32014-08-12 10:16:20 -0700310 if (enterViewsTransition != null && decor != null) {
311 enterViewsTransition.resume(decor);
312 }
George Mountfbd45962015-01-26 14:38:19 -0800313 if (delayExitBack && decor != null) {
314 final ViewGroup finalDecor = decor;
315 decor.getViewTreeObserver().addOnPreDrawListener(
316 new ViewTreeObserver.OnPreDrawListener() {
317 @Override
318 public boolean onPreDraw() {
319 finalDecor.getViewTreeObserver().removeOnPreDrawListener(this);
320 if (mReturnExitCoordinator != null) {
321 mReturnExitCoordinator.startExit(activity.mResultCode,
322 activity.mResultData);
323 }
324 return true;
325 }
326 });
327 } else {
328 mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData);
329 }
George Mount62ab9b72014-05-02 13:51:17 -0700330 }
331 return true;
332 }
333 }
334
335 public void startExitOutTransition(Activity activity, Bundle options) {
George Mount9826f632014-09-11 08:50:09 -0700336 if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
George Mount62ab9b72014-05-02 13:51:17 -0700337 return;
338 }
George Mount1fecfb22014-06-18 14:55:55 -0700339 ActivityOptions activityOptions = new ActivityOptions(options);
George Mount01e9a972014-07-09 15:39:23 -0700340 mEnterTransitionCoordinator = null;
George Mount1fecfb22014-06-18 14:55:55 -0700341 if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
342 int key = activityOptions.getExitCoordinatorKey();
343 int index = mExitTransitionCoordinators.indexOfKey(key);
344 if (index >= 0) {
345 mCalledExitCoordinator = mExitTransitionCoordinators.valueAt(index).get();
346 mExitTransitionCoordinators.removeAt(index);
347 if (mCalledExitCoordinator != null) {
348 mExitingFrom = mCalledExitCoordinator.getAcceptedNames();
349 mExitingTo = mCalledExitCoordinator.getMappedNames();
George Mount700db2a2014-07-07 17:17:49 -0700350 mExitingToView = mCalledExitCoordinator.copyMappedViews();
George Mount1fecfb22014-06-18 14:55:55 -0700351 mCalledExitCoordinator.startExit();
352 }
353 }
George Mount62ab9b72014-05-02 13:51:17 -0700354 }
355 }
356}