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