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