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 17 package com.android.gallery3d.app; 18 19 import android.app.Activity; 20 import android.content.Intent; 21 import android.content.res.Configuration; 22 import android.os.Bundle; 23 import android.os.Parcelable; 24 import android.view.Menu; 25 import android.view.MenuItem; 26 27 import com.android.gallery3d.common.Utils; 28 29 import java.util.Stack; 30 31 public 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 StateManager(GalleryActivity context)45 public StateManager(GalleryActivity context) { 46 mContext = context; 47 } 48 startState(Class<? extends ActivityState> klass, Bundle data)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 startStateForResult(Class<? extends ActivityState> klass, int requestCode, Bundle data)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 createOptionsMenu(Menu menu)95 public boolean createOptionsMenu(Menu menu) { 96 if (mStack.isEmpty()) { 97 return false; 98 } else { 99 return getTopState().onCreateActionBar(menu); 100 } 101 } 102 onConfigurationChange(Configuration config)103 public void onConfigurationChange(Configuration config) { 104 for (StateEntry entry : mStack) { 105 entry.activityState.onConfigurationChanged(config); 106 } 107 } 108 resume()109 public void resume() { 110 if (mIsResumed) return; 111 mIsResumed = true; 112 if (!mStack.isEmpty()) getTopState().resume(); 113 } 114 pause()115 public void pause() { 116 if (!mIsResumed) return; 117 mIsResumed = false; 118 if (!mStack.isEmpty()) getTopState().onPause(); 119 } 120 notifyActivityResult(int requestCode, int resultCode, Intent data)121 public void notifyActivityResult(int requestCode, int resultCode, Intent data) { 122 getTopState().onStateResult(requestCode, resultCode, data); 123 } 124 getStateCount()125 public int getStateCount() { 126 return mStack.size(); 127 } 128 itemSelected(MenuItem item)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 onBackPressed()142 public void onBackPressed() { 143 if (!mStack.isEmpty()) { 144 getTopState().onBackPressed(); 145 } 146 } 147 finishState(ActivityState state)148 void finishState(ActivityState state) { 149 // The finish() request could be rejected (only happens under Monkey), 150 // If it is rejected, we won't close the last page. 151 if (mStack.size() == 1) { 152 Activity activity = (Activity) mContext.getAndroidContext(); 153 if (mResult != null) { 154 activity.setResult(mResult.resultCode, mResult.resultData); 155 } 156 activity.finish(); 157 if (!activity.isFinishing()) { 158 Log.w(TAG, "finish is rejected, keep the last state"); 159 return; 160 } 161 Log.v(TAG, "no more state, finish activity"); 162 } 163 164 Log.v(TAG, "finishState " + state); 165 if (state != mStack.peek().activityState) { 166 if (state.isDestroyed()) { 167 Log.d(TAG, "The state is already destroyed"); 168 return; 169 } else { 170 throw new IllegalArgumentException("The stateview to be finished" 171 + " is not at the top of the stack: " + state + ", " 172 + mStack.peek().activityState); 173 } 174 } 175 176 // Remove the top state. 177 mStack.pop(); 178 state.mIsFinishing = true; 179 if (mIsResumed) state.onPause(); 180 mContext.getGLRoot().setContentPane(null); 181 state.onDestroy(); 182 183 if (!mStack.isEmpty()) { 184 // Restore the immediately previous state 185 ActivityState top = mStack.peek().activityState; 186 if (mIsResumed) top.resume(); 187 } 188 } 189 switchState(ActivityState oldState, Class<? extends ActivityState> klass, Bundle data)190 void switchState(ActivityState oldState, 191 Class<? extends ActivityState> klass, Bundle data) { 192 Log.v(TAG, "switchState " + oldState + ", " + klass); 193 if (oldState != mStack.peek().activityState) { 194 throw new IllegalArgumentException("The stateview to be finished" 195 + " is not at the top of the stack: " + oldState + ", " 196 + mStack.peek().activityState); 197 } 198 // Remove the top state. 199 mStack.pop(); 200 if (mIsResumed) oldState.onPause(); 201 oldState.onDestroy(); 202 203 // Create new state. 204 ActivityState state = null; 205 try { 206 state = klass.newInstance(); 207 } catch (Exception e) { 208 throw new AssertionError(e); 209 } 210 state.initialize(mContext, data); 211 mStack.push(new StateEntry(data, state)); 212 state.onCreate(data, null); 213 if (mIsResumed) state.resume(); 214 } 215 destroy()216 public void destroy() { 217 Log.v(TAG, "destroy"); 218 while (!mStack.isEmpty()) { 219 mStack.pop().activityState.onDestroy(); 220 } 221 mStack.clear(); 222 } 223 224 @SuppressWarnings("unchecked") restoreFromState(Bundle inState)225 public void restoreFromState(Bundle inState) { 226 Log.v(TAG, "restoreFromState"); 227 Parcelable list[] = inState.getParcelableArray(KEY_MAIN); 228 for (Parcelable parcelable : list) { 229 Bundle bundle = (Bundle) parcelable; 230 Class<? extends ActivityState> klass = 231 (Class<? extends ActivityState>) bundle.getSerializable(KEY_CLASS); 232 233 Bundle data = bundle.getBundle(KEY_DATA); 234 Bundle state = bundle.getBundle(KEY_STATE); 235 236 ActivityState activityState; 237 try { 238 Log.v(TAG, "restoreFromState " + klass); 239 activityState = klass.newInstance(); 240 } catch (Exception e) { 241 throw new AssertionError(e); 242 } 243 activityState.initialize(mContext, data); 244 activityState.onCreate(data, state); 245 mStack.push(new StateEntry(data, activityState)); 246 } 247 } 248 saveState(Bundle outState)249 public void saveState(Bundle outState) { 250 Log.v(TAG, "saveState"); 251 252 Parcelable list[] = new Parcelable[mStack.size()]; 253 int i = 0; 254 for (StateEntry entry : mStack) { 255 Bundle bundle = new Bundle(); 256 bundle.putSerializable(KEY_CLASS, entry.activityState.getClass()); 257 bundle.putBundle(KEY_DATA, entry.data); 258 Bundle state = new Bundle(); 259 entry.activityState.onSaveState(state); 260 bundle.putBundle(KEY_STATE, state); 261 Log.v(TAG, "saveState " + entry.activityState.getClass()); 262 list[i++] = bundle; 263 } 264 outState.putParcelableArray(KEY_MAIN, list); 265 } 266 hasStateClass(Class<? extends ActivityState> klass)267 public boolean hasStateClass(Class<? extends ActivityState> klass) { 268 for (StateEntry entry : mStack) { 269 if (klass.isInstance(entry.activityState)) { 270 return true; 271 } 272 } 273 return false; 274 } 275 getTopState()276 public ActivityState getTopState() { 277 Utils.assertTrue(!mStack.isEmpty()); 278 return mStack.peek().activityState; 279 } 280 281 private static class StateEntry { 282 public Bundle data; 283 public ActivityState activityState; 284 StateEntry(Bundle data, ActivityState state)285 public StateEntry(Bundle data, ActivityState state) { 286 this.data = data; 287 this.activityState = state; 288 } 289 } 290 } 291