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