blob: bf0bd795230ed9f02edd9447f747483915b7c8e3 [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 Mount8cab50a2014-05-15 09:57:17 -0700239 public void onResume() {
240 restoreExitedViews();
George Mountd7d0fb32016-01-04 14:56:08 -0800241 restoreReenteringViews();
George Mount8cab50a2014-05-15 09:57:17 -0700242 }
243
George Mounta0a02602014-06-20 18:22:26 -0700244 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 Mount8cab50a2014-05-15 09:57:17 -0700255 private void restoreExitedViews() {
George Mount1fecfb22014-06-18 14:55:55 -0700256 if (mCalledExitCoordinator != null) {
257 mCalledExitCoordinator.resetViews();
258 mCalledExitCoordinator = null;
George Mount8cab50a2014-05-15 09:57:17 -0700259 }
260 }
261
George Mountd7d0fb32016-01-04 14:56:08 -0800262 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 Mountfbd45962015-01-26 14:38:19 -0800271 public boolean startExitBackTransition(final Activity activity) {
George Mount62ab9b72014-05-02 13:51:17 -0700272 if (mEnteringNames == null) {
273 return false;
274 } else {
275 if (!mHasExited) {
276 mHasExited = true;
George Mounta2bbbb32014-08-12 10:16:20 -0700277 Transition enterViewsTransition = null;
278 ViewGroup decor = null;
George Mountfbd45962015-01-26 14:38:19 -0800279 boolean delayExitBack = false;
George Mount62ab9b72014-05-02 13:51:17 -0700280 if (mEnterTransitionCoordinator != null) {
George Mounta2bbbb32014-08-12 10:16:20 -0700281 enterViewsTransition = mEnterTransitionCoordinator.getEnterViewsTransition();
282 decor = mEnterTransitionCoordinator.getDecor();
George Mountfbd45962015-01-26 14:38:19 -0800283 delayExitBack = mEnterTransitionCoordinator.cancelEnter();
George Mount62ab9b72014-05-02 13:51:17 -0700284 mEnterTransitionCoordinator = null;
George Mounta2bbbb32014-08-12 10:16:20 -0700285 if (enterViewsTransition != null && decor != null) {
286 enterViewsTransition.pause(decor);
287 }
George Mount62ab9b72014-05-02 13:51:17 -0700288 }
George Mount62ab9b72014-05-02 13:51:17 -0700289
George Mountf1abef62014-09-22 13:58:21 -0700290 mReturnExitCoordinator =
George Mount1fecfb22014-06-18 14:55:55 -0700291 new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true);
George Mounta2bbbb32014-08-12 10:16:20 -0700292 if (enterViewsTransition != null && decor != null) {
293 enterViewsTransition.resume(decor);
294 }
George Mountfbd45962015-01-26 14:38:19 -0800295 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 Mount62ab9b72014-05-02 13:51:17 -0700312 }
313 return true;
314 }
315 }
316
317 public void startExitOutTransition(Activity activity, Bundle options) {
George Mount9826f632014-09-11 08:50:09 -0700318 if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
George Mount62ab9b72014-05-02 13:51:17 -0700319 return;
320 }
George Mount1fecfb22014-06-18 14:55:55 -0700321 ActivityOptions activityOptions = new ActivityOptions(options);
George Mount01e9a972014-07-09 15:39:23 -0700322 mEnterTransitionCoordinator = null;
George Mount1fecfb22014-06-18 14:55:55 -0700323 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 Mount700db2a2014-07-07 17:17:49 -0700332 mExitingToView = mCalledExitCoordinator.copyMappedViews();
George Mount1fecfb22014-06-18 14:55:55 -0700333 mCalledExitCoordinator.startExit();
334 }
335 }
George Mount62ab9b72014-05-02 13:51:17 -0700336 }
337 }
338}