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