1a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu/*
2a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu * Copyright (C) 2014 The Android Open Source Project
3a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu *
4a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu * in compliance with the License. You may obtain a copy of the License at
6a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu *
7a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu * http://www.apache.org/licenses/LICENSE-2.0
8a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu *
9a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu * Unless required by applicable law or agreed to in writing, software distributed under the License
10a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu * or implied. See the License for the specific language governing permissions and limitations under
12a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu * the License.
13a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu */
14a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gupackage android.support.v17.leanback.util;
15a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
168e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
178e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas
18c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo;
1989097f67f988ebba714a95e10369665280db0c27Dake Guimport android.util.Log;
20c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette
21a5a85434f936023043f074fb86eaa6d48f7f6411Dake Guimport java.util.ArrayList;
22a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
23a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu/**
2489097f67f988ebba714a95e10369665280db0c27Dake Gu * State: each State has incoming Transitions and outgoing Transitions.
2589097f67f988ebba714a95e10369665280db0c27Dake Gu * When {@link State#mBranchStart} is true, all the outgoing Transitions may be triggered, when
2689097f67f988ebba714a95e10369665280db0c27Dake Gu * {@link State#mBranchStart} is false, only first outgoing Transition will be triggered.
2789097f67f988ebba714a95e10369665280db0c27Dake Gu * When {@link State#mBranchEnd} is true, all the incoming Transitions must be triggered for the
2889097f67f988ebba714a95e10369665280db0c27Dake Gu * State to run. When {@link State#mBranchEnd} is false, only need one incoming Transition triggered
2989097f67f988ebba714a95e10369665280db0c27Dake Gu * for the State to run.
3089097f67f988ebba714a95e10369665280db0c27Dake Gu * Transition: three types:
3189097f67f988ebba714a95e10369665280db0c27Dake Gu * 1. Event based transition, transition will be triggered when {@link #fireEvent(Event)} is called.
3289097f67f988ebba714a95e10369665280db0c27Dake Gu * 2. Auto transition, transition will be triggered when {@link Transition#mFromState} is executed.
3389097f67f988ebba714a95e10369665280db0c27Dake Gu * 3. Condiitonal Auto transition, transition will be triggered when {@link Transition#mFromState}
3489097f67f988ebba714a95e10369665280db0c27Dake Gu * is executed and {@link Transition#mCondition} passes.
35a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu * @hide
36a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu */
378e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas@RestrictTo(LIBRARY_GROUP)
38a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gupublic final class StateMachine {
39a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
4089097f67f988ebba714a95e10369665280db0c27Dake Gu    static boolean DEBUG = false;
4189097f67f988ebba714a95e10369665280db0c27Dake Gu    static final String TAG = "StateMachine";
4289097f67f988ebba714a95e10369665280db0c27Dake Gu
43a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    /**
44a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu     * No request on the State
45a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu     */
46a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    public static final int STATUS_ZERO = 0;
4789097f67f988ebba714a95e10369665280db0c27Dake Gu
48a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    /**
4989097f67f988ebba714a95e10369665280db0c27Dake Gu     * Has been executed
50a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu     */
51a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    public static final int STATUS_INVOKED = 1;
5289097f67f988ebba714a95e10369665280db0c27Dake Gu
53a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    /**
5489097f67f988ebba714a95e10369665280db0c27Dake Gu     * Used in Transition
55a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu     */
5689097f67f988ebba714a95e10369665280db0c27Dake Gu    public static class Event {
5789097f67f988ebba714a95e10369665280db0c27Dake Gu        final String mName;
58a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
5989097f67f988ebba714a95e10369665280db0c27Dake Gu        public Event(String name) {
6089097f67f988ebba714a95e10369665280db0c27Dake Gu            mName = name;
6189097f67f988ebba714a95e10369665280db0c27Dake Gu        }
6289097f67f988ebba714a95e10369665280db0c27Dake Gu    }
6389097f67f988ebba714a95e10369665280db0c27Dake Gu
6489097f67f988ebba714a95e10369665280db0c27Dake Gu    /**
6589097f67f988ebba714a95e10369665280db0c27Dake Gu     * Used in transition
6689097f67f988ebba714a95e10369665280db0c27Dake Gu     */
6789097f67f988ebba714a95e10369665280db0c27Dake Gu    public static class Condition {
6889097f67f988ebba714a95e10369665280db0c27Dake Gu        final String mName;
69a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
7089097f67f988ebba714a95e10369665280db0c27Dake Gu        public Condition(String name) {
7189097f67f988ebba714a95e10369665280db0c27Dake Gu            mName = name;
7289097f67f988ebba714a95e10369665280db0c27Dake Gu        }
73a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
74a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        /**
7589097f67f988ebba714a95e10369665280db0c27Dake Gu         * @return True if can proceed and mark the transition INVOKED
76a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu         */
7789097f67f988ebba714a95e10369665280db0c27Dake Gu        public boolean canProceed() {
7889097f67f988ebba714a95e10369665280db0c27Dake Gu            return true;
7989097f67f988ebba714a95e10369665280db0c27Dake Gu        }
8089097f67f988ebba714a95e10369665280db0c27Dake Gu    }
8189097f67f988ebba714a95e10369665280db0c27Dake Gu
8289097f67f988ebba714a95e10369665280db0c27Dake Gu    static class Transition {
8389097f67f988ebba714a95e10369665280db0c27Dake Gu        final State mFromState;
8489097f67f988ebba714a95e10369665280db0c27Dake Gu        final State mToState;
8589097f67f988ebba714a95e10369665280db0c27Dake Gu        final Event mEvent;
8689097f67f988ebba714a95e10369665280db0c27Dake Gu        final Condition mCondition;
8789097f67f988ebba714a95e10369665280db0c27Dake Gu        int mState = STATUS_ZERO;
8889097f67f988ebba714a95e10369665280db0c27Dake Gu
8989097f67f988ebba714a95e10369665280db0c27Dake Gu        Transition(State fromState, State toState, Event event) {
9089097f67f988ebba714a95e10369665280db0c27Dake Gu            if (event == null) {
9189097f67f988ebba714a95e10369665280db0c27Dake Gu                throw new IllegalArgumentException();
9289097f67f988ebba714a95e10369665280db0c27Dake Gu            }
9389097f67f988ebba714a95e10369665280db0c27Dake Gu            mFromState = fromState;
9489097f67f988ebba714a95e10369665280db0c27Dake Gu            mToState = toState;
9589097f67f988ebba714a95e10369665280db0c27Dake Gu            mEvent = event;
9689097f67f988ebba714a95e10369665280db0c27Dake Gu            mCondition = null;
9789097f67f988ebba714a95e10369665280db0c27Dake Gu        }
9889097f67f988ebba714a95e10369665280db0c27Dake Gu
9989097f67f988ebba714a95e10369665280db0c27Dake Gu        Transition(State fromState, State toState) {
10089097f67f988ebba714a95e10369665280db0c27Dake Gu            mFromState = fromState;
10189097f67f988ebba714a95e10369665280db0c27Dake Gu            mToState = toState;
10289097f67f988ebba714a95e10369665280db0c27Dake Gu            mEvent = null;
10389097f67f988ebba714a95e10369665280db0c27Dake Gu            mCondition = null;
10489097f67f988ebba714a95e10369665280db0c27Dake Gu        }
10589097f67f988ebba714a95e10369665280db0c27Dake Gu
10689097f67f988ebba714a95e10369665280db0c27Dake Gu        Transition(State fromState, State toState, Condition condition) {
10789097f67f988ebba714a95e10369665280db0c27Dake Gu            if (condition == null) {
10889097f67f988ebba714a95e10369665280db0c27Dake Gu                throw new IllegalArgumentException();
10989097f67f988ebba714a95e10369665280db0c27Dake Gu            }
11089097f67f988ebba714a95e10369665280db0c27Dake Gu            mFromState = fromState;
11189097f67f988ebba714a95e10369665280db0c27Dake Gu            mToState = toState;
11289097f67f988ebba714a95e10369665280db0c27Dake Gu            mEvent = null;
11389097f67f988ebba714a95e10369665280db0c27Dake Gu            mCondition = condition;
11489097f67f988ebba714a95e10369665280db0c27Dake Gu        }
11589097f67f988ebba714a95e10369665280db0c27Dake Gu
11689097f67f988ebba714a95e10369665280db0c27Dake Gu        @Override
11789097f67f988ebba714a95e10369665280db0c27Dake Gu        public String toString() {
11889097f67f988ebba714a95e10369665280db0c27Dake Gu            String signalName;
11989097f67f988ebba714a95e10369665280db0c27Dake Gu            if (mEvent != null) {
12089097f67f988ebba714a95e10369665280db0c27Dake Gu                signalName = mEvent.mName;
12189097f67f988ebba714a95e10369665280db0c27Dake Gu            } else if (mCondition != null) {
12289097f67f988ebba714a95e10369665280db0c27Dake Gu                signalName = mCondition.mName;
12389097f67f988ebba714a95e10369665280db0c27Dake Gu            } else {
12489097f67f988ebba714a95e10369665280db0c27Dake Gu                signalName = "auto";
12589097f67f988ebba714a95e10369665280db0c27Dake Gu            }
12689097f67f988ebba714a95e10369665280db0c27Dake Gu            return "[" + mFromState.mName + " -> " + mToState.mName + " <"
12789097f67f988ebba714a95e10369665280db0c27Dake Gu                    + signalName + ">]";
12889097f67f988ebba714a95e10369665280db0c27Dake Gu        }
12989097f67f988ebba714a95e10369665280db0c27Dake Gu    }
13089097f67f988ebba714a95e10369665280db0c27Dake Gu
13189097f67f988ebba714a95e10369665280db0c27Dake Gu    /**
13289097f67f988ebba714a95e10369665280db0c27Dake Gu     * @see StateMachine
13389097f67f988ebba714a95e10369665280db0c27Dake Gu     */
13489097f67f988ebba714a95e10369665280db0c27Dake Gu    public static class State {
13589097f67f988ebba714a95e10369665280db0c27Dake Gu
13689097f67f988ebba714a95e10369665280db0c27Dake Gu        final String mName;
13789097f67f988ebba714a95e10369665280db0c27Dake Gu        final boolean mBranchStart;
13889097f67f988ebba714a95e10369665280db0c27Dake Gu        final boolean mBranchEnd;
13989097f67f988ebba714a95e10369665280db0c27Dake Gu        int mStatus = STATUS_ZERO;
14089097f67f988ebba714a95e10369665280db0c27Dake Gu        int mInvokedOutTransitions = 0;
14189097f67f988ebba714a95e10369665280db0c27Dake Gu        ArrayList<Transition> mIncomings;
14289097f67f988ebba714a95e10369665280db0c27Dake Gu        ArrayList<Transition> mOutgoings;
14389097f67f988ebba714a95e10369665280db0c27Dake Gu
14489097f67f988ebba714a95e10369665280db0c27Dake Gu        @Override
14589097f67f988ebba714a95e10369665280db0c27Dake Gu        public String toString() {
14689097f67f988ebba714a95e10369665280db0c27Dake Gu            return "[" + mName + " " + mStatus + "]";
147a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        }
148a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
149a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        /**
15089097f67f988ebba714a95e10369665280db0c27Dake Gu         * Create a State which is not branch start and a branch end.
151a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu         */
15289097f67f988ebba714a95e10369665280db0c27Dake Gu        public State(String name) {
15389097f67f988ebba714a95e10369665280db0c27Dake Gu            this(name, false, true);
154a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        }
155a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
156a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        /**
15789097f67f988ebba714a95e10369665280db0c27Dake Gu         * Create a State
15889097f67f988ebba714a95e10369665280db0c27Dake Gu         * @param branchStart True if can run all out going transitions or false execute the first
15989097f67f988ebba714a95e10369665280db0c27Dake Gu         *                    out going transition.
16089097f67f988ebba714a95e10369665280db0c27Dake Gu         * @param branchEnd True if wait all incoming transitions executed or false
16189097f67f988ebba714a95e10369665280db0c27Dake Gu         *                              only need one of the transition executed.
162a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu         */
16389097f67f988ebba714a95e10369665280db0c27Dake Gu        public State(String name, boolean branchStart, boolean branchEnd) {
16489097f67f988ebba714a95e10369665280db0c27Dake Gu            mName = name;
16589097f67f988ebba714a95e10369665280db0c27Dake Gu            mBranchStart = branchStart;
16689097f67f988ebba714a95e10369665280db0c27Dake Gu            mBranchEnd = branchEnd;
16789097f67f988ebba714a95e10369665280db0c27Dake Gu        }
16889097f67f988ebba714a95e10369665280db0c27Dake Gu
16989097f67f988ebba714a95e10369665280db0c27Dake Gu        void addIncoming(Transition t) {
17089097f67f988ebba714a95e10369665280db0c27Dake Gu            if (mIncomings == null) {
17189097f67f988ebba714a95e10369665280db0c27Dake Gu                mIncomings = new ArrayList();
172a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu            }
17389097f67f988ebba714a95e10369665280db0c27Dake Gu            mIncomings.add(t);
17489097f67f988ebba714a95e10369665280db0c27Dake Gu        }
17589097f67f988ebba714a95e10369665280db0c27Dake Gu
17689097f67f988ebba714a95e10369665280db0c27Dake Gu        void addOutgoing(Transition t) {
17789097f67f988ebba714a95e10369665280db0c27Dake Gu            if (mOutgoings == null) {
17889097f67f988ebba714a95e10369665280db0c27Dake Gu                mOutgoings = new ArrayList();
17989097f67f988ebba714a95e10369665280db0c27Dake Gu            }
18089097f67f988ebba714a95e10369665280db0c27Dake Gu            mOutgoings.add(t);
18189097f67f988ebba714a95e10369665280db0c27Dake Gu        }
18289097f67f988ebba714a95e10369665280db0c27Dake Gu
18389097f67f988ebba714a95e10369665280db0c27Dake Gu        /**
18489097f67f988ebba714a95e10369665280db0c27Dake Gu         * Run State, Subclass may override.
18589097f67f988ebba714a95e10369665280db0c27Dake Gu         */
18689097f67f988ebba714a95e10369665280db0c27Dake Gu        public void run() {
187a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        }
188a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
18989097f67f988ebba714a95e10369665280db0c27Dake Gu        final boolean checkPreCondition() {
19089097f67f988ebba714a95e10369665280db0c27Dake Gu            if (mIncomings == null) {
19189097f67f988ebba714a95e10369665280db0c27Dake Gu                return true;
192a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu            }
19389097f67f988ebba714a95e10369665280db0c27Dake Gu            if (mBranchEnd) {
19489097f67f988ebba714a95e10369665280db0c27Dake Gu                for (Transition t: mIncomings) {
19589097f67f988ebba714a95e10369665280db0c27Dake Gu                    if (t.mState != STATUS_INVOKED) {
19689097f67f988ebba714a95e10369665280db0c27Dake Gu                        return false;
19789097f67f988ebba714a95e10369665280db0c27Dake Gu                    }
19889097f67f988ebba714a95e10369665280db0c27Dake Gu                }
19989097f67f988ebba714a95e10369665280db0c27Dake Gu                return true;
20089097f67f988ebba714a95e10369665280db0c27Dake Gu            } else {
20189097f67f988ebba714a95e10369665280db0c27Dake Gu                for (Transition t: mIncomings) {
20289097f67f988ebba714a95e10369665280db0c27Dake Gu                    if (t.mState == STATUS_INVOKED) {
20389097f67f988ebba714a95e10369665280db0c27Dake Gu                        return true;
20489097f67f988ebba714a95e10369665280db0c27Dake Gu                    }
20589097f67f988ebba714a95e10369665280db0c27Dake Gu                }
20689097f67f988ebba714a95e10369665280db0c27Dake Gu                return false;
207a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu            }
208a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        }
209a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
21089097f67f988ebba714a95e10369665280db0c27Dake Gu        /**
21189097f67f988ebba714a95e10369665280db0c27Dake Gu         * @return True if the State has been executed.
21289097f67f988ebba714a95e10369665280db0c27Dake Gu         */
21389097f67f988ebba714a95e10369665280db0c27Dake Gu        final boolean runIfNeeded() {
21489097f67f988ebba714a95e10369665280db0c27Dake Gu            if (mStatus != STATUS_INVOKED) {
21589097f67f988ebba714a95e10369665280db0c27Dake Gu                if (checkPreCondition()) {
21689097f67f988ebba714a95e10369665280db0c27Dake Gu                    if (DEBUG) {
21789097f67f988ebba714a95e10369665280db0c27Dake Gu                        Log.d(TAG, "execute " + this);
21889097f67f988ebba714a95e10369665280db0c27Dake Gu                    }
21989097f67f988ebba714a95e10369665280db0c27Dake Gu                    mStatus = STATUS_INVOKED;
22089097f67f988ebba714a95e10369665280db0c27Dake Gu                    run();
22189097f67f988ebba714a95e10369665280db0c27Dake Gu                    signalAutoTransitionsAfterRun();
22289097f67f988ebba714a95e10369665280db0c27Dake Gu                    return true;
22389097f67f988ebba714a95e10369665280db0c27Dake Gu                }
224a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu            }
22589097f67f988ebba714a95e10369665280db0c27Dake Gu            return false;
226a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        }
227a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
22889097f67f988ebba714a95e10369665280db0c27Dake Gu        final void signalAutoTransitionsAfterRun() {
22989097f67f988ebba714a95e10369665280db0c27Dake Gu            if (mOutgoings != null) {
23089097f67f988ebba714a95e10369665280db0c27Dake Gu                for (Transition t: mOutgoings) {
23189097f67f988ebba714a95e10369665280db0c27Dake Gu                    if (t.mEvent == null) {
23289097f67f988ebba714a95e10369665280db0c27Dake Gu                        if (t.mCondition == null || t.mCondition.canProceed()) {
23389097f67f988ebba714a95e10369665280db0c27Dake Gu                            if (DEBUG) {
23489097f67f988ebba714a95e10369665280db0c27Dake Gu                                Log.d(TAG, "signal " + t);
23589097f67f988ebba714a95e10369665280db0c27Dake Gu                            }
23689097f67f988ebba714a95e10369665280db0c27Dake Gu                            mInvokedOutTransitions++;
23789097f67f988ebba714a95e10369665280db0c27Dake Gu                            t.mState = STATUS_INVOKED;
23889097f67f988ebba714a95e10369665280db0c27Dake Gu                            if (!mBranchStart) {
23989097f67f988ebba714a95e10369665280db0c27Dake Gu                                break;
24089097f67f988ebba714a95e10369665280db0c27Dake Gu                            }
24189097f67f988ebba714a95e10369665280db0c27Dake Gu                        }
24289097f67f988ebba714a95e10369665280db0c27Dake Gu                    }
24389097f67f988ebba714a95e10369665280db0c27Dake Gu                }
24489097f67f988ebba714a95e10369665280db0c27Dake Gu            }
245a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        }
246a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
247a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        /**
24889097f67f988ebba714a95e10369665280db0c27Dake Gu         * Get status, return one of {@link #STATUS_ZERO}, {@link #STATUS_INVOKED}.
249a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu         * @return Status of the State.
250a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu         */
251a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        public final int getStatus() {
252a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu            return mStatus;
253a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        }
254a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    }
255a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
25689097f67f988ebba714a95e10369665280db0c27Dake Gu    final ArrayList<State> mStates = new ArrayList<State>();
25789097f67f988ebba714a95e10369665280db0c27Dake Gu    final ArrayList<State> mFinishedStates = new ArrayList();
25889097f67f988ebba714a95e10369665280db0c27Dake Gu    final ArrayList<State> mUnfinishedStates = new ArrayList();
25989097f67f988ebba714a95e10369665280db0c27Dake Gu
26089097f67f988ebba714a95e10369665280db0c27Dake Gu    public StateMachine() {
26189097f67f988ebba714a95e10369665280db0c27Dake Gu    }
262a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
263a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    /**
264a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu     * Add a State to StateMachine, ignore if it is already added.
265a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu     * @param state The state to add.
266a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu     */
267a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    public void addState(State state) {
26889097f67f988ebba714a95e10369665280db0c27Dake Gu        if (!mStates.contains(state)) {
26989097f67f988ebba714a95e10369665280db0c27Dake Gu            mStates.add(state);
270a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        }
271a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    }
272a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
273a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    /**
27489097f67f988ebba714a95e10369665280db0c27Dake Gu     * Add event-triggered transition between two states.
27589097f67f988ebba714a95e10369665280db0c27Dake Gu     * @param fromState The from state.
27689097f67f988ebba714a95e10369665280db0c27Dake Gu     * @param toState The to state.
27789097f67f988ebba714a95e10369665280db0c27Dake Gu     * @param event The event that needed to perform the transition.
278a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu     */
27989097f67f988ebba714a95e10369665280db0c27Dake Gu    public void addTransition(State fromState, State toState, Event event) {
28089097f67f988ebba714a95e10369665280db0c27Dake Gu        Transition transition = new Transition(fromState, toState, event);
28189097f67f988ebba714a95e10369665280db0c27Dake Gu        toState.addIncoming(transition);
28289097f67f988ebba714a95e10369665280db0c27Dake Gu        fromState.addOutgoing(transition);
283a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    }
284a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
28589097f67f988ebba714a95e10369665280db0c27Dake Gu    /**
28689097f67f988ebba714a95e10369665280db0c27Dake Gu     * Add a conditional auto transition between two states.
28789097f67f988ebba714a95e10369665280db0c27Dake Gu     * @param fromState The from state.
28889097f67f988ebba714a95e10369665280db0c27Dake Gu     * @param toState The to state.
28989097f67f988ebba714a95e10369665280db0c27Dake Gu     */
29089097f67f988ebba714a95e10369665280db0c27Dake Gu    public void addTransition(State fromState, State toState, Condition condition) {
29189097f67f988ebba714a95e10369665280db0c27Dake Gu        Transition transition = new Transition(fromState, toState, condition);
29289097f67f988ebba714a95e10369665280db0c27Dake Gu        toState.addIncoming(transition);
29389097f67f988ebba714a95e10369665280db0c27Dake Gu        fromState.addOutgoing(transition);
294a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    }
295a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
29689097f67f988ebba714a95e10369665280db0c27Dake Gu    /**
29789097f67f988ebba714a95e10369665280db0c27Dake Gu     * Add an auto transition between two states.
29889097f67f988ebba714a95e10369665280db0c27Dake Gu     * @param fromState The from state to add.
29989097f67f988ebba714a95e10369665280db0c27Dake Gu     * @param toState The to state to add.
30089097f67f988ebba714a95e10369665280db0c27Dake Gu     */
30189097f67f988ebba714a95e10369665280db0c27Dake Gu    public void addTransition(State fromState, State toState) {
30289097f67f988ebba714a95e10369665280db0c27Dake Gu        Transition transition = new Transition(fromState, toState);
30389097f67f988ebba714a95e10369665280db0c27Dake Gu        toState.addIncoming(transition);
30489097f67f988ebba714a95e10369665280db0c27Dake Gu        fromState.addOutgoing(transition);
305a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    }
306a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
30789097f67f988ebba714a95e10369665280db0c27Dake Gu    /**
30889097f67f988ebba714a95e10369665280db0c27Dake Gu     * Start the state machine.
30989097f67f988ebba714a95e10369665280db0c27Dake Gu     */
31089097f67f988ebba714a95e10369665280db0c27Dake Gu    public void start() {
31189097f67f988ebba714a95e10369665280db0c27Dake Gu        if (DEBUG) {
31289097f67f988ebba714a95e10369665280db0c27Dake Gu            Log.d(TAG, "start");
313a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        }
31489097f67f988ebba714a95e10369665280db0c27Dake Gu        mUnfinishedStates.addAll(mStates);
31589097f67f988ebba714a95e10369665280db0c27Dake Gu        runUnfinishedStates();
316a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    }
317a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
31889097f67f988ebba714a95e10369665280db0c27Dake Gu    void runUnfinishedStates() {
31989097f67f988ebba714a95e10369665280db0c27Dake Gu        boolean changed;
32089097f67f988ebba714a95e10369665280db0c27Dake Gu        do {
32189097f67f988ebba714a95e10369665280db0c27Dake Gu            changed = false;
32289097f67f988ebba714a95e10369665280db0c27Dake Gu            for (int i = mUnfinishedStates.size() - 1; i >= 0; i--) {
32389097f67f988ebba714a95e10369665280db0c27Dake Gu                State state = mUnfinishedStates.get(i);
32489097f67f988ebba714a95e10369665280db0c27Dake Gu                if (state.runIfNeeded()) {
32589097f67f988ebba714a95e10369665280db0c27Dake Gu                    mUnfinishedStates.remove(i);
32689097f67f988ebba714a95e10369665280db0c27Dake Gu                    mFinishedStates.add(state);
32789097f67f988ebba714a95e10369665280db0c27Dake Gu                    changed = true;
32889097f67f988ebba714a95e10369665280db0c27Dake Gu                }
32989097f67f988ebba714a95e10369665280db0c27Dake Gu            }
33089097f67f988ebba714a95e10369665280db0c27Dake Gu        } while (changed);
331a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    }
332a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
333a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    /**
33489097f67f988ebba714a95e10369665280db0c27Dake Gu     * Find outgoing Transitions of invoked State whose Event matches, mark the Transition invoked.
335a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu     */
33689097f67f988ebba714a95e10369665280db0c27Dake Gu    public void fireEvent(Event event) {
33789097f67f988ebba714a95e10369665280db0c27Dake Gu        for (int i = 0; i < mFinishedStates.size(); i++) {
33889097f67f988ebba714a95e10369665280db0c27Dake Gu            State state = mFinishedStates.get(i);
33989097f67f988ebba714a95e10369665280db0c27Dake Gu            if (state.mOutgoings != null) {
34089097f67f988ebba714a95e10369665280db0c27Dake Gu                if (!state.mBranchStart && state.mInvokedOutTransitions > 0) {
34189097f67f988ebba714a95e10369665280db0c27Dake Gu                    continue;
34289097f67f988ebba714a95e10369665280db0c27Dake Gu                }
34389097f67f988ebba714a95e10369665280db0c27Dake Gu                for (Transition t : state.mOutgoings) {
34489097f67f988ebba714a95e10369665280db0c27Dake Gu                    if (t.mState != STATUS_INVOKED && t.mEvent == event) {
34589097f67f988ebba714a95e10369665280db0c27Dake Gu                        if (DEBUG) {
34689097f67f988ebba714a95e10369665280db0c27Dake Gu                            Log.d(TAG, "signal " + t);
34789097f67f988ebba714a95e10369665280db0c27Dake Gu                        }
34889097f67f988ebba714a95e10369665280db0c27Dake Gu                        t.mState = STATUS_INVOKED;
34989097f67f988ebba714a95e10369665280db0c27Dake Gu                        state.mInvokedOutTransitions++;
35089097f67f988ebba714a95e10369665280db0c27Dake Gu                        if (!state.mBranchStart) {
35189097f67f988ebba714a95e10369665280db0c27Dake Gu                            break;
35289097f67f988ebba714a95e10369665280db0c27Dake Gu                        }
35389097f67f988ebba714a95e10369665280db0c27Dake Gu                    }
35489097f67f988ebba714a95e10369665280db0c27Dake Gu                }
355a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu            }
356a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        }
35789097f67f988ebba714a95e10369665280db0c27Dake Gu        runUnfinishedStates();
35889097f67f988ebba714a95e10369665280db0c27Dake Gu    }
359a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu
36089097f67f988ebba714a95e10369665280db0c27Dake Gu    /**
36189097f67f988ebba714a95e10369665280db0c27Dake Gu     * Reset status to orignal status
36289097f67f988ebba714a95e10369665280db0c27Dake Gu     */
36389097f67f988ebba714a95e10369665280db0c27Dake Gu    public void reset() {
36489097f67f988ebba714a95e10369665280db0c27Dake Gu        if (DEBUG) {
36589097f67f988ebba714a95e10369665280db0c27Dake Gu            Log.d(TAG, "reset");
36689097f67f988ebba714a95e10369665280db0c27Dake Gu        }
36789097f67f988ebba714a95e10369665280db0c27Dake Gu        mUnfinishedStates.clear();
36889097f67f988ebba714a95e10369665280db0c27Dake Gu        mFinishedStates.clear();
36989097f67f988ebba714a95e10369665280db0c27Dake Gu        for (State state: mStates) {
37089097f67f988ebba714a95e10369665280db0c27Dake Gu            state.mStatus = STATUS_ZERO;
37189097f67f988ebba714a95e10369665280db0c27Dake Gu            state.mInvokedOutTransitions = 0;
37289097f67f988ebba714a95e10369665280db0c27Dake Gu            if (state.mOutgoings != null) {
37389097f67f988ebba714a95e10369665280db0c27Dake Gu                for (Transition t: state.mOutgoings) {
37489097f67f988ebba714a95e10369665280db0c27Dake Gu                    t.mState = STATUS_ZERO;
375a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu                }
376a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu            }
377a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu        }
378a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu    }
379a5a85434f936023043f074fb86eaa6d48f7f6411Dake Gu}
380