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.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