Home | History | Annotate | Download | only in stateful
      1 /*
      2  * Copyright (C) 2015 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.camera.captureintent.stateful;
     18 
     19 import com.google.common.base.Optional;
     20 
     21 import com.android.camera.debug.Log;
     22 
     23 import java.util.concurrent.locks.Condition;
     24 import java.util.concurrent.locks.ReentrantLock;
     25 
     26 import javax.annotation.Nonnull;
     27 
     28 public class StateMachineImpl implements StateMachine {
     29     private static final Log.Tag TAG = new Log.Tag("StateMachine");
     30 
     31     /** The current state. */
     32     private State mState;
     33 
     34     /** The lock to protect mState. */
     35     private final ReentrantLock mStateLock;
     36 
     37     /** The condition to synchronize state changed event. */
     38     private final Condition mStateChangedCondition;
     39 
     40     public StateMachineImpl() {
     41         mStateLock = new ReentrantLock();
     42         mStateChangedCondition = mStateLock.newCondition();
     43         mState = new StateUninitialized(this);
     44     }
     45 
     46     /**
     47      * Jumps directly to a specific state.
     48      *
     49      * @param newState The new state.
     50      */
     51     public void jumpToState(@Nonnull State newState) {
     52         mStateLock.lock();
     53         try {
     54             if (newState.equals(mState)) {
     55                 Log.d(TAG, "No op since jump to the same state.");
     56             } else {
     57                 // While changing to a particular state, execute its onEnter() hook
     58                 // and keep forwarding to new states if necessary.
     59                 Log.d(TAG, "Change state : " + mState + " => " + newState);
     60                 mState.onLeave();
     61                 mState = newState;
     62                 Optional<State> nextState = mState.onEnter();
     63                 while (nextState.isPresent()) {
     64                     Log.d(TAG, "Forward state : " + mState + " => " + nextState.get());
     65                     mState.onLeave();
     66                     mState = nextState.get();
     67                     nextState = mState.onEnter();
     68                 }
     69 
     70                 mStateChangedCondition.signalAll();
     71             }
     72         } finally {
     73             mStateLock.unlock();
     74         }
     75     }
     76 
     77     @Override
     78     public State getCurrentState() {
     79         mStateLock.lock();
     80         try {
     81             return mState;
     82         } finally {
     83             mStateLock.unlock();
     84         }
     85     }
     86 
     87     @Override
     88     public boolean setInitialState(State initialState) {
     89         mStateLock.lock();
     90         try {
     91             if (!(mState instanceof StateUninitialized)) {
     92                 return false;
     93             }
     94             jumpToState(initialState);
     95             return true;
     96         } finally {
     97             mStateLock.unlock();
     98         }
     99     }
    100 
    101     @Override
    102     public void processEvent(Event event) {
    103         mStateLock.lock();
    104         try {
    105             EventHandler eventHandler = mState.getEventHandler(event.getClass());
    106             if (eventHandler != null) {
    107                 Log.d(TAG, "Process event : " + event);
    108                 Optional<State> newState = eventHandler.processEvent(event);
    109                 if (newState.isPresent()) {
    110                     jumpToState(newState.get());
    111                 }
    112             }
    113         } catch (Exception ex) {
    114             Log.e(TAG, "Failed to process event: " + event);
    115             throw ex;
    116         } finally {
    117             mStateLock.unlock();
    118         }
    119     }
    120 
    121     /**
    122      * The initial state of the state machine.
    123      */
    124     public static class StateUninitialized extends StateImpl {
    125         public StateUninitialized(StateMachine stateMachine) {
    126             super(stateMachine);
    127         }
    128     }
    129 }
    130