package com.zachklipp.captivate.state_machine;
import android.content.Context;
import com.zachklipp.captivate.captive_portal.PortalDetector;
import com.zachklipp.captivate.captive_portal.PortalInfo;
import com.zachklipp.captivate.state_machine.StateMachineStorage.StorageBackend;
import com.zachklipp.captivate.util.Log;
import com.zachklipp.captivate.util.Observable;
import com.zachklipp.captivate.util.Observer;
public class PortalStateMachine extends StateMachine
{
private static final String LOG_TAG = "PortalStateMachine";
public static interface StorageBackendFactory
{
public StorageBackend create(Context context, PortalDetector detector);
}
public final static class State extends com.zachklipp.captivate.state_machine.State
{
public static final State UNKNOWN = new State("unknown", false, false);
public static final State NO_PORTAL = new State("no_portal", false, false);
public static final State SIGNIN_REQUIRED = new State("signin_required", true, true);
public static final State SIGNING_IN = new State("signing_in", true, true);
public static final State SIGNED_IN = new State("signed_in", true, false);
private boolean mIsBehindPortal;
private boolean mIsBlocked;
private State(String name, boolean behindPortal, boolean isBlocked)
{
super(name);
mIsBehindPortal = behindPortal;
mIsBlocked = isBlocked;
}
/*
* Return true if the state means the device is behind a portal.
*/
public boolean isBehindPortal()
{
return mIsBehindPortal;
}
/*
* Return true if the state means the portal is blocking network traffic.
*/
public boolean isBlocked()
{
return mIsBlocked;
}
}
public static final State[][] TRANSITION_MATRIX = new State[][] {
new State[] {State.UNKNOWN, State.NO_PORTAL, State.SIGNIN_REQUIRED},
new State[] {State.NO_PORTAL, State.UNKNOWN, State.SIGNIN_REQUIRED},
new State[] {State.SIGNIN_REQUIRED, State.UNKNOWN, State.SIGNING_IN, State.SIGNED_IN, State.NO_PORTAL},
new State[] {State.SIGNING_IN, State.UNKNOWN, State.SIGNED_IN, State.NO_PORTAL},
new State[] {State.SIGNED_IN, State.UNKNOWN, State.SIGNIN_REQUIRED, State.NO_PORTAL},
};
private PortalDetector mPortalDetector;
private Observer<PortalInfo> mPortalDetectorObserver = new Observer<PortalInfo>()
{
@Override
public void update(Observable<PortalInfo> observable, PortalInfo portal)
{
if (portal == null)
{
onSigninNoLongerRequired();
}
else
{
onSigninRequired();
}
}
};
public PortalStateMachine(PortalDetector detector)
{
super(State.UNKNOWN, TRANSITION_MATRIX);
onCreate(detector);
}
public PortalStateMachine(PortalDetector detector, String initialStateName)
{
super(initialStateName, TRANSITION_MATRIX);
onCreate(detector);
}
private void onCreate(PortalDetector detector)
{
assert(detector != null);
mPortalDetector = detector;
mPortalDetector.addObserver(mPortalDetectorObserver);
Log.d(LOG_TAG, "Portal state machine initialized to state %s",
getCurrentState().getName());
}
public State getCurrentPortalState()
{
return (State) getCurrentState();
}
public void startSignIn()
{
transitionTo(State.SIGNING_IN);
}
public void onDisabled()
{
transitionTo(State.UNKNOWN);
}
public void onNoWifi()
{
transitionTo(State.NO_PORTAL);
}
private void onSigninRequired()
{
if (getCurrentState() != State.SIGNIN_REQUIRED)
{
Log.d(LOG_TAG, "Captive portal detected.");
transitionTo(State.SIGNIN_REQUIRED);
}
}
private void onSigninNoLongerRequired()
{
if (getCurrentState() == State.SIGNING_IN
|| getCurrentState() == State.SIGNIN_REQUIRED
|| getCurrentState() == State.SIGNED_IN)
{
Log.d(LOG_TAG, "Portal signed in.");
transitionTo(State.SIGNED_IN);
}
else
{
Log.d(LOG_TAG, "No portal detected.");
transitionTo(State.NO_PORTAL);
}
}
}