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 StateMachineImpl()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 */ jumpToState(@onnull State newState)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 getCurrentState()78 public State getCurrentState() { 79 mStateLock.lock(); 80 try { 81 return mState; 82 } finally { 83 mStateLock.unlock(); 84 } 85 } 86 87 @Override setInitialState(State initialState)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 processEvent(Event event)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 { StateUninitialized(StateMachine stateMachine)125 public StateUninitialized(StateMachine stateMachine) { 126 super(stateMachine); 127 } 128 } 129 } 130