Home | History | Annotate | Download | only in app
      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 
     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         // 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 
    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 
    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")
    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 
    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 
    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 
    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 
    285         public StateEntry(Bundle data, ActivityState state) {
    286             this.data = data;
    287             this.activityState = state;
    288         }
    289     }
    290 }
    291