package com.robot.open;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import at.abraxas.amarino.Amarino;
import at.abraxas.amarino.AmarinoIntent;
import com.openrobot.common.ClientService;
import com.openrobot.common.ClientServiceInterface;
import com.openrobot.common.DialogHelper;
import com.openrobot.common.EditTextDialogInterface;
import com.openrobot.common.NetworkHelper;
import com.openrobot.common.ServerService;
import com.openrobot.common.ServerServiceInterface;
import com.openrobot.common.ThumbBall;
import com.openrobot.common.ThumbBallListener;
public class TouchControl extends Activity implements ThumbBallListener, ServerServiceInterface,
ClientServiceInterface, EditTextDialogInterface {
private final String DEVICE_ADDRESS = "00:07:80:91:32:51";
private static final char ARDUINO_CONTROL_INPUT_FUNCTION_FLAG = 'c';
private static final char ARDUINO_SHOULD_KILL_FUNCTION_FLAG = 'd';
private final boolean BLUETOOTH_ENABLED = false;
private static final int ARDUINO_MAC_DIALOG_TAG = 1;
private static final int SERVER_IP_DIALOG_TAG = 2;
private static final int SERVER_PORT_DIALOG_TAG = 3;
private static final int DEFINE_SERVER_PORT_DIALOG_TAG = 4;
private static final String ARDUINO_MAC_ADDRESS_KEY = "ARDUINO_MAC_ADDRESS";
private static final String SERVER_IP_ADDRESS_KEY = "SERVER_IP_ADDRESS";
private static final String SERVER_PORT_ADDRESS_KEY = "SERVER_PORT_ADDRESS";
private static final String DEFINE_SERVER_PORT_ADDRESS_KEY = "DEFINE_SERVER_PORT_ADDRESS";
private static final String DEFAULT_ARDUINO_MAC_ADDRESS = "00:07:80:91:32:51";
private static final String DEFAULT_SERVER_IP_ADDRESS = "192.168.1.164";
private static final int DEFAULT_SERVER_PORT_ADDRESS = 8080;
public final static int DELAY = 150;
public final static int CIRCLE_RADIUS = 40;
private ThumbBall thumbBall;
private TextView xPosTextView;
private TextView yPosTextView;
private FrameLayout main;
private Button killButton;
private boolean shouldKill = false;
private boolean shouldEnable = false;
private boolean killEnabled = false;
private ClientService clientService;
private ServerService serverService;
boolean clientServiceIsBound;
boolean serverServiceIsBound;
private long lastChange;
private Intent clientServiceIntent;
private Intent serverServiceIntent;
private ServiceConnection clientConnection;
private ServiceConnection serverConnection;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (BLUETOOTH_ENABLED) {
// Establish bluetooth connection with Arduino
Amarino.connect(this, DEVICE_ADDRESS);
}
lastChange = System.currentTimeMillis();
main = (FrameLayout) findViewById(R.id.main_view);
main.setBackgroundColor(Color.CYAN);
xPosTextView = (TextView)findViewById(R.id.x_position_textview);
yPosTextView = (TextView)findViewById(R.id.y_position_textview);
thumbBall = new ThumbBall(this,
(main.getRight() - main.getLeft()) / 2,
(main.getBottom() - main.getTop()) / 2 ,
CIRCLE_RADIUS);
thumbBall.setDelegate(this);
main.addView(thumbBall);
this.killButton = (Button)this.findViewById(R.id.kill_button);
this.killButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (killEnabled) {
shouldEnable = true;
shouldKill = false;
killEnabled = false;
killButton.setText("Stop, please");
} else {
shouldEnable = false;
shouldKill = true;
killEnabled = true;
killButton.setText("OK, Game On.");
}
}
});
}
void makeServerConnection() {
serverService.setDelegate(this);
serverService.makeConnection(getDefinedServerPortAddress());
}
void makeClientConnection() {
clientService.setDelegate(this);
clientService.makeConnection(getServerIPAddress(), getServerPortAddress(), false);
}
@Override
public void onStart() {
super.onStart();
thumbBall.setX(ThumbBall.FRAME_WIDTH / 2);
thumbBall.setY(ThumbBall.FRAME_HEIGHT / 2);
thumbBall.invalidate();
}
@Override
protected void onStop() {
super.onStop();
// Housekeeping
Amarino.disconnect(this, DEVICE_ADDRESS);
}
@Override
protected void onDestroy() {
// prevents circular reference memory leak ???
if (thumbBall != null) {
thumbBall.setDelegate(null);
}
unbindClientService();
unbindServerService();
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.toggle_server_state:
if (serverServiceIsBound) {
unbindServerService();
} else {
bindServerService();
}
return true;
case R.id.toggle_client_state:
if (clientServiceIsBound) {
unbindClientService();
} else {
bindClientService();
}
return true;
case R.id.set_arduino_mac:
letUserSetArduinoMacAddress();
return true;
case R.id.define_server_port:
letUserDefineServerPort();
return true;
case R.id.set_server_ip:
letUserSetServerIPAddress();
return true;
case R.id.set_server_port:
letUserSetServerPort();
return true;
case R.id.server_settings:
case R.id.client_settings:
case R.id.curr_server_ip:
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onPrepareOptionsMenu (Menu menu) {
MenuItem serverItem = menu.findItem(R.id.toggle_server_state);
serverItem.setTitle(serverServiceIsBound ? R.string.stop_server : R.string.start_server);
serverItem.setEnabled(!clientServiceIsBound);
MenuItem clientItem = menu.findItem(R.id.toggle_client_state);
clientItem.setTitle(clientServiceIsBound ? R.string.stop_client : R.string.start_client);
clientItem.setEnabled(!serverServiceIsBound);
MenuItem currServerIPItem = menu.findItem(R.id.curr_server_ip);
currServerIPItem.setTitle("Server IP: " + NetworkHelper.getLocalIpAddress());
currServerIPItem.setEnabled(false);
MenuItem arduinoMACItem = menu.findItem(R.id.set_arduino_mac);
arduinoMACItem.setTitle("Arduino: " + getArduinoMACAddress());
MenuItem defineServerPort = menu.findItem(R.id.define_server_port);
defineServerPort.setTitle("Server Port #: " + Integer.toString(getDefinedServerPortAddress()));
MenuItem setServerPort = menu.findItem(R.id.set_server_port);
setServerPort.setTitle("Server Port #: " + Integer.toString(getServerPortAddress()));
MenuItem setServerIP = menu.findItem(R.id.set_server_ip);
setServerIP.setTitle("Server IP: " + getServerIPAddress());
return true;
}
// Methods used in Server App
private void letUserSetArduinoMacAddress() {
DialogHelper.textEntryAlertDialog(this, "Set Arduino MAC",
this.getArduinoMACAddress(), this, ARDUINO_MAC_DIALOG_TAG).show();
}
private void letUserDefineServerPort() {
DialogHelper.textEntryAlertDialog(this, "Define Server Port",
Integer.toString(this.getDefinedServerPortAddress()), this, DEFINE_SERVER_PORT_DIALOG_TAG).show();
}
// Methods used in Client App
private void letUserSetServerIPAddress() {
DialogHelper.textEntryAlertDialog(this, "Set Server IP",
this.getServerIPAddress(), this, SERVER_IP_DIALOG_TAG).show();
}
private void letUserSetServerPort() {
DialogHelper.textEntryAlertDialog(this, "Set Server Port",
Integer.toString(this.getServerPortAddress()), this, SERVER_PORT_DIALOG_TAG).show();
}
// EditTextDialogInterface
public void dialogFinishedWithStatus(boolean positiveStatus, String endingString, int tag) {
if (positiveStatus) {
if (tag == ARDUINO_MAC_DIALOG_TAG) {
this.setPreferenceStringForKey(ARDUINO_MAC_ADDRESS_KEY, endingString);
} else if (tag == DEFINE_SERVER_PORT_DIALOG_TAG) {
this.setPreferenceIntForKey(DEFINE_SERVER_PORT_ADDRESS_KEY, Integer.parseInt(endingString));
} else if (tag == SERVER_IP_DIALOG_TAG) {
this.setPreferenceStringForKey(SERVER_IP_ADDRESS_KEY, endingString);
} else if (tag == SERVER_PORT_DIALOG_TAG) {
this.setPreferenceIntForKey(SERVER_PORT_ADDRESS_KEY, Integer.parseInt(endingString));
}
}
}
private void bindServerService() {
if (serverService != null) {
unbindServerService();
}
serverServiceIntent = new Intent(this, ServerService.class);
initServerServiceConnection();
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(serverServiceIntent, serverConnection, Context.BIND_AUTO_CREATE);
serverServiceIsBound = true;
}
private void unbindServerService() {
if (serverServiceIsBound) {
// Detach our existing connection.
unbindService(serverConnection);
serverService = null;
serverServiceIntent = null;
serverServiceIsBound = false;
}
}
private void bindClientService() {
if (clientServiceIsBound) {
unbindClientService();
}
clientServiceIntent = new Intent(this, ClientService.class);
initClientServiceConnection();
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(clientServiceIntent, clientConnection, Context.BIND_AUTO_CREATE);
clientServiceIsBound = true;
}
private void unbindClientService() {
if (clientServiceIsBound) {
clientService.closeConnection();
// Detach our existing connection.
unbindService(clientConnection);
clientService = null;
clientServiceIntent = null;
clientServiceIsBound = false;
}
}
private Context currentContext() {
return this;
}
// ThumbBallInterface
@Override
public void thumbBallPositionChanged(ThumbBall tb) {
Log.d("OUTPUT", "X: " + tb.getX() + " Y: " + tb.getY());
xPosTextView.setText(Integer.toString(tb.translatedX()));
yPosTextView.setText(Integer.toString(-tb.translatedY()));
if (clientService != null && clientService.isConnected()) {
Float xFloat = new Float(thumbBall.getX());
Float yFloat = new Float(thumbBall.getY());
String stringToSend = xFloat.toString() + ServerService.SERVER_DELIMITER + yFloat.toString();
if (!clientService.sendStringToServer(stringToSend)) {
Log.d("TouchControl", "ClientService wasn't able to send String");
}
}
this.messageArduinoIfAppropriate((int)thumbBall.getX(), (int)thumbBall.getY());
}
// ServerServiceInterface
@Override
public void serverServiceStatusChange(String message, int status) {
Log.d("OUTPUT", message);
}
@Override
public void serverServiceReceivedMessage(ServerService service, String message) {
//Log.d("OUTPUT", "Message: " + message);
String[] splitArray = message.split(ServerService.SERVER_DELIMITER);
if (splitArray.length >= 2) {
float x = Float.parseFloat(((String)splitArray[0]));
float y = Float.parseFloat(((String)splitArray[1]));
Log.d("OUTPUT", splitArray[0] + ", " + x + " " + splitArray[1] + ", " + y);
thumbBall.setX(x);
thumbBall.setY(y);
thumbBall.invalidate();
this.messageArduinoIfAppropriate((int)thumbBall.getX(), (int)thumbBall.getY());
}
}
// ClientServiceInterface
public void clientServiceStatusChange(String message, int status) {
Log.d("OUTPUT", message);
}
public String messageToSend() {
Float xFloat = new Float(thumbBall.getX());
Float yFloat = new Float(thumbBall.getY());
return xFloat.toString() + ServerService.SERVER_DELIMITER + yFloat.toString();
}
public void clientServiceReceivedResponse(ClientService theClientService, String response) {
}
private void messageArduinoIfAppropriate(int x, int y) {
if (shouldKill || shouldEnable) {
// Send unconditional kill or resume message
if (BLUETOOTH_ENABLED) {
sendIntToArduino(shouldKill ? 1 : 0, ARDUINO_SHOULD_KILL_FUNCTION_FLAG);
}
shouldKill = false;
shouldEnable = false;
thumbBall.zeroThumbBallPosition();
return;
}
if (System.currentTimeMillis() - lastChange > DELAY ) {
int[] values = new int[2];
values[0] = x;
values[1] = y;
if (BLUETOOTH_ENABLED) {
sendIntArrayToArduino(values, ARDUINO_CONTROL_INPUT_FUNCTION_FLAG);
}
}
}
private void sendIntToArduino(int message, char methodFlag) {
lastChange = System.currentTimeMillis();
Intent intent = new Intent(AmarinoIntent.ACTION_SEND);
intent.putExtra(AmarinoIntent.EXTRA_DEVICE_ADDRESS, DEVICE_ADDRESS);
intent.putExtra(AmarinoIntent.EXTRA_DATA_TYPE, AmarinoIntent.INT_EXTRA);
intent.putExtra(AmarinoIntent.EXTRA_FLAG, methodFlag);
intent.putExtra(AmarinoIntent.EXTRA_DATA, message);
this.sendBroadcast(intent);
}
private void sendIntArrayToArduino(int[] message, char methodFlag) {
lastChange = System.currentTimeMillis();
Intent intent = new Intent(AmarinoIntent.ACTION_SEND);
intent.putExtra(AmarinoIntent.EXTRA_DEVICE_ADDRESS, DEVICE_ADDRESS);
intent.putExtra(AmarinoIntent.EXTRA_DATA_TYPE, AmarinoIntent.INT_ARRAY_EXTRA);
intent.putExtra(AmarinoIntent.EXTRA_FLAG, methodFlag);
intent.putExtra(AmarinoIntent.EXTRA_DATA, message);
this.sendBroadcast(intent);
}
private void initServerServiceConnection() {
serverConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
serverService = ((ServerService.ServerBinder)service).getService();
Toast.makeText(currentContext(), R.string.server_service_connected, Toast.LENGTH_SHORT).show();
makeServerConnection();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
serverService = null;
Toast.makeText(currentContext(), R.string.server_service_disconnected, Toast.LENGTH_SHORT).show();
}
};
}
private void initClientServiceConnection() {
clientConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
clientService = ((ClientService.ClientBinder)service).getService();
Toast.makeText(currentContext(), R.string.client_service_connected, Toast.LENGTH_SHORT).show();
makeClientConnection();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
clientService = null;
Toast.makeText(currentContext(), R.string.client_service_disconnected, Toast.LENGTH_SHORT).show();
}
};
}
// Preferences changed in Server App
private String getArduinoMACAddress() {
return this.getPreferenceStringForKey(ARDUINO_MAC_ADDRESS_KEY, DEFAULT_ARDUINO_MAC_ADDRESS);
}
private int getDefinedServerPortAddress() {
return this.getPreferenceIntForKey(DEFINE_SERVER_PORT_ADDRESS_KEY, DEFAULT_SERVER_PORT_ADDRESS);
}
// Preferences changed in Client App
private String getServerIPAddress() {
return this.getPreferenceStringForKey(SERVER_IP_ADDRESS_KEY, DEFAULT_SERVER_IP_ADDRESS);
}
private int getServerPortAddress() {
return this.getPreferenceIntForKey(SERVER_PORT_ADDRESS_KEY, DEFAULT_SERVER_PORT_ADDRESS);
}
// ************************************************************************
// PREFERENCES UTILITY FUNCTIONS
// ************************************************************************
private String getPreferenceStringForKey(String key, String defVal) {
SharedPreferences settings = getPreferences(MODE_PRIVATE);
return settings.getString(key, defVal);
}
private void setPreferenceStringForKey(String key, String string) {
SharedPreferences settings = getPreferences(MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(key, string);
editor.commit();
}
private int getPreferenceIntForKey(String key, int defVal) {
SharedPreferences settings = getPreferences(MODE_PRIVATE);
return settings.getInt(key, defVal);
}
private void setPreferenceIntForKey(String key, int theInt) {
SharedPreferences settings = getPreferences(MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putInt(key, theInt);
editor.commit();
}
}