StateManager.java revision 3847e4e8e26ed6c6f18ee4a6b2984d33051d155e
1/*
2 * Copyright (C) 2010 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 */
16
17package com.android.gallery3d.app;
18
19import android.app.Activity;
20import android.content.Intent;
21import android.content.res.Configuration;
22import android.os.Bundle;
23import android.os.Parcelable;
24import android.view.Menu;
25import android.view.MenuItem;
26
27import com.android.gallery3d.anim.StateTransitionAnimation;
28import com.android.gallery3d.common.Utils;
29import com.android.gallery3d.util.UsageStatistics;
30
31import java.util.Stack;
32
33public class StateManager {
34    @SuppressWarnings("unused")
35    private static final String TAG = "StateManager";
36    private boolean mIsResumed = false;
37
38    private static final String KEY_MAIN = "activity-state";
39    private static final String KEY_DATA = "data";
40    private static final String KEY_STATE = "bundle";
41    private static final String KEY_CLASS = "class";
42
43    private AbstractGalleryActivity mActivity;
44    private Stack<StateEntry> mStack = new Stack<StateEntry>();
45    private ActivityState.ResultEntry mResult;
46
47    public StateManager(AbstractGalleryActivity activity) {
48        mActivity = activity;
49    }
50
51    public void startState(Class<? extends ActivityState> klass,
52            Bundle data) {
53        Log.v(TAG, "startState " + klass);
54        ActivityState state = null;
55        try {
56            state = klass.newInstance();
57        } catch (Exception e) {
58            throw new AssertionError(e);
59        }
60        if (!mStack.isEmpty()) {
61            ActivityState top = getTopState();
62            top.transitionOnNextPause(top.getClass(), klass,
63                    StateTransitionAnimation.Transition.Incoming);
64            if (mIsResumed) top.onPause();
65        }
66        UsageStatistics.onContentViewChanged(
67                UsageStatistics.COMPONENT_GALLERY,
68                klass.getSimpleName());
69        state.initialize(mActivity, data);
70
71        mStack.push(new StateEntry(data, state));
72        state.onCreate(data, null);
73        if (mIsResumed) state.resume();
74    }
75
76    public void startStateForResult(Class<? extends ActivityState> klass,
77            int requestCode, Bundle data) {
78        Log.v(TAG, "startStateForResult " + klass + ", " + requestCode);
79        ActivityState state = null;
80        try {
81            state = klass.newInstance();
82        } catch (Exception e) {
83            throw new AssertionError(e);
84        }
85        state.initialize(mActivity, data);
86        state.mResult = new ActivityState.ResultEntry();
87        state.mResult.requestCode = requestCode;
88
89        if (!mStack.isEmpty()) {
90            ActivityState as = getTopState();
91            as.transitionOnNextPause(as.getClass(), klass,
92                    StateTransitionAnimation.Transition.Incoming);
93            as.mReceivedResults = state.mResult;
94            if (mIsResumed) as.onPause();
95        } else {
96            mResult = state.mResult;
97        }
98        UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_GALLERY,
99                klass.getSimpleName());
100        mStack.push(new StateEntry(data, state));
101        state.onCreate(data, null);
102        if (mIsResumed) state.resume();
103    }
104
105    public boolean createOptionsMenu(Menu menu) {
106        if (mStack.isEmpty()) {
107            return false;
108        } else {
109            return getTopState().onCreateActionBar(menu);
110        }
111    }
112
113    public void onConfigurationChange(Configuration config) {
114        for (StateEntry entry : mStack) {
115            entry.activityState.onConfigurationChanged(config);
116        }
117    }
118
119    public void resume() {
120        if (mIsResumed) return;
121        mIsResumed = true;
122        if (!mStack.isEmpty()) getTopState().resume();
123    }
124
125    public void pause() {
126        if (!mIsResumed) return;
127        mIsResumed = false;
128        if (!mStack.isEmpty()) getTopState().onPause();
129    }
130
131    public void notifyActivityResult(int requestCode, int resultCode, Intent data) {
132        getTopState().onStateResult(requestCode, resultCode, data);
133    }
134
135    public void clearActivityResult() {
136        if (!mStack.isEmpty()) {
137            getTopState().clearStateResult();
138        }
139    }
140
141    public int getStateCount() {
142        return mStack.size();
143    }
144
145    public boolean itemSelected(MenuItem item) {
146        if (!mStack.isEmpty()) {
147            if (getTopState().onItemSelected(item)) return true;
148            if (item.getItemId() == android.R.id.home) {
149                if (mStack.size() > 1) {
150                    getTopState().onBackPressed();
151                }
152                return true;
153            }
154        }
155        return false;
156    }
157
158    public void onBackPressed() {
159        if (!mStack.isEmpty()) {
160            getTopState().onBackPressed();
161        }
162    }
163
164    void finishState(ActivityState state) {
165        finishState(state, true);
166    }
167
168    public void clearTasks() {
169        // Remove all the states that are on top of the bottom PhotoPage state
170        while (mStack.size() > 1) {
171            mStack.pop().activityState.onDestroy();
172        }
173    }
174
175    void finishState(ActivityState state, boolean fireOnPause) {
176        // The finish() request could be rejected (only happens under Monkey),
177        // If it is rejected, we won't close the last page.
178        if (mStack.size() == 1) {
179            Activity activity = (Activity) mActivity.getAndroidContext();
180            if (mResult != null) {
181                activity.setResult(mResult.resultCode, mResult.resultData);
182            }
183            activity.finish();
184            if (!activity.isFinishing()) {
185                Log.w(TAG, "finish is rejected, keep the last state");
186                return;
187            }
188            Log.v(TAG, "no more state, finish activity");
189        }
190
191        Log.v(TAG, "finishState " + state);
192        if (state != mStack.peek().activityState) {
193            if (state.isDestroyed()) {
194                Log.d(TAG, "The state is already destroyed");
195                return;
196            } else {
197                throw new IllegalArgumentException("The stateview to be finished"
198                        + " is not at the top of the stack: " + state + ", "
199                        + mStack.peek().activityState);
200            }
201        }
202
203        // Remove the top state.
204        mStack.pop();
205        state.mIsFinishing = true;
206        ActivityState top = !mStack.isEmpty() ? mStack.peek().activityState : null;
207        if (mIsResumed && fireOnPause) {
208            if (top != null) {
209                state.transitionOnNextPause(state.getClass(), top.getClass(),
210                        StateTransitionAnimation.Transition.Outgoing);
211            }
212            state.onPause();
213        }
214        mActivity.getGLRoot().setContentPane(null);
215        state.onDestroy();
216
217        if (top != null && mIsResumed) top.resume();
218        if (top != null) {
219            UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_GALLERY,
220                    top.getClass().getSimpleName());
221        }
222    }
223
224    public void switchState(ActivityState oldState,
225            Class<? extends ActivityState> klass, Bundle data) {
226        Log.v(TAG, "switchState " + oldState + ", " + klass);
227        if (oldState != mStack.peek().activityState) {
228            throw new IllegalArgumentException("The stateview to be finished"
229                    + " is not at the top of the stack: " + oldState + ", "
230                    + mStack.peek().activityState);
231        }
232        // Remove the top state.
233        mStack.pop();
234        if (!data.containsKey(PhotoPage.KEY_APP_BRIDGE)) {
235            // Do not do the fade out stuff when we are switching camera modes
236            oldState.transitionOnNextPause(oldState.getClass(), klass,
237                    StateTransitionAnimation.Transition.Incoming);
238        }
239        if (mIsResumed) oldState.onPause();
240        oldState.onDestroy();
241
242        // Create new state.
243        ActivityState state = null;
244        try {
245            state = klass.newInstance();
246        } catch (Exception e) {
247            throw new AssertionError(e);
248        }
249        state.initialize(mActivity, data);
250        mStack.push(new StateEntry(data, state));
251        state.onCreate(data, null);
252        if (mIsResumed) state.resume();
253        UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_GALLERY,
254                klass.getSimpleName());
255    }
256
257    public void destroy() {
258        Log.v(TAG, "destroy");
259        while (!mStack.isEmpty()) {
260            mStack.pop().activityState.onDestroy();
261        }
262        mStack.clear();
263    }
264
265    @SuppressWarnings("unchecked")
266    public void restoreFromState(Bundle inState) {
267        Log.v(TAG, "restoreFromState");
268        Parcelable list[] = inState.getParcelableArray(KEY_MAIN);
269        ActivityState topState = null;
270        for (Parcelable parcelable : list) {
271            Bundle bundle = (Bundle) parcelable;
272            Class<? extends ActivityState> klass =
273                    (Class<? extends ActivityState>) bundle.getSerializable(KEY_CLASS);
274
275            Bundle data = bundle.getBundle(KEY_DATA);
276            Bundle state = bundle.getBundle(KEY_STATE);
277
278            ActivityState activityState;
279            try {
280                Log.v(TAG, "restoreFromState " + klass);
281                activityState = klass.newInstance();
282            } catch (Exception e) {
283                throw new AssertionError(e);
284            }
285            activityState.initialize(mActivity, data);
286            activityState.onCreate(data, state);
287            mStack.push(new StateEntry(data, activityState));
288            topState = activityState;
289        }
290        if (topState != null) {
291            UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_GALLERY,
292                    topState.getClass().getSimpleName());
293        }
294    }
295
296    public void saveState(Bundle outState) {
297        Log.v(TAG, "saveState");
298
299        Parcelable list[] = new Parcelable[mStack.size()];
300        int i = 0;
301        for (StateEntry entry : mStack) {
302            Bundle bundle = new Bundle();
303            bundle.putSerializable(KEY_CLASS, entry.activityState.getClass());
304            bundle.putBundle(KEY_DATA, entry.data);
305            Bundle state = new Bundle();
306            entry.activityState.onSaveState(state);
307            bundle.putBundle(KEY_STATE, state);
308            Log.v(TAG, "saveState " + entry.activityState.getClass());
309            list[i++] = bundle;
310        }
311        outState.putParcelableArray(KEY_MAIN, list);
312    }
313
314    public boolean hasStateClass(Class<? extends ActivityState> klass) {
315        for (StateEntry entry : mStack) {
316            if (klass.isInstance(entry.activityState)) {
317                return true;
318            }
319        }
320        return false;
321    }
322
323    public ActivityState getTopState() {
324        Utils.assertTrue(!mStack.isEmpty());
325        return mStack.peek().activityState;
326    }
327
328    private static class StateEntry {
329        public Bundle data;
330        public ActivityState activityState;
331
332        public StateEntry(Bundle data, ActivityState state) {
333            this.data = data;
334            this.activityState = state;
335        }
336    }
337}
338