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