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