package eu.hgross.blaubotcam;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.SeekBar;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import java.util.Date;
import java.util.Observable;
import java.util.Observer;
import java.util.UUID;
import eu.hgross.blaubot.android.BlaubotAndroid;
import eu.hgross.blaubot.android.BlaubotAndroidFactory;
import eu.hgross.blaubot.android.views.DebugView;
import eu.hgross.blaubot.android.views.StateView;
import eu.hgross.blaubot.android.views.edit.WebsocketServerConnectorEditView;
import eu.hgross.blaubot.core.Blaubot;
import eu.hgross.blaubot.core.LifecycleListenerAdapter;
import eu.hgross.blaubot.core.statemachine.ConnectionStateMachineAdapter;
import eu.hgross.blaubot.core.statemachine.states.FreeState;
import eu.hgross.blaubot.core.statemachine.states.IBlaubotState;
import eu.hgross.blaubot.core.statemachine.states.IBlaubotSubordinatedState;
import eu.hgross.blaubot.core.statemachine.states.KingState;
import eu.hgross.blaubot.core.statemachine.states.StoppedState;
import eu.hgross.blaubot.messaging.BlaubotChannelConfig;
import eu.hgross.blaubot.messaging.BlaubotMessage;
import eu.hgross.blaubot.messaging.IBlaubotChannel;
import eu.hgross.blaubot.util.Log;
import eu.hgross.blaubot.util.SubscriptionWatcher;
import eu.hgross.blaubotcam.audio.BlaubotWalkieTalkie;
import eu.hgross.blaubotcam.audio.IRecordingListener;
import eu.hgross.blaubotcam.video.CameraReader;
import eu.hgross.blaubotcam.video.ImageMessage;
import eu.hgross.blaubotcam.views.AudioViewer;
import eu.hgross.blaubotcam.views.VideoViewer;
/**
* A simple video cam app to show the m:n communication of blaubot with bigger byte messages
*/
public class BlaubotCamMainActivity extends Activity {
private static final String LOG_TAG = "BlaubotCamMainActivity";
private static final UUID APP_UUID = UUID.fromString("cc3aee81-2187-4324-91ac-db9b0697e61c");
private static final int ACCEPTOR_PORT = 17171;
private static final int BEACON_PORT = 17172;
private static final int BEACON_BROADCAST_PORT = 17173;
/**
* Iff true, blaubot will be auto-started
*/
private static final boolean AUTOSTART = false;
/**
* The channel id over which we send our pictures
*/
private static final short VIDEO_CHANNEL_ID = 1;
/**
* The channel id used to send audio messages
*/
private static final short AUDIO_CHANNEL_ID = 2;
/**
* The max settable delay in ms between the publish of two pictures
*/
private static final int MAX_FEED_DELAY = 5000;
/**
* Never hold more than 10 messages in the queue
*/
private static final int PICTURE_QUEUE_SIZE = 10;
/**
* The default unique device id of the server. Has to match the unique device on the server side.
*/
private static final String SERVER_DEFAULT_UNIQUE_DEVICE_ID = "BlaubotCamServer";
/**
* The default uri path to the websocket endpoint.
*/
private static final String SERVER_DEFAULT_WEBSOCKET_URL_PATH = "/blaubot";
/**
* The default server port of the websocket endpoint
*/
private static final int SERVER_DEFAULT_PORT = 8080;
/**
* The default hostname of the server
*/
private static final String SERVER_DEFAULT_HOST = "192.168.168.2";
private static final String DEBUG_VIEW_TAB_INDICATOR_TEXT = "DebugView";
private Blaubot mBlaubot;
private VideoViewer mVideoViewer;
private AudioViewer mAudioViewer;
private CameraReader mCameraReader;
private BlaubotWalkieTalkie mWalkieTalkie;
private Handler mUiHandler;
private Button mToggleVideoButton;
private Button mCreateServerConnectorButton;
private Button mWalkieTalkieRecordButton;
private ScrollView mPreviewTabScrollView;
private View mConnectionStatusIndicatorView;
private SeekBar mVideoChannelMessageRateSeekBar;
private TextView mSeekBarValueTextView;
private SeekBar mJpegQualitySeekBar;
private TextView mJpegQualitySeekBarValueTextView;
/**
* Start/Stop buttons and state visualization for each tab
*/
private StateView mStateView2, mStateView3;
/**
* The tab tag for the config view
*/
private static final String TAB_PREVIEW = "Preview";
/**
* The tab tag for the debug view
*/
private static final String TAB_DEBUG_VIEW = "DebugView";
/**
* The tab tag for the picture feed
*/
private static final String TAB_FEED = "Feed";
private DebugView mDebugView;
private TabHost mTabHost;
private ToggleButton mToggleReceiveImagesButton;
private ToggleButton mToggleReceiveAudioButton;
private LinearLayout mSurfaceViewContainer;
private IBlaubotChannel mVideoChannel;
private IBlaubotChannel mAudioChannel;
private boolean mConnected;
/**
* If the creation of blaubot fails due to an exception (no wifi or whatever)
* the exception is stored here to be displayed to the user.
*/
private Exception mFailException;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_blaubot_cam_main);
mUiHandler = new Handler(Looper.getMainLooper());
mCameraReader = new CameraReader(this);
this.mDebugView = (DebugView) findViewById(R.id.debugView);
this.mStateView2 = (StateView) findViewById(R.id.stateView2);
this.mStateView3 = (StateView) findViewById(R.id.stateView3);
this.mTabHost = (TabHost) findViewById(R.id.tabHost);
this.mTabHost.setup();
// create tabspecs
TabHost.TabSpec previewTabSpec = this.mTabHost.newTabSpec(TAB_PREVIEW);
previewTabSpec.setContent(R.id.previewContainer);
previewTabSpec.setIndicator("Preview");
TabHost.TabSpec feedTabSpec = this.mTabHost.newTabSpec(TAB_FEED);
feedTabSpec.setContent(R.id.feedContainer);
feedTabSpec.setIndicator("Feed");
final TabHost.TabSpec debugViewTabSpec = this.mTabHost.newTabSpec(TAB_DEBUG_VIEW);
debugViewTabSpec.setContent(R.id.debugViewContainer);
debugViewTabSpec.setIndicator(DEBUG_VIEW_TAB_INDICATOR_TEXT);
mTabHost.addTab(previewTabSpec);
mTabHost.addTab(feedTabSpec);
mTabHost.addTab(debugViewTabSpec);
mTabHost.setCurrentTabByTag(TAB_DEBUG_VIEW);
mConnectionStatusIndicatorView = findViewById(R.id.connectionStatusIndicator);
mPreviewTabScrollView = (ScrollView) findViewById(R.id.previewTabScrollView);
mWalkieTalkieRecordButton = (Button) findViewById(R.id.walkieTalkieButton);
mToggleVideoButton = (Button) findViewById(R.id.toggleVideoButton);
mToggleVideoButton.setSelected(false);
mVideoChannelMessageRateSeekBar = (SeekBar) findViewById(R.id.videoChannelMessageRateSeekBar);
mJpegQualitySeekBar = (SeekBar) findViewById(R.id.jpegQualitySeekBar);
mSeekBarValueTextView = (TextView) findViewById(R.id.seekbarValueTextView);
mJpegQualitySeekBarValueTextView = (TextView) findViewById(R.id.jpegQualitySeekBarValueTextView);
mSurfaceViewContainer = (LinearLayout) findViewById(R.id.liveVideoSurfaceViewContainer);
mCreateServerConnectorButton = (Button) findViewById(R.id.createServerConnectorButton);
mVideoViewer = (VideoViewer) findViewById(R.id.videoViewer);
mAudioViewer = (AudioViewer) findViewById(R.id.audioViewer);
mToggleReceiveAudioButton = (ToggleButton) findViewById(R.id.toggleReceiveAudioButton);
mToggleReceiveAudioButton.setChecked(true);
mToggleReceiveAudioButton.setEnabled(false);
mToggleReceiveImagesButton = (ToggleButton) findViewById(R.id.toggleReceiveVideoButton);
mToggleReceiveImagesButton.setChecked(false);
mToggleReceiveImagesButton.setEnabled(false);
// Try to create blaubot or save the exception
try {
// mBlaubot = BlaubotAndroidFactory.createBluetoothBlaubot(APP_UUID);
// mBlaubot = BlaubotAndroidFactory.createBluetoothBlaubotWithNFCBeacon(APP_UUID);
// mBlaubot = BlaubotAndroidFactory.createBluetoothBlaubotWithMulticastBeacon(APP_UUID, BEACON_PORT, BEACON_BROADCAST_PORT);
mBlaubot = BlaubotAndroidFactory.createEthernetBlaubot(APP_UUID);
// mBlaubot = BlaubotAndroidFactory.createEthernetBlaubot(APP_UUID, ACCEPTOR_PORT, BEACON_PORT, BEACON_BROADCAST_PORT, BlaubotAndroidFactory.getLocalIpAddress());
// mBlaubot = BlaubotAndroidFactory.createEthernetBlaubotWithNFCBeacon(APP_UUID, ACCEPTOR_PORT, BlaubotAndroidFactory.getLocalIpAddress());
// mBlaubot = BlaubotAndroidFactory.createEthernetBlaubotWithBonjourBeacon(APP_UUID, ACCEPTOR_PORT, BEACON_PORT, BlaubotAndroidFactory.getLocalIpAddress());
// mBlaubot = BlaubotAndroidFactory.createWebSocketBlaubotWithMulticastBeacon(APP_UUID, ACCEPTOR_PORT, BEACON_PORT, BEACON_BROADCAST_PORT, BlaubotAndroidFactory.getLocalIpAddress());
// mBlaubot = BlaubotAndroidFactory.createWebSocketBlaubotWithBonjourBeacon(APP_UUID, ACCEPTOR_PORT, BEACON_PORT, BlaubotAndroidFactory.getLocalIpAddress());
// mBlaubot = BlaubotAndroidFactory.createEthernetBlaubotWithBluetoothBeacon(APP_UUID, ACCEPTOR_PORT, BlaubotAndroidFactory.getLocalIpAddress());
// mBlaubot = BlaubotAndroidFactory.createWifiApWithBluetoothBeaconBlaubot(APP_UUID, (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE), (WifiManager) getSystemService(WIFI_SERVICE), ACCEPTOR_PORT);
// mBlaubot = BlaubotAndroidFactory.createWifiApWithNfcBeaconBlaubot(APP_UUID, (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE), (WifiManager) getSystemService(WIFI_SERVICE), ACCEPTOR_PORT);
} catch (Exception e) {
mFailException = e;
e.printStackTrace();
return;
}
// wire everything
mSurfaceViewContainer.addView(mCameraReader.getSurfaceView());
mToggleVideoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toggleVideo();
}
});
mCameraReader.addObserver(mNewImageObserver);
mDebugView.registerBlaubotInstance(mBlaubot);
mStateView2.registerBlaubotInstance(mBlaubot);
mStateView3.registerBlaubotInstance(mBlaubot);
mBlaubot.addLifecycleListener(new LifecycleListenerAdapter() {
@Override
public void onConnected() {
mConnected = true;
mUiHandler.post(new Runnable() {
@Override
public void run() {
mToggleReceiveImagesButton.setEnabled(true);
mToggleReceiveAudioButton.setEnabled(true);
}
});
}
@Override
public void onDisconnected() {
mConnected = false;
mUiHandler.post(new Runnable() {
@Override
public void run() {
mToggleReceiveImagesButton.setEnabled(false);
mToggleReceiveAudioButton.setEnabled(false);
}
});
}
});
// create configure and add a listener to the audio channel
mAudioChannel = mBlaubot.createChannel(AUDIO_CHANNEL_ID);
mWalkieTalkie = new BlaubotWalkieTalkie(this, mAudioChannel, mBlaubot.getOwnDevice());
// create, configure and add a listener to the video channel
mVideoChannel = mBlaubot.createChannel(VIDEO_CHANNEL_ID);
mVideoChannel.getChannelConfig().setMessageRateLimit(500).setMessagePickerStrategy(BlaubotChannelConfig.MessagePickerStrategy.DISCARD_OLD);
mVideoChannel.getChannelConfig().setPriority(BlaubotMessage.Priority.LOW);
mVideoChannel.getChannelConfig().setQueueCapacity(PICTURE_QUEUE_SIZE);
mVideoChannel.addMessageListener(mVideoViewer);
mBlaubot.getChannelManager().addAdminMessageListener(mVideoViewer);
// set up the WalkieTalkie UI, the subscription toggle buttons for audio and video and the
// seekbars to configure message rate and jpeg quality
setUpWalkieTalkieUi();
setUpSubscriptionButtons();
setUpSeekBars();
// wire the create server connector button to an edit view
mCreateServerConnectorButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
WebsocketServerConnectorEditView.createAsDialog(BlaubotCamMainActivity.this, mBlaubot, SERVER_DEFAULT_HOST, SERVER_DEFAULT_PORT, SERVER_DEFAULT_WEBSOCKET_URL_PATH, SERVER_DEFAULT_UNIQUE_DEVICE_ID).show();
}
});
// maintain the connection indicator color
final ConnectionStatusIndicatorUpdater indicatorUpdater = new ConnectionStatusIndicatorUpdater();
mBlaubot.getConnectionStateMachine().addConnectionStateMachineListener(indicatorUpdater);
indicatorUpdater.setIndicatorFromState(mBlaubot.getConnectionStateMachine().getCurrentState());
// Autostart switch
if (AUTOSTART) {
this.mBlaubot.startBlaubot();
}
}
/**
* Creates the WalkieTalkie send button and some logic to avoid scrolling while it is pressed.
*/
private void setUpWalkieTalkieUi() {
mAudioViewer.setWalkieTalkie(mWalkieTalkie);
/**
* a touch listener that triggers audio recording and prevents scrolling when we record audio
* by blocking the wrapping scroll view
*/
final View.OnTouchListener recordingTouchListener = new View.OnTouchListener() {
/**
* if true, the scrollview in the preview tab will not scroll and we are recording
*/
private boolean mNoScrollingInPreviewTab = false;
@Override
public boolean onTouch(View v, MotionEvent event) {
if (v == mWalkieTalkieRecordButton && event.getAction() == MotionEvent.ACTION_DOWN) {
// prevent scroll view scrolling when pressed
mNoScrollingInPreviewTab = true;
// start recording audio
mWalkieTalkie.startRecording();
} else if (event.getAction() == MotionEvent.ACTION_UP) {// || event.getAction() == MotionEvent.ACTION_CANCEL) {
if (mNoScrollingInPreviewTab) {
mWalkieTalkie.stopRecording();
// enable scrolling
mNoScrollingInPreviewTab = false;
}
}
return mNoScrollingInPreviewTab;
}
};
// Button to record audio
mWalkieTalkieRecordButton.setOnTouchListener(recordingTouchListener);
mPreviewTabScrollView.setOnTouchListener(recordingTouchListener);
// add listener to maintain button state
mWalkieTalkie.addRecordingListener(new IRecordingListener() {
@Override
public void onRecordingStarted() {
mUiHandler.post(new Runnable() {
@Override
public void run() {
mWalkieTalkieRecordButton.setText("RECORDING ...");
}
});
}
@Override
public void onRecordingStopped() {
mUiHandler.post(new Runnable() {
@Override
public void run() {
mWalkieTalkieRecordButton.setText("record and send audio");
}
});
}
});
}
/**
* Sets up the seekbars to change the message rate and image quality.
*/
private void setUpSeekBars() {
// Configure the message rate seek bar
mVideoChannelMessageRateSeekBar.setMax(MAX_FEED_DELAY);
mVideoChannelMessageRateSeekBar.setProgress(mVideoChannel.getChannelConfig().getMinMessageRateDelay());
mVideoChannelMessageRateSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
private int progress = mVideoChannelMessageRateSeekBar.getProgress();
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (progress <= 0) {
progress = 1;
}
this.progress = progress;
updateMessageRateSeekbarTextView(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mVideoChannel.getChannelConfig().setMessageRateLimit(progress);
}
});
// configure the jpeg quality seekbar
mJpegQualitySeekBar.setMax(100);
mJpegQualitySeekBar.setProgress(mCameraReader.getJpegQuality());
mJpegQualitySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
private int progress = mJpegQualitySeekBar.getProgress();
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (progress <= 0) {
progress = 1;
}
this.progress = progress;
updateJpegQualitySeekbarTextView(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mCameraReader.setJpegQuality(progress);
}
});
// we attach an observer to the channel config to get informed about changes
Observer configChangeObserver = new Observer() {
@Override
public void update(Observable observable, Object data) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
final int minMessageRateDelay = mVideoChannel.getChannelConfig().getMinMessageRateDelay();
updateMessageRateSeekbarTextView(minMessageRateDelay);
mVideoChannelMessageRateSeekBar.setProgress(minMessageRateDelay);
}
});
}
};
mVideoChannel.getChannelConfig().addObserver(configChangeObserver);
// update the views initially
updateMessageRateSeekbarTextView(mVideoChannel.getChannelConfig().getMinMessageRateDelay());
updateJpegQualitySeekbarTextView(mCameraReader.getJpegQuality());
}
/**
* Wires the subscription toggle buttons for the audio and video channel with some listeners
*/
private void setUpSubscriptionButtons() {
/*
Toggle buttons for subscriptions
*/
// using a watcher to maintain the toggle state of the receive images button
final SubscriptionWatcher videoChannelSubscriptionWatcher = new SubscriptionWatcher(VIDEO_CHANNEL_ID) {
private void setChecked(final boolean checked) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
mToggleReceiveImagesButton.setChecked(checked);
}
});
}
@Override
public void onUnsubscribed(short channelId) {
setChecked(false);
}
@Override
public void onSubscribed(short channelId) {
setChecked(true);
}
};
videoChannelSubscriptionWatcher.registerWithBlaubot(mBlaubot);
// Button to toggle the subscription to the video channel
mToggleReceiveImagesButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!mConnected) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(BlaubotCamMainActivity.this, "Not connected to blaubot network.", Toast.LENGTH_SHORT).show();
mToggleReceiveImagesButton.setChecked(videoChannelSubscriptionWatcher.isSubscribed());
}
});
return;
}
if (!videoChannelSubscriptionWatcher.isSubscribed()) {
mVideoChannel.subscribe();
} else {
mVideoChannel.unsubscribe();
}
}
});
// using a watcher to maintain the toggle state of the receive images button
final SubscriptionWatcher audioChannelSubscriptionWatcher = new SubscriptionWatcher(AUDIO_CHANNEL_ID) {
private void setChecked(final boolean checked) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
mToggleReceiveAudioButton.setChecked(checked);
}
});
}
@Override
public void onUnsubscribed(short channelId) {
setChecked(false);
}
@Override
public void onSubscribed(short channelId) {
setChecked(true);
}
};
// Button to toggle the audio receiving
mToggleReceiveAudioButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!mConnected) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(BlaubotCamMainActivity.this, "Not connected to blaubot network.", Toast.LENGTH_SHORT).show();
mToggleReceiveAudioButton.setChecked(audioChannelSubscriptionWatcher.isSubscribed());
}
});
return;
}
if (!audioChannelSubscriptionWatcher.isSubscribed()) {
mAudioChannel.subscribe();
} else {
mAudioChannel.unsubscribe();
}
}
});
}
private void updateMessageRateSeekbarTextView(int value) {
mSeekBarValueTextView.setText(value + " ms");
}
private void updateJpegQualitySeekbarTextView(int value) {
mJpegQualitySeekBarValueTextView.setText(value + "%");
}
/**
* Listens to new images received from the camera
*/
private Observer mNewImageObserver = new Observer() {
// TODO: CHUNKING NECESSARY!
@Override
public void update(Observable observable, Object data) {
final byte[] jpegData = (byte[]) data;
if (mConnected && mVideoChannel != null) {
// construct a message containing the image and our id
final String myUniqueDeviceId = mBlaubot.getOwnDevice().getUniqueDeviceID();
final ImageMessage msg = new ImageMessage(myUniqueDeviceId, jpegData, new Date());
final boolean addedToQueue = mVideoChannel.publish(msg.toBytes(), 150);
if (!addedToQueue) {
// message was not added to queue and will be skipped
}
}
}
};
private Object videoToggleLock = new Object();
private boolean videoActive = false;
private void toggleVideo() {
synchronized (videoToggleLock) {
mCameraReader.toggleVideoStream();
videoActive = !videoActive;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_blaubot_cam_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onNewIntent(Intent intent) {
if (mBlaubot instanceof BlaubotAndroid) {
final BlaubotAndroid blaubotAndroid = (BlaubotAndroid) mBlaubot;
blaubotAndroid.onNewIntent(intent);
}
super.onNewIntent(intent);
}
@Override
protected void onResume() {
Log.d(LOG_TAG, "LifeCycle.onResume");
if (this.mBlaubot == null) {
// stop
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIcon(R.drawable.ic_stopped);
builder.setTitle("Creation error");
builder.setMessage("Could not create Blaubot. Is Wi-Fi/Bluetooth/... turned on? Message: " + mFailException.getMessage())
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
BlaubotCamMainActivity.this.finish();
}
});
builder.create().show();
super.onResume();
return;
}
if (mBlaubot instanceof BlaubotAndroid) {
final BlaubotAndroid blaubotAndroid = (BlaubotAndroid) mBlaubot;
blaubotAndroid.setContext(this);
blaubotAndroid.registerReceivers(this);
blaubotAndroid.onResume(this);
}
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
if (mBlaubot instanceof BlaubotAndroid) {
final BlaubotAndroid blaubotAndroid = (BlaubotAndroid) mBlaubot;
blaubotAndroid.unregisterReceivers(this);
blaubotAndroid.onResume(this);
}
}
@Override
protected void onStop() {
if (mBlaubot == null) {
super.onStop();
return;
}
mWalkieTalkie.release();
if (mBlaubot instanceof BlaubotAndroid) {
final BlaubotAndroid blaubotAndroid = (BlaubotAndroid) mBlaubot;
blaubotAndroid.stopBlaubot();
}
super.onStop();
}
/**
* Maintains the indicator color to show if we are connected, stopped or searching.
*/
private class ConnectionStatusIndicatorUpdater extends ConnectionStateMachineAdapter {
private static final int CONNECTED_COLOR = R.color.Green;
private static final int STOPPED_COLOR = R.color.Maroon;
private static final int SEARCHING_COLOR = R.color.DarkGoldenrod;
private void setColor(final int color) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
mConnectionStatusIndicatorView.setBackgroundResource(color);
}
});
}
@Override
public void onStateChanged(IBlaubotState oldState, IBlaubotState newState) {
setIndicatorFromState(newState);
}
public void setIndicatorFromState(IBlaubotState state) {
if (state instanceof FreeState) {
setColor(SEARCHING_COLOR);
} else if (state instanceof IBlaubotSubordinatedState || state instanceof KingState) {
setColor(CONNECTED_COLOR);
} else if (state instanceof StoppedState) {
setColor(STOPPED_COLOR);
}
}
}
}