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