StateManager.java revision 4b69c5f52e8ef3209bc0b6889c4a6355614607ca
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.common.Utils;
28
29import java.util.Stack;
30
31public class StateManager {
32    @SuppressWarnings("unused")
33    private static final String TAG = "StateManager";
34    private boolean mIsResumed = false;
35
36    private static final String KEY_MAIN = "activity-state";
37    private static final String KEY_DATA = "data";
38    private static final String KEY_STATE = "bundle";
39    private static final String KEY_CLASS = "class";
40
41    private GalleryActivity mContext;
42    private Stack<StateEntry> mStack = new Stack<StateEntry>();
43    private ActivityState.ResultEntry mResult;
44
45    public StateManager(GalleryActivity context) {
46        mContext = context;
47    }
48
49    public void startState(Class<? extends ActivityState> klass,
50            Bundle data) {
51        Log.v(TAG, "startState " + klass);
52        ActivityState state = null;
53        try {
54            state = klass.newInstance();
55        } catch (Exception e) {
56            throw new AssertionError(e);
57        }
58        if (!mStack.isEmpty()) {
59            ActivityState top = getTopState();
60            if (mIsResumed) top.onPause();
61        }
62        state.initialize(mContext, data);
63
64        mStack.push(new StateEntry(data, state));
65        state.onCreate(data, null);
66        if (mIsResumed) state.resume();
67    }
68
69    public void startStateForResult(Class<? extends ActivityState> klass,
70            int requestCode, Bundle data) {
71        Log.v(TAG, "startStateForResult " + klass + ", " + requestCode);
72        ActivityState state = null;
73        try {
74            state = klass.newInstance();
75        } catch (Exception e) {
76            throw new AssertionError(e);
77        }
78        state.initialize(mContext, data);
79        state.mResult = new ActivityState.ResultEntry();
80        state.mResult.requestCode = requestCode;
81
82        if (!mStack.isEmpty()) {
83            ActivityState as = getTopState();
84            as.mReceivedResults = state.mResult;
85            if (mIsResumed) as.onPause();
86        } else {
87            mResult = state.mResult;
88        }
89
90        mStack.push(new StateEntry(data, state));
91        state.onCreate(data, null);
92        if (mIsResumed) state.resume();
93    }
94
95    public boolean createOptionsMenu(Menu menu) {
96        if (mStack.isEmpty()) {
97            return false;
98        } else {
99            return getTopState().onCreateActionBar(menu);
100        }
101    }
102
103    public void onConfigurationChange(Configuration config) {
104        for (StateEntry entry : mStack) {
105            entry.activityState.onConfigurationChanged(config);
106        }
107    }
108
109    public void resume() {
110        if (mIsResumed) return;
111        mIsResumed = true;
112        if (!mStack.isEmpty()) getTopState().resume();
113    }
114
115    public void pause() {
116        if (!mIsResumed) return;
117        mIsResumed = false;
118        if (!mStack.isEmpty()) getTopState().onPause();
119    }
120
121    public void notifyActivityResult(int requestCode, int resultCode, Intent data) {
122        getTopState().onStateResult(requestCode, resultCode, data);
123    }
124
125    public int getStateCount() {
126        return mStack.size();
127    }
128
129    public boolean itemSelected(MenuItem item) {
130        if (!mStack.isEmpty()) {
131            if (getTopState().onItemSelected(item)) return true;
132            if (item.getItemId() == android.R.id.home) {
133                if (mStack.size() > 1) {
134                    getTopState().onBackPressed();
135                }
136                return true;
137            }
138        }
139        return false;
140    }
141
142    public void onBackPressed() {
143        if (!mStack.isEmpty()) {
144            getTopState().onBackPressed();
145        }
146    }
147
148    void finishState(ActivityState state) {
149        Log.v(TAG, "finishState " + state);
150        if (state != mStack.peek().activityState) {
151            if (state.isDestroyed()) {
152                Log.d(TAG, "The state is already destroyed");
153                return;
154            } else {
155                throw new IllegalArgumentException("The stateview to be finished"
156                        + " is not at the top of the stack: " + state + ", "
157                        + mStack.peek().activityState);
158            }
159        }
160
161        // Remove the top state.
162        mStack.pop();
163        if (mIsResumed) state.onPause();
164        mContext.getGLRoot().setContentPane(null);
165        state.onDestroy();
166
167        if (mStack.isEmpty()) {
168            Log.v(TAG, "no more state, finish activity");
169            Activity activity = (Activity) mContext.getAndroidContext();
170            if (mResult != null) {
171                activity.setResult(mResult.resultCode, mResult.resultData);
172            }
173            activity.finish();
174
175            // The finish() request is rejected (only happens under Monkey),
176            // so we start the default page instead.
177            if (!activity.isFinishing()) {
178                Log.v(TAG, "finish() failed, start default page");
179                ((Gallery) mContext).startDefaultPage();
180            }
181        } else {
182            // Restore the immediately previous state
183            ActivityState top = mStack.peek().activityState;
184            if (mIsResumed) top.resume();
185        }
186    }
187
188    void switchState(ActivityState oldState,
189            Class<? extends ActivityState> klass, Bundle data) {
190        Log.v(TAG, "switchState " + oldState + ", " + klass);
191        if (oldState != mStack.peek().activityState) {
192            throw new IllegalArgumentException("The stateview to be finished"
193                    + " is not at the top of the stack: " + oldState + ", "
194                    + mStack.peek().activityState);
195        }
196        // Remove the top state.
197        mStack.pop();
198        if (mIsResumed) oldState.onPause();
199        oldState.onDestroy();
200
201        // Create new state.
202        ActivityState state = null;
203        try {
204            state = klass.newInstance();
205        } catch (Exception e) {
206            throw new AssertionError(e);
207        }
208        state.initialize(mContext, data);
209        mStack.push(new StateEntry(data, state));
210        state.onCreate(data, null);
211        if (mIsResumed) state.resume();
212    }
213
214    public void destroy() {
215        Log.v(TAG, "destroy");
216        while (!mStack.isEmpty()) {
217            mStack.pop().activityState.onDestroy();
218        }
219        mStack.clear();
220    }
221
222    @SuppressWarnings("unchecked")
223    public void restoreFromState(Bundle inState) {
224        Log.v(TAG, "restoreFromState");
225        Parcelable list[] = inState.getParcelableArray(KEY_MAIN);
226        for (Parcelable parcelable : list) {
227            Bundle bundle = (Bundle) parcelable;
228            Class<? extends ActivityState> klass =
229                    (Class<? extends ActivityState>) bundle.getSerializable(KEY_CLASS);
230
231            Bundle data = bundle.getBundle(KEY_DATA);
232            Bundle state = bundle.getBundle(KEY_STATE);
233
234            ActivityState activityState;
235            try {
236                Log.v(TAG, "restoreFromState " + klass);
237                activityState = klass.newInstance();
238            } catch (Exception e) {
239                throw new AssertionError(e);
240            }
241            activityState.initialize(mContext, data);
242            activityState.onCreate(data, state);
243            mStack.push(new StateEntry(data, activityState));
244        }
245    }
246
247    public void saveState(Bundle outState) {
248        Log.v(TAG, "saveState");
249
250        Parcelable list[] = new Parcelable[mStack.size()];
251        int i = 0;
252        for (StateEntry entry : mStack) {
253            Bundle bundle = new Bundle();
254            bundle.putSerializable(KEY_CLASS, entry.activityState.getClass());
255            bundle.putBundle(KEY_DATA, entry.data);
256            Bundle state = new Bundle();
257            entry.activityState.onSaveState(state);
258            bundle.putBundle(KEY_STATE, state);
259            Log.v(TAG, "saveState " + entry.activityState.getClass());
260            list[i++] = bundle;
261        }
262        outState.putParcelableArray(KEY_MAIN, list);
263    }
264
265    public boolean hasStateClass(Class<? extends ActivityState> klass) {
266        for (StateEntry entry : mStack) {
267            if (klass.isInstance(entry.activityState)) {
268                return true;
269            }
270        }
271        return false;
272    }
273
274    public ActivityState getTopState() {
275        Utils.assertTrue(!mStack.isEmpty());
276        return mStack.peek().activityState;
277    }
278
279    private static class StateEntry {
280        public Bundle data;
281        public ActivityState activityState;
282
283        public StateEntry(Bundle data, ActivityState state) {
284            this.data = data;
285            this.activityState = state;
286        }
287    }
288}
289