ActivityTransitionState.java revision 99c82fd1de23deeb8cf640bb574c76af76429df6
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 */
16package android.app;
17
18import android.os.Bundle;
19import android.os.ResultReceiver;
20import android.transition.Transition;
21import android.util.ArrayMap;
22import android.util.SparseArray;
23import android.view.View;
24import android.view.ViewGroup;
25import android.view.Window;
26
27import java.lang.ref.WeakReference;
28import 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
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    /**
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    /**
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
65     * Visibility of exited Views.
66     */
67    private ExitTransitionCoordinator mCalledExitCoordinator;
68
69    /**
70     * We must be able to cancel entering transitions to stop changing the Window to
71     * opaque when we exit before making the Window opaque.
72     */
73    private EnterTransitionCoordinator mEnterTransitionCoordinator;
74
75    /**
76     * ActivityOptions used on entering this Activity.
77     */
78    private ActivityOptions mEnterActivityOptions;
79
80    /**
81     * Has an exit transition been started? If so, we don't want to double-exit.
82     */
83    private boolean mHasExited;
84
85    /**
86     * Postpone painting and starting the enter transition until this is false.
87     */
88    private boolean mIsEnterPostponed;
89
90    /**
91     * Potential exit transition coordinators.
92     */
93    private SparseArray<WeakReference<ExitTransitionCoordinator>> mExitTransitionCoordinators;
94
95    /**
96     * Next key for mExitTransitionCoordinator.
97     */
98    private int mExitTransitionCoordinatorsKey = 1;
99
100    private boolean mIsEnterTriggered;
101
102    public ActivityTransitionState() {
103    }
104
105    public int addExitTransitionCoordinator(ExitTransitionCoordinator exitTransitionCoordinator) {
106        if (mExitTransitionCoordinators == null) {
107            mExitTransitionCoordinators =
108                    new SparseArray<WeakReference<ExitTransitionCoordinator>>();
109        }
110        WeakReference<ExitTransitionCoordinator> ref = new WeakReference(exitTransitionCoordinator);
111        // clean up old references:
112        for (int i = mExitTransitionCoordinators.size() - 1; i >= 0; i--) {
113            WeakReference<ExitTransitionCoordinator> oldRef
114                    = mExitTransitionCoordinators.valueAt(i);
115            if (oldRef.get() == null) {
116                mExitTransitionCoordinators.removeAt(i);
117            }
118        }
119        int newKey = mExitTransitionCoordinatorsKey++;
120        mExitTransitionCoordinators.append(newKey, ref);
121        return newKey;
122    }
123
124    public void readState(Bundle bundle) {
125        if (bundle != null) {
126            if (mEnterTransitionCoordinator == null || mEnterTransitionCoordinator.isReturning()) {
127                mEnteringNames = bundle.getStringArrayList(ENTERING_SHARED_ELEMENTS);
128            }
129            if (mEnterTransitionCoordinator == null) {
130                mExitingFrom = bundle.getStringArrayList(EXITING_MAPPED_FROM);
131                mExitingTo = bundle.getStringArrayList(EXITING_MAPPED_TO);
132            }
133        }
134    }
135
136    public void saveState(Bundle bundle) {
137        if (mEnteringNames != null) {
138            bundle.putStringArrayList(ENTERING_SHARED_ELEMENTS, mEnteringNames);
139        }
140        if (mExitingFrom != null) {
141            bundle.putStringArrayList(EXITING_MAPPED_FROM, mExitingFrom);
142            bundle.putStringArrayList(EXITING_MAPPED_TO, mExitingTo);
143        }
144    }
145
146    public void setEnterActivityOptions(Activity activity, ActivityOptions options) {
147        if (activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)
148                && options != null && mEnterActivityOptions == null
149                && mEnterTransitionCoordinator == null
150                && options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
151            mEnterActivityOptions = options;
152            mIsEnterTriggered = false;
153            if (mEnterActivityOptions.isReturning()) {
154                restoreExitedViews();
155                int result = mEnterActivityOptions.getResultCode();
156                if (result != 0) {
157                    activity.onActivityReenter(result, mEnterActivityOptions.getResultData());
158                }
159            }
160        }
161    }
162
163    public void enterReady(Activity activity) {
164        if (mEnterActivityOptions == null || mIsEnterTriggered) {
165            return;
166        }
167        mIsEnterTriggered = true;
168        mHasExited = false;
169        ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
170        ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
171        if (mEnterActivityOptions.isReturning()) {
172            restoreExitedViews();
173            activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
174        }
175        mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
176                resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning());
177
178        if (!mIsEnterPostponed) {
179            startEnter();
180        }
181    }
182
183    public void postponeEnterTransition() {
184        mIsEnterPostponed = true;
185    }
186
187    public void startPostponedEnterTransition() {
188        if (mIsEnterPostponed) {
189            mIsEnterPostponed = false;
190            if (mEnterTransitionCoordinator != null) {
191                startEnter();
192            }
193        }
194    }
195
196    private void startEnter() {
197        if (mEnterActivityOptions.isReturning()) {
198            if (mExitingToView != null) {
199                mEnterTransitionCoordinator.viewInstancesReady(mExitingFrom, mExitingTo,
200                        mExitingToView);
201            } else {
202                mEnterTransitionCoordinator.namedViewsReady(mExitingFrom, mExitingTo);
203            }
204        } else {
205            mEnterTransitionCoordinator.namedViewsReady(null, null);
206            mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames();
207        }
208
209        mExitingFrom = null;
210        mExitingTo = null;
211        mExitingToView = null;
212        mEnterActivityOptions = null;
213    }
214
215    public void onStop() {
216        restoreExitedViews();
217        if (mEnterTransitionCoordinator != null) {
218            mEnterTransitionCoordinator.stop();
219            mEnterTransitionCoordinator = null;
220        }
221    }
222
223    public void onResume() {
224        restoreExitedViews();
225    }
226
227    public void clear() {
228        mEnteringNames = null;
229        mExitingFrom = null;
230        mExitingTo = null;
231        mExitingToView = null;
232        mCalledExitCoordinator = null;
233        mEnterTransitionCoordinator = null;
234        mEnterActivityOptions = null;
235        mExitTransitionCoordinators = null;
236    }
237
238    private void restoreExitedViews() {
239        if (mCalledExitCoordinator != null) {
240            mCalledExitCoordinator.resetViews();
241            mCalledExitCoordinator = null;
242        }
243    }
244
245    public boolean startExitBackTransition(Activity activity) {
246        if (mEnteringNames == null) {
247            return false;
248        } else {
249            if (!mHasExited) {
250                mHasExited = true;
251                Transition enterViewsTransition = null;
252                ViewGroup decor = null;
253                if (mEnterTransitionCoordinator != null) {
254                    enterViewsTransition = mEnterTransitionCoordinator.getEnterViewsTransition();
255                    decor = mEnterTransitionCoordinator.getDecor();
256                    mEnterTransitionCoordinator.cancelEnter();
257                    mEnterTransitionCoordinator = null;
258                    if (enterViewsTransition != null && decor != null) {
259                        enterViewsTransition.pause(decor);
260                    }
261                }
262
263                ExitTransitionCoordinator exitCoordinator =
264                        new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true);
265                if (enterViewsTransition != null && decor != null) {
266                    enterViewsTransition.resume(decor);
267                }
268                exitCoordinator.startExit(activity.mResultCode, activity.mResultData);
269            }
270            return true;
271        }
272    }
273
274    public void startExitOutTransition(Activity activity, Bundle options) {
275        if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
276            return;
277        }
278        ActivityOptions activityOptions = new ActivityOptions(options);
279        mEnterTransitionCoordinator = null;
280        if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
281            int key = activityOptions.getExitCoordinatorKey();
282            int index = mExitTransitionCoordinators.indexOfKey(key);
283            if (index >= 0) {
284                mCalledExitCoordinator = mExitTransitionCoordinators.valueAt(index).get();
285                mExitTransitionCoordinators.removeAt(index);
286                if (mCalledExitCoordinator != null) {
287                    mExitingFrom = mCalledExitCoordinator.getAcceptedNames();
288                    mExitingTo = mCalledExitCoordinator.getMappedNames();
289                    mExitingToView = mCalledExitCoordinator.copyMappedViews();
290                    mCalledExitCoordinator.startExit();
291                }
292            }
293        }
294    }
295}
296