ActivityTransitionState.java revision a2bbbb3bac464c3a52e33008401fed4a7542744c
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                int result = mEnterActivityOptions.getResultCode();
155                if (result != 0) {
156                    activity.onActivityReenter(result, mEnterActivityOptions.getResultData());
157                }
158            }
159        }
160    }
161
162    public void enterReady(Activity activity) {
163        if (mEnterActivityOptions == null || mIsEnterTriggered) {
164            return;
165        }
166        mIsEnterTriggered = true;
167        mHasExited = false;
168        ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
169        ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
170        if (mEnterActivityOptions.isReturning()) {
171            restoreExitedViews();
172            activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
173        }
174        mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
175                resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning());
176
177        if (!mIsEnterPostponed) {
178            startEnter();
179        }
180    }
181
182    public void postponeEnterTransition() {
183        mIsEnterPostponed = true;
184    }
185
186    public void startPostponedEnterTransition() {
187        if (mIsEnterPostponed) {
188            mIsEnterPostponed = false;
189            if (mEnterTransitionCoordinator != null) {
190                startEnter();
191            }
192        }
193    }
194
195    private void startEnter() {
196        if (mEnterActivityOptions.isReturning()) {
197            if (mExitingToView != null) {
198                mEnterTransitionCoordinator.viewInstancesReady(mExitingFrom, mExitingTo,
199                        mExitingToView);
200            } else {
201                mEnterTransitionCoordinator.namedViewsReady(mExitingFrom, mExitingTo);
202            }
203        } else {
204            mEnterTransitionCoordinator.namedViewsReady(null, null);
205            mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames();
206        }
207
208        mExitingFrom = null;
209        mExitingTo = null;
210        mExitingToView = null;
211        mEnterActivityOptions = null;
212    }
213
214    public void onStop() {
215        restoreExitedViews();
216        if (mEnterTransitionCoordinator != null) {
217            mEnterTransitionCoordinator.stop();
218            mEnterTransitionCoordinator = null;
219        }
220    }
221
222    public void onResume() {
223        restoreExitedViews();
224    }
225
226    public void clear() {
227        mEnteringNames = null;
228        mExitingFrom = null;
229        mExitingTo = null;
230        mExitingToView = null;
231        mCalledExitCoordinator = null;
232        mEnterTransitionCoordinator = null;
233        mEnterActivityOptions = null;
234        mExitTransitionCoordinators = null;
235    }
236
237    private void restoreExitedViews() {
238        if (mCalledExitCoordinator != null) {
239            mCalledExitCoordinator.resetViews();
240            mCalledExitCoordinator = null;
241        }
242    }
243
244    public boolean startExitBackTransition(Activity activity) {
245        if (mEnteringNames == null) {
246            return false;
247        } else {
248            if (!mHasExited) {
249                mHasExited = true;
250                Transition enterViewsTransition = null;
251                ViewGroup decor = null;
252                if (mEnterTransitionCoordinator != null) {
253                    enterViewsTransition = mEnterTransitionCoordinator.getEnterViewsTransition();
254                    decor = mEnterTransitionCoordinator.getDecor();
255                    mEnterTransitionCoordinator.cancelEnter();
256                    mEnterTransitionCoordinator = null;
257                    if (enterViewsTransition != null && decor != null) {
258                        enterViewsTransition.pause(decor);
259                    }
260                }
261
262                ExitTransitionCoordinator exitCoordinator =
263                        new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true);
264                if (enterViewsTransition != null && decor != null) {
265                    enterViewsTransition.resume(decor);
266                }
267                exitCoordinator.startExit(activity.mResultCode, activity.mResultData);
268            }
269            return true;
270        }
271    }
272
273    public void startExitOutTransition(Activity activity, Bundle options) {
274        if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
275            return;
276        }
277        ActivityOptions activityOptions = new ActivityOptions(options);
278        mEnterTransitionCoordinator = null;
279        if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
280            int key = activityOptions.getExitCoordinatorKey();
281            int index = mExitTransitionCoordinators.indexOfKey(key);
282            if (index >= 0) {
283                mCalledExitCoordinator = mExitTransitionCoordinators.valueAt(index).get();
284                mExitTransitionCoordinators.removeAt(index);
285                if (mCalledExitCoordinator != null) {
286                    mExitingFrom = mCalledExitCoordinator.getAcceptedNames();
287                    mExitingTo = mCalledExitCoordinator.getMappedNames();
288                    mExitingToView = mCalledExitCoordinator.copyMappedViews();
289                    mCalledExitCoordinator.startExit();
290                }
291            }
292        }
293    }
294}
295