First version.
This commit is contained in:
50
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/About.java
Normal file
50
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/About.java
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright 2010 Guenther Hoelzl, Shawn Brown
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.Window;
|
||||
import android.widget.Button;
|
||||
|
||||
class About {
|
||||
|
||||
private Dialog dialog;
|
||||
|
||||
public void show(Activity myActivity) {
|
||||
dialog = new Dialog(myActivity);
|
||||
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(R.layout.aboutbox);
|
||||
|
||||
Button buttonOK = (Button) dialog.findViewById(R.id.AboutOKbutton);
|
||||
buttonOK.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v)
|
||||
{
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
/**
|
||||
* Copyright 2010 Guenther Hoelzl, Shawn Brown
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.UUID;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
/**
|
||||
* This class is for talking to a LEGO NXT robot via bluetooth.
|
||||
* The communciation to the robot is done via LCP (LEGO communication protocol).
|
||||
* Objects of this class can either be run as standalone thread or controlled
|
||||
* by the owners, i.e. calling the send/recive methods by themselves.
|
||||
*/
|
||||
public class BTCommunicator extends Thread {
|
||||
public static final int MOTOR_A = 0;
|
||||
public static final int MOTOR_B = 1;
|
||||
public static final int MOTOR_C = 2;
|
||||
public static final int MOTOR_B_ACTION = 40;
|
||||
public static final int MOTOR_RESET = 10;
|
||||
public static final int DO_BEEP = 51;
|
||||
public static final int DO_ACTION = 52;
|
||||
public static final int READ_MOTOR_STATE = 60;
|
||||
public static final int GET_FIRMWARE_VERSION = 70;
|
||||
public static final int DISCONNECT = 99;
|
||||
|
||||
public static final int DISPLAY_TOAST = 1000;
|
||||
public static final int STATE_CONNECTED = 1001;
|
||||
public static final int STATE_CONNECTERROR = 1002;
|
||||
public static final int STATE_CONNECTERROR_PAIRING = 1022;
|
||||
public static final int MOTOR_STATE = 1003;
|
||||
public static final int STATE_RECEIVEERROR = 1004;
|
||||
public static final int STATE_SENDERROR = 1005;
|
||||
public static final int FIRMWARE_VERSION = 1006;
|
||||
public static final int FIND_FILES = 1007;
|
||||
public static final int START_PROGRAM = 1008;
|
||||
public static final int STOP_PROGRAM = 1009;
|
||||
public static final int GET_PROGRAM_NAME = 1010;
|
||||
public static final int PROGRAM_NAME = 1011;
|
||||
public static final int SAY_TEXT = 1030;
|
||||
public static final int VIBRATE_PHONE = 1031;
|
||||
|
||||
public static final int NO_DELAY = 0;
|
||||
|
||||
private static final UUID SERIAL_PORT_SERVICE_CLASS_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
|
||||
// this is the only OUI registered by LEGO, see http://standards.ieee.org/regauth/oui/index.shtml
|
||||
public static final String OUI_LEGO = "00:16:53";
|
||||
|
||||
private Resources mResources;
|
||||
private BluetoothAdapter btAdapter;
|
||||
private BluetoothSocket nxtBTsocket = null;
|
||||
private OutputStream nxtOutputStream = null;
|
||||
private InputStream nxtInputStream = null;
|
||||
private boolean connected = false;
|
||||
|
||||
private Handler uiHandler;
|
||||
private String mMACaddress;
|
||||
private BTConnectable myOwner;
|
||||
|
||||
private byte[] returnMessage;
|
||||
|
||||
public BTCommunicator(BTConnectable myOwner, Handler uiHandler, BluetoothAdapter btAdapter, Resources resources) {
|
||||
this.myOwner = myOwner;
|
||||
this.uiHandler = uiHandler;
|
||||
this.btAdapter = btAdapter;
|
||||
this.mResources = resources;
|
||||
}
|
||||
|
||||
public Handler getHandler() {
|
||||
return myHandler;
|
||||
}
|
||||
|
||||
public byte[] getReturnMessage() {
|
||||
return returnMessage;
|
||||
}
|
||||
|
||||
public void setMACAddress(String mMACaddress) {
|
||||
this.mMACaddress = mMACaddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The current status of the connection
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the connection, waits for incoming messages and dispatches them. The thread will be terminated
|
||||
* on closing of the connection.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
createNXTconnection();
|
||||
}
|
||||
catch (IOException e) { }
|
||||
|
||||
while (connected) {
|
||||
try {
|
||||
returnMessage = receiveMessage();
|
||||
if ((returnMessage.length >= 2) && ((returnMessage[0] == LCPMessage.REPLY_COMMAND) ||
|
||||
(returnMessage[0] == LCPMessage.DIRECT_COMMAND_NOREPLY)))
|
||||
dispatchMessage(returnMessage);
|
||||
|
||||
} catch (IOException e) {
|
||||
// don't inform the user when connection is already closed
|
||||
if (connected)
|
||||
sendState(STATE_RECEIVEERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bluetooth connection with SerialPortServiceClass_UUID
|
||||
* @see <a href=
|
||||
* "http://lejos.sourceforge.net/forum/viewtopic.php?t=1991&highlight=android"
|
||||
* />
|
||||
* On error the method either sends a message to it's owner or creates an exception in the
|
||||
* case of no message handler.
|
||||
*/
|
||||
public void createNXTconnection() throws IOException {
|
||||
try {
|
||||
BluetoothSocket nxtBTSocketTemporary;
|
||||
BluetoothDevice nxtDevice = null;
|
||||
nxtDevice = btAdapter.getRemoteDevice(mMACaddress);
|
||||
if (nxtDevice == null) {
|
||||
if (uiHandler == null)
|
||||
throw new IOException();
|
||||
else {
|
||||
sendToast(mResources.getString(R.string.no_paired_nxt));
|
||||
sendState(STATE_CONNECTERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
nxtBTSocketTemporary = nxtDevice.createRfcommSocketToServiceRecord(SERIAL_PORT_SERVICE_CLASS_UUID);
|
||||
try {
|
||||
nxtBTSocketTemporary.connect();
|
||||
}
|
||||
catch (IOException e) {
|
||||
if (myOwner.isPairing()) {
|
||||
if (uiHandler != null) {
|
||||
sendToast(mResources.getString(R.string.pairing_message));
|
||||
sendState(STATE_CONNECTERROR_PAIRING);
|
||||
}
|
||||
else
|
||||
throw e;
|
||||
return;
|
||||
}
|
||||
|
||||
// try another method for connection, this should work on the HTC desire, credits to Michael Biermann
|
||||
try {
|
||||
Method mMethod = nxtDevice.getClass().getMethod("createRfcommSocket", new Class[] { int.class });
|
||||
nxtBTSocketTemporary = (BluetoothSocket) mMethod.invoke(nxtDevice, Integer.valueOf(1));
|
||||
nxtBTSocketTemporary.connect();
|
||||
}
|
||||
catch (Exception e1){
|
||||
if (uiHandler == null)
|
||||
throw new IOException();
|
||||
else
|
||||
sendState(STATE_CONNECTERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
nxtBTsocket = nxtBTSocketTemporary;
|
||||
nxtInputStream = nxtBTsocket.getInputStream();
|
||||
nxtOutputStream = nxtBTsocket.getOutputStream();
|
||||
connected = true;
|
||||
} catch (IOException e) {
|
||||
if (uiHandler == null)
|
||||
throw e;
|
||||
else {
|
||||
if (myOwner.isPairing())
|
||||
sendToast(mResources.getString(R.string.pairing_message));
|
||||
sendState(STATE_CONNECTERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// everything was OK
|
||||
if (uiHandler != null)
|
||||
sendState(STATE_CONNECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the bluetooth connection. On error the method either sends a message
|
||||
* to it's owner or creates an exception in the case of no message handler.
|
||||
*/
|
||||
public void destroyNXTconnection() throws IOException {
|
||||
try {
|
||||
if (nxtBTsocket != null) {
|
||||
connected = false;
|
||||
nxtBTsocket.close();
|
||||
nxtBTsocket = null;
|
||||
}
|
||||
|
||||
nxtInputStream = null;
|
||||
nxtOutputStream = null;
|
||||
|
||||
} catch (IOException e) {
|
||||
if (uiHandler == null)
|
||||
throw e;
|
||||
else
|
||||
sendToast(mResources.getString(R.string.problem_at_closing));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message on the opened OutputStream
|
||||
* @param message, the message as a byte array
|
||||
*/
|
||||
public void sendMessage(byte[] message) throws IOException {
|
||||
if (nxtOutputStream == null)
|
||||
throw new IOException();
|
||||
|
||||
// send message length
|
||||
int messageLength = message.length;
|
||||
nxtOutputStream.write(messageLength);
|
||||
nxtOutputStream.write(messageLength >> 8);
|
||||
nxtOutputStream.write(message, 0, message.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a message on the opened InputStream
|
||||
* @return the message
|
||||
*/
|
||||
public byte[] receiveMessage() throws IOException {
|
||||
if (nxtInputStream == null)
|
||||
throw new IOException();
|
||||
|
||||
int length = nxtInputStream.read();
|
||||
length = (nxtInputStream.read() << 8) + length;
|
||||
byte[] returnMessage = new byte[length];
|
||||
nxtInputStream.read(returnMessage);
|
||||
return returnMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message on the opened OutputStream. In case of
|
||||
* an error the state is sent to the handler.
|
||||
* @param message, the message as a byte array
|
||||
*/
|
||||
private void sendMessageAndState(byte[] message) {
|
||||
if (nxtOutputStream == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
sendMessage(message);
|
||||
}
|
||||
catch (IOException e) {
|
||||
sendState(STATE_SENDERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchMessage(byte[] message) {
|
||||
switch (message[1]) {
|
||||
|
||||
case LCPMessage.GET_OUTPUT_STATE:
|
||||
|
||||
if (message.length >= 25)
|
||||
sendState(MOTOR_STATE);
|
||||
|
||||
break;
|
||||
|
||||
case LCPMessage.GET_FIRMWARE_VERSION:
|
||||
|
||||
if (message.length >= 7)
|
||||
sendState(FIRMWARE_VERSION);
|
||||
|
||||
break;
|
||||
|
||||
case LCPMessage.FIND_FIRST:
|
||||
case LCPMessage.FIND_NEXT:
|
||||
|
||||
if (message.length >= 28) {
|
||||
// Success
|
||||
if (message[2] == 0)
|
||||
sendState(FIND_FILES);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LCPMessage.GET_CURRENT_PROGRAM_NAME:
|
||||
|
||||
if (message.length >= 23) {
|
||||
sendState(PROGRAM_NAME);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LCPMessage.SAY_TEXT:
|
||||
|
||||
if (message.length == 22) {
|
||||
sendState(SAY_TEXT);
|
||||
}
|
||||
|
||||
case LCPMessage.VIBRATE_PHONE:
|
||||
if (message.length == 3) {
|
||||
sendState(VIBRATE_PHONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doBeep(int frequency, int duration) {
|
||||
byte[] message = LCPMessage.getBeepMessage(frequency, duration);
|
||||
sendMessageAndState(message);
|
||||
waitSomeTime(20);
|
||||
}
|
||||
|
||||
private void doAction(int actionNr) {
|
||||
byte[] message = LCPMessage.getActionMessage(actionNr);
|
||||
sendMessageAndState(message);
|
||||
}
|
||||
|
||||
private void startProgram(String programName) {
|
||||
byte[] message = LCPMessage.getStartProgramMessage(programName);
|
||||
sendMessageAndState(message);
|
||||
}
|
||||
|
||||
private void stopProgram() {
|
||||
byte[] message = LCPMessage.getStopProgramMessage();
|
||||
sendMessageAndState(message);
|
||||
}
|
||||
|
||||
private void getProgramName() {
|
||||
byte[] message = LCPMessage.getProgramNameMessage();
|
||||
sendMessageAndState(message);
|
||||
}
|
||||
|
||||
private void changeMotorSpeed(int motor, int speed) {
|
||||
if (speed > 100)
|
||||
speed = 100;
|
||||
|
||||
else if (speed < -100)
|
||||
speed = -100;
|
||||
|
||||
byte[] message = LCPMessage.getMotorMessage(motor, speed);
|
||||
sendMessageAndState(message);
|
||||
}
|
||||
|
||||
private void rotateTo(int motor, int end) {
|
||||
byte[] message = LCPMessage.getMotorMessage(motor, -80, end);
|
||||
sendMessageAndState(message);
|
||||
}
|
||||
|
||||
private void reset(int motor) {
|
||||
byte[] message = LCPMessage.getResetMessage(motor);
|
||||
sendMessageAndState(message);
|
||||
}
|
||||
|
||||
private void readMotorState(int motor) {
|
||||
byte[] message = LCPMessage.getOutputStateMessage(motor);
|
||||
sendMessageAndState(message);
|
||||
}
|
||||
|
||||
private void getFirmwareVersion() {
|
||||
byte[] message = LCPMessage.getFirmwareVersionMessage();
|
||||
sendMessageAndState(message);
|
||||
}
|
||||
|
||||
private void findFiles(boolean findFirst, int handle) {
|
||||
byte[] message = LCPMessage.getFindFilesMessage(findFirst, handle, "*.*");
|
||||
sendMessageAndState(message);
|
||||
}
|
||||
|
||||
private void waitSomeTime(int millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendToast(String toastText) {
|
||||
Bundle myBundle = new Bundle();
|
||||
myBundle.putInt("message", DISPLAY_TOAST);
|
||||
myBundle.putString("toastText", toastText);
|
||||
sendBundle(myBundle);
|
||||
}
|
||||
|
||||
private void sendState(int message) {
|
||||
Bundle myBundle = new Bundle();
|
||||
myBundle.putInt("message", message);
|
||||
sendBundle(myBundle);
|
||||
}
|
||||
|
||||
private void sendBundle(Bundle myBundle) {
|
||||
Message myMessage = myHandler.obtainMessage();
|
||||
myMessage.setData(myBundle);
|
||||
uiHandler.sendMessage(myMessage);
|
||||
}
|
||||
|
||||
// receive messages from the UI
|
||||
final Handler myHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message myMessage) {
|
||||
|
||||
int message;
|
||||
|
||||
switch (message = myMessage.getData().getInt("message")) {
|
||||
case MOTOR_A:
|
||||
case MOTOR_B:
|
||||
case MOTOR_C:
|
||||
changeMotorSpeed(message, myMessage.getData().getInt("value1"));
|
||||
break;
|
||||
case MOTOR_B_ACTION:
|
||||
rotateTo(MOTOR_B, myMessage.getData().getInt("value1"));
|
||||
break;
|
||||
case MOTOR_RESET:
|
||||
reset(myMessage.getData().getInt("value1"));
|
||||
break;
|
||||
case START_PROGRAM:
|
||||
startProgram(myMessage.getData().getString("name"));
|
||||
break;
|
||||
case STOP_PROGRAM:
|
||||
stopProgram();
|
||||
break;
|
||||
case GET_PROGRAM_NAME:
|
||||
getProgramName();
|
||||
break;
|
||||
case DO_BEEP:
|
||||
doBeep(myMessage.getData().getInt("value1"), myMessage.getData().getInt("value2"));
|
||||
break;
|
||||
case DO_ACTION:
|
||||
doAction(0);
|
||||
break;
|
||||
case READ_MOTOR_STATE:
|
||||
readMotorState(myMessage.getData().getInt("value1"));
|
||||
break;
|
||||
case GET_FIRMWARE_VERSION:
|
||||
getFirmwareVersion();
|
||||
break;
|
||||
case FIND_FILES:
|
||||
findFiles(myMessage.getData().getInt("value1") == 0, myMessage.getData().getInt("value2"));
|
||||
break;
|
||||
case DISCONNECT:
|
||||
// send stop messages before closing
|
||||
changeMotorSpeed(MOTOR_A, 0);
|
||||
changeMotorSpeed(MOTOR_B, 0);
|
||||
changeMotorSpeed(MOTOR_C, 0);
|
||||
waitSomeTime(500);
|
||||
try {
|
||||
destroyNXTconnection();
|
||||
}
|
||||
catch (IOException e) { }
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright 2010 Guenther Hoelzl
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
public interface BTConnectable {
|
||||
|
||||
/**
|
||||
* @return true, when currently pairing
|
||||
*/
|
||||
public boolean isPairing();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* (Changes from original are) Copyright 2010 Guenther Hoelzl, Shawn Brown
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* (original work is) Copyright (C) 2009 The Android Open Source Project
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.Window;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* This Activity appears as a dialog. It lists any paired devices and
|
||||
* devices detected in the area after discovery. When a device is chosen
|
||||
* by the user, the MAC address of the device is sent back to the parent
|
||||
* Activity in the result Intent.
|
||||
*/
|
||||
public class DeviceListActivity extends Activity {
|
||||
static final String PAIRING = "pairing";
|
||||
|
||||
// Return Intent extra
|
||||
public static String DEVICE_NAME_AND_ADDRESS = "device_infos";
|
||||
public static String EXTRA_DEVICE_ADDRESS = "device_address";
|
||||
|
||||
// Member fields
|
||||
private BluetoothAdapter mBtAdapter;
|
||||
private ArrayAdapter<String> mPairedDevicesArrayAdapter;
|
||||
private ArrayAdapter<String> mNewDevicesArrayAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Setup the window
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
setContentView(R.layout.device_list);
|
||||
|
||||
// Set result CANCELED incase the user backs out
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
|
||||
// Initialize the button to perform device discovery
|
||||
Button scanButton = (Button) findViewById(R.id.button_scan);
|
||||
scanButton.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
doDiscovery();
|
||||
v.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize array adapters. One for already paired devices and
|
||||
// one for newly discovered devices
|
||||
mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
|
||||
mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
|
||||
|
||||
// Find and set up the ListView for paired devices
|
||||
ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
|
||||
pairedListView.setAdapter(mPairedDevicesArrayAdapter);
|
||||
pairedListView.setOnItemClickListener(mDeviceClickListener);
|
||||
|
||||
// Find and set up the ListView for newly discovered devices
|
||||
ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
|
||||
newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
|
||||
newDevicesListView.setOnItemClickListener(mDeviceClickListener);
|
||||
|
||||
// Register for broadcasts when a device is discovered
|
||||
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
|
||||
this.registerReceiver(mReceiver, filter);
|
||||
|
||||
// Register for broadcasts when discovery has finished
|
||||
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
|
||||
this.registerReceiver(mReceiver, filter);
|
||||
|
||||
// Get the local Bluetooth adapter
|
||||
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
|
||||
// Get a set of currently paired devices
|
||||
Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
|
||||
|
||||
// If there are paired devices, add each one to the ArrayAdapter
|
||||
boolean legoDevicesFound = false;
|
||||
|
||||
if (pairedDevices.size() > 0) {
|
||||
findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
|
||||
for (BluetoothDevice device : pairedDevices) {
|
||||
// only add LEGO devices
|
||||
if (device.getAddress().startsWith(BTCommunicator.OUI_LEGO)) {
|
||||
legoDevicesFound = true;
|
||||
mPairedDevicesArrayAdapter.add(device.getName() + "-" + device.getAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (legoDevicesFound == false) {
|
||||
String noDevices = getResources().getText(R.string.none_paired).toString();
|
||||
mPairedDevicesArrayAdapter.add(noDevices);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
// Make sure we're not doing discovery anymore
|
||||
if (mBtAdapter != null) {
|
||||
mBtAdapter.cancelDiscovery();
|
||||
}
|
||||
|
||||
// Unregister broadcast listeners
|
||||
this.unregisterReceiver(mReceiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start device discover with the BluetoothAdapter
|
||||
*/
|
||||
private void doDiscovery() {
|
||||
|
||||
// Indicate scanning in the title
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
setTitle(R.string.scanning);
|
||||
|
||||
// Turn on sub-title for new devices
|
||||
findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
|
||||
|
||||
// If we're already discovering, stop it
|
||||
if (mBtAdapter.isDiscovering()) {
|
||||
mBtAdapter.cancelDiscovery();
|
||||
}
|
||||
|
||||
// Request discover from BluetoothAdapter
|
||||
mBtAdapter.startDiscovery();
|
||||
}
|
||||
|
||||
// The on-click listener for all devices in the ListViews
|
||||
private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
|
||||
public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
|
||||
|
||||
String info = ((TextView) v).getText().toString();
|
||||
// did we choose a correct name and address?
|
||||
if (info.lastIndexOf('-') != info.length()-18)
|
||||
return;
|
||||
|
||||
// Cancel discovery because it's costly and we're about to connect
|
||||
mBtAdapter.cancelDiscovery();
|
||||
// Get the device MAC address, this is the text after the last '-' character
|
||||
String address = info.substring(info.lastIndexOf('-')+1);
|
||||
// Create the result Intent and include the infos
|
||||
Intent intent = new Intent();
|
||||
Bundle data = new Bundle();
|
||||
data.putString(DEVICE_NAME_AND_ADDRESS, info);
|
||||
data.putString(EXTRA_DEVICE_ADDRESS, address);
|
||||
data.putBoolean(PAIRING,av.getId()==R.id.new_devices);
|
||||
intent.putExtras(data);
|
||||
// Set result and finish this Activity
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
// The BroadcastReceiver that listens for discovered devices and
|
||||
// changes the title when discovery is finished
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
|
||||
// When discovery finds a device
|
||||
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
|
||||
// Get the BluetoothDevice object from the Intent
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
// If it's already paired, skip it, because it's been listed already
|
||||
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
|
||||
mNewDevicesArrayAdapter.add(device.getName() + "-" + device.getAddress());
|
||||
}
|
||||
// When discovery is finished, change the Activity title
|
||||
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
setTitle(R.string.select_device);
|
||||
if (mNewDevicesArrayAdapter.getCount() == 0) {
|
||||
String noDevices = getResources().getText(R.string.none_found).toString();
|
||||
mNewDevicesArrayAdapter.add(noDevices);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright 2010 Guenther Hoelzl
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
public interface DialogListener {
|
||||
|
||||
public void dialogUpdate(String fileName);
|
||||
}
|
||||
93
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/EnableBT.java
Normal file
93
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/EnableBT.java
Normal file
@@ -0,0 +1,93 @@
|
||||
package com.lego.minddroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
public class EnableBT extends Activity { //currently unused. Will be implemented to allow connection without user having to say "Yes turn bt on" (when it isn't)
|
||||
|
||||
boolean processStarted = false;
|
||||
StatusReciever statusReciever;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
|
||||
statusReciever = new StatusReciever();
|
||||
registerReceiver(statusReciever, new IntentFilter("android.bluetooth.adapter.action.STATE_CHANGED"));
|
||||
processStarted = turnOnBt();
|
||||
if (!processStarted) {
|
||||
sendFailureStatus();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean turnOnBt() {
|
||||
|
||||
return BluetoothAdapter.getDefaultAdapter().enable();
|
||||
}
|
||||
|
||||
public void sendFailureStatus() {
|
||||
|
||||
Log.d("EnableBT sendFailureStatus", "RESULT_CANCELED");
|
||||
this.setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
|
||||
public void sendSuccessStatus() {
|
||||
Log.d("EnableBT sendSuccessStatus", "RESULT_OK");
|
||||
this.setResult(Activity.RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
try {
|
||||
unregisterReceiver(statusReciever);
|
||||
} catch (Exception e) {
|
||||
// not registered
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
try {
|
||||
unregisterReceiver(statusReciever);
|
||||
} catch (Exception e) {
|
||||
// not registered
|
||||
}
|
||||
super.onDestroy();
|
||||
|
||||
}
|
||||
|
||||
public class StatusReciever extends BroadcastReceiver {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
public final static String STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d("EnableBT statusReciever", "onReceive");
|
||||
if (intent.getAction().equals(STATE_CHANGED)) {
|
||||
Log.d("EnableBT statusReciever", "ACTION_STATE_CHANGED");
|
||||
sendSuccessStatus();
|
||||
} else {
|
||||
Log.d("EnableBT statusReciever", "fail: " + intent.getAction());
|
||||
sendFailureStatus();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright 2010 Guenther Hoelzl, Shawn Brown
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
class FileDialog {
|
||||
|
||||
private Activity myActivity;
|
||||
private List<String> myList;
|
||||
private int programNr = -1;
|
||||
private CharSequence[] programs;
|
||||
|
||||
public FileDialog(Activity activity, List<String> list) {
|
||||
myActivity = activity;
|
||||
myList = list;
|
||||
// copy Strings from list to CharSequence array
|
||||
programs = new CharSequence[myList.size()];
|
||||
Iterator<String> iterator = myList.iterator();
|
||||
int position = 0;
|
||||
while(iterator.hasNext()) {
|
||||
programs[position++] = iterator.next();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the dialog
|
||||
* @param startStop when true shows another title (for leJOSMINDdroid)
|
||||
*/
|
||||
public void show(boolean startStop) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(myActivity);
|
||||
builder.setTitle(myActivity.getResources().getString(startStop ? R.string.file_dialog_title_1 : R.string.file_dialog_title_2));
|
||||
builder.setItems(programs, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int item) {
|
||||
startProgram(item);
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void startProgram(int number) {
|
||||
((MINDdroidCV) myActivity).startProgram((String) programs[number]);
|
||||
}
|
||||
|
||||
}
|
||||
797
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/GameView.java
Normal file
797
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/GameView.java
Normal file
@@ -0,0 +1,797 @@
|
||||
/**
|
||||
* Copyright 2010 Guenther Hoelzl, Shawn Brown
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Vibrator;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
|
||||
|
||||
private static final int SHORT_PRESS_MAX_DURATION = 750;
|
||||
|
||||
class GameThread extends Thread {
|
||||
/** time between each redraw */
|
||||
private static final int REDRAW_SCHED = 100;
|
||||
private int ICON_MAX_SIZE;
|
||||
private int ICON_MIN_SIZE;
|
||||
|
||||
private int GOAL_HEIGHT;
|
||||
private int GOAL_WIDTH;
|
||||
private static final int HAPTIC_FEEDBACK_LENGTH = 30;
|
||||
/**
|
||||
* is tilt icon in goal
|
||||
*/
|
||||
boolean mInGoal = true;
|
||||
|
||||
/**
|
||||
* to notify users when leaving goal
|
||||
*/
|
||||
Vibrator mHapticFeedback;
|
||||
|
||||
/** The drawable to use as the background of the animation canvas */
|
||||
private Bitmap mBackgroundImage;
|
||||
|
||||
private Drawable mIconOrange;
|
||||
|
||||
private Drawable mIconWhite;
|
||||
|
||||
private Bitmap mTarget;
|
||||
|
||||
private Bitmap mTargetInactive;
|
||||
|
||||
private Bitmap mActionButton;
|
||||
|
||||
private Bitmap mActionDownButton;
|
||||
|
||||
/**
|
||||
* Current height of the surface/canvas.
|
||||
*
|
||||
* @see #setSurfaceSize
|
||||
*/
|
||||
private int mCanvasHeight = 1;
|
||||
|
||||
/**
|
||||
* Current width of the surface/canvas.
|
||||
*
|
||||
* @see #setSurfaceSize
|
||||
*/
|
||||
private int mCanvasWidth = 1;
|
||||
|
||||
/** Message handler used by thread to interact with TextView */
|
||||
private Handler mHandler;
|
||||
|
||||
/** Used to figure out elapsed time between frames */
|
||||
private long mLastTime;
|
||||
|
||||
/** Indicate whether the surface has been created & is ready to draw */
|
||||
private boolean mRun = false;
|
||||
|
||||
/** Handle to the surface manager object we interact with */
|
||||
private SurfaceHolder mSurfaceHolder;
|
||||
|
||||
/** X of motion indicator */
|
||||
private float mX;
|
||||
|
||||
/** Y of motion indicator */
|
||||
private float mY;
|
||||
|
||||
/**
|
||||
* mIconSize grows within target between ICON_MIN_SIZE and ICON_MAX_SIZE
|
||||
*/
|
||||
private int mGrowAdjust;
|
||||
|
||||
/**
|
||||
* time when haptic feedback will stop - needed to ensure we don't take
|
||||
* tilt measurements while handset if vibrating
|
||||
*/
|
||||
//private long mFeedbackEnd = 0;
|
||||
|
||||
/**
|
||||
* track how long since we redrew screen
|
||||
*/
|
||||
long mElapsedSinceDraw = 0;
|
||||
|
||||
/**
|
||||
* track how long since we redrew screen
|
||||
*/
|
||||
long mElapsedSinceNXTCommand = 0;
|
||||
|
||||
/**
|
||||
* count how many times we took tilt readings in 100ms so we can average
|
||||
* position
|
||||
*/
|
||||
int mAvCount = 0;
|
||||
/**
|
||||
* time when tilt icon should change color
|
||||
*/
|
||||
long mNextPulse = 0;
|
||||
|
||||
/* holder for current color in pulse effect* */
|
||||
Drawable mPulsingTiltIcon;
|
||||
|
||||
/** was action button just pressed */
|
||||
boolean mActionPressed = false;
|
||||
|
||||
/** */
|
||||
boolean mToNXT = false;
|
||||
|
||||
/** buffers to hold tilt readings for averaging */
|
||||
float mNumAcX;
|
||||
float mNumAcY;
|
||||
|
||||
/** digital filtering variables **/
|
||||
private float xX0 = 0;
|
||||
private float xX1 = 0;
|
||||
private float xY0 = 0;
|
||||
private float xY1 = 0;
|
||||
|
||||
private float yX0 = 0;
|
||||
private float yX1 = 0;
|
||||
private float yY0 = 0;
|
||||
private float yY1 = 0;
|
||||
public boolean longPressCancel;
|
||||
|
||||
public GameThread(SurfaceHolder surfaceHolder, Context context, Vibrator vibrator, Handler handler) {
|
||||
// get handles to some important objects
|
||||
mHapticFeedback = vibrator;
|
||||
mSurfaceHolder = surfaceHolder;
|
||||
mHandler = handler;
|
||||
Resources res = context.getResources();
|
||||
mIconOrange = context.getResources().getDrawable(R.drawable.orange);
|
||||
// load background image as a Bitmap instead of a Drawable b/c
|
||||
// we don't need to transform it and it's faster to draw this way
|
||||
mIconWhite = context.getResources().getDrawable(R.drawable.white);
|
||||
mTarget = BitmapFactory.decodeResource(res, R.drawable.target_no_orange_dot);
|
||||
mTargetInactive = BitmapFactory.decodeResource(res, R.drawable.target);
|
||||
mActionButton = BitmapFactory.decodeResource(res, R.drawable.action_btn_up);
|
||||
mActionDownButton = BitmapFactory.decodeResource(res, R.drawable.action_btn_down);
|
||||
mBackgroundImage = BitmapFactory.decodeResource(res, R.drawable.background_2);
|
||||
}
|
||||
|
||||
private int calcGrowAdjust(float mX2, float mY2) {
|
||||
|
||||
int xDistanceFromCenter = (int) Math.abs((mCanvasWidth / 2) - mX2);
|
||||
int yDistanceFromCenter = (int) Math.abs(((mCanvasHeight - mActionButton.getHeight()) / 2) - mY2);
|
||||
|
||||
if (xDistanceFromCenter > ICON_MAX_SIZE || yDistanceFromCenter > ICON_MAX_SIZE) {
|
||||
return ICON_MAX_SIZE;
|
||||
}
|
||||
|
||||
if (xDistanceFromCenter > yDistanceFromCenter) {
|
||||
return (xDistanceFromCenter > ICON_MIN_SIZE ? xDistanceFromCenter : ICON_MIN_SIZE);
|
||||
}
|
||||
|
||||
return (yDistanceFromCenter > ICON_MIN_SIZE ? yDistanceFromCenter : ICON_MIN_SIZE);
|
||||
}
|
||||
|
||||
private int calcNextPulse() {
|
||||
|
||||
int xDistanceFromGoal = 0;
|
||||
int yDistanceFromGoal = 0;
|
||||
|
||||
if (mX > mCanvasWidth / 2) {
|
||||
xDistanceFromGoal = (int) ((mX - (mCanvasWidth / 2)) - (GOAL_WIDTH / 2));
|
||||
|
||||
} else {
|
||||
xDistanceFromGoal = (int) ((mCanvasWidth / 2) - mX) - (GOAL_WIDTH / 2);
|
||||
}
|
||||
xDistanceFromGoal += ICON_MAX_SIZE / 2;//adjust for icon width so that when icon touches outer edge, it will be at 100%.
|
||||
|
||||
if (mY > ((mCanvasHeight - mActionButton.getHeight()) / 2)) {
|
||||
yDistanceFromGoal = (int) ((mY - ((mCanvasHeight - mActionButton.getHeight()) / 2)) - (GOAL_WIDTH / 2));//GOAL_WIDTH ok for y when square
|
||||
} else {
|
||||
yDistanceFromGoal = (int) (((mCanvasHeight - mActionButton.getHeight()) / 2) - mY - (GOAL_WIDTH / 2));
|
||||
|
||||
}
|
||||
yDistanceFromGoal += ICON_MAX_SIZE / 2;//adjust for icon width so that when icon touches outer edge, it will be at 100%.
|
||||
|
||||
double mOneSideGameWidth = (mCanvasWidth - GOAL_WIDTH) / 2;//
|
||||
|
||||
double mOneSideGameHeight = ((mCanvasHeight - mActionButton.getHeight()) / 2) - (GOAL_WIDTH / 2);// if it's square --OK
|
||||
|
||||
double mPercentToXEdge = (xDistanceFromGoal / (mOneSideGameWidth)) * 100;
|
||||
double mPercentToYEdge = (yDistanceFromGoal / mOneSideGameHeight) * 100;
|
||||
|
||||
float closeEdge = (float) (mPercentToXEdge > mPercentToYEdge ? mPercentToXEdge : mPercentToYEdge);
|
||||
return (int) (800 - ((closeEdge * 8)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws move indicator, button and background to the provided Canvas.
|
||||
*/
|
||||
private void doDraw(Canvas mCanvas) {
|
||||
|
||||
if (!mActivity.isConnected()) {
|
||||
|
||||
// draw the background
|
||||
mCanvas.drawBitmap(mBackgroundImage, 0, 0, null);
|
||||
//draw pressed action button
|
||||
mCanvas.drawBitmap(mActionDownButton, 0, mCanvasHeight - mActionButton.getHeight(), null);
|
||||
//draw icon in goal
|
||||
// draw the goal
|
||||
mCanvas.drawBitmap(mTargetInactive, (mCanvasWidth - mTarget.getWidth()) / 2, ((mCanvasHeight - mActionButton.getHeight()) / 2)
|
||||
- (mTarget.getHeight() / 2), null);
|
||||
} else {
|
||||
|
||||
// Draw the background image. Operations on the Canvas accumulate
|
||||
if (thread.isInGoal()) { // icon is in goal
|
||||
mInGoal = true;
|
||||
mGrowAdjust = calcGrowAdjust(mX, mY);
|
||||
} else {
|
||||
mGrowAdjust = ICON_MAX_SIZE;
|
||||
if (mInGoal) {// was in goal before
|
||||
mInGoal = false;
|
||||
vibrate();
|
||||
}
|
||||
}
|
||||
|
||||
// draw the background
|
||||
mCanvas.drawBitmap(mBackgroundImage, 0, 0, null);
|
||||
|
||||
// draw the action button
|
||||
mCanvas.drawBitmap(mActionPressed ? mActionDownButton : mActionButton, 0, mCanvasHeight - mActionButton.getHeight(), null);
|
||||
mActionPressed = false;
|
||||
|
||||
// draw the goal
|
||||
mCanvas.drawBitmap(mTarget, (mCanvasWidth - mTarget.getWidth()) / 2,
|
||||
((mCanvasHeight - mActionButton.getHeight()) / 2) - (mTarget.getHeight() / 2), null);
|
||||
|
||||
// update the icon location and draw (or blink) it
|
||||
if (mInGoal) {
|
||||
|
||||
mIconOrange.setBounds((int) mX - (mGrowAdjust / 2), (int) mY - ((mGrowAdjust / 2)), ((int) mX + (mGrowAdjust / 2)), (int) mY
|
||||
+ (mGrowAdjust / 2));
|
||||
mIconOrange.draw(mCanvas);
|
||||
|
||||
} else {
|
||||
|
||||
// boundary checking, don't want the move_icon going off-screen.
|
||||
if (mX + ICON_MAX_SIZE / 2 >= mCanvasWidth) {// set at outer edge
|
||||
|
||||
mX = mCanvasWidth - (ICON_MAX_SIZE / 2);
|
||||
} else if (mX - (ICON_MAX_SIZE / 2) < 0) {
|
||||
mX = ICON_MAX_SIZE / 2;
|
||||
}
|
||||
|
||||
// boundary checking, don't want the move_icon rolling
|
||||
// off-screen.
|
||||
if (mY + ICON_MAX_SIZE / 2 >= (mCanvasHeight - mActionButton.getHeight())) {// set at outer edge
|
||||
|
||||
mY = mCanvasHeight - mActionButton.getHeight() - ICON_MAX_SIZE / 2;
|
||||
} else if (mY - ICON_MAX_SIZE / 2 < 0) {
|
||||
mY = ICON_MAX_SIZE / 2;
|
||||
}
|
||||
|
||||
if (mLastTime > mNextPulse) {
|
||||
|
||||
mPulsingTiltIcon = mPulsingTiltIcon == mIconOrange ? mIconWhite : mIconOrange;
|
||||
|
||||
mNextPulse = mPulsingTiltIcon == mIconOrange ? mLastTime + calcNextPulse() : mLastTime + 90;
|
||||
}
|
||||
|
||||
mPulsingTiltIcon.setBounds((int) mX - (mGrowAdjust / 2), (int) mY - (mGrowAdjust / 2), ((int) mX + mGrowAdjust / 2),
|
||||
((int) mY + mGrowAdjust / 2));
|
||||
mPulsingTiltIcon.draw(mCanvas);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the game, setting parameters for the current difficulty.
|
||||
*/
|
||||
public void doStart() {
|
||||
synchronized (mSurfaceHolder) {
|
||||
|
||||
mX = mCanvasWidth / 2;
|
||||
mY = (mCanvasHeight - mActionButton.getHeight()) / 2;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the animation.
|
||||
*/
|
||||
public void pause() {
|
||||
thread.setRunning(false);
|
||||
synchronized (mSurfaceHolder) {
|
||||
|
||||
}
|
||||
boolean retry = true;
|
||||
getThread().setRunning(false);
|
||||
while (retry) {
|
||||
try {
|
||||
getThread().join();
|
||||
retry = false;
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores game state from the indicated Bundle. Typically called when
|
||||
* the Activity is being restored after having been previously
|
||||
* destroyed.
|
||||
*
|
||||
* @param savedState
|
||||
* Bundle containing the game state
|
||||
*/
|
||||
public synchronized void restoreState(Bundle savedState) {
|
||||
synchronized (mSurfaceHolder) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
// Log.d(TAG, "--run--");
|
||||
while (mRun) {
|
||||
|
||||
// sleep some time
|
||||
try {
|
||||
Thread.sleep(30);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
updateTime();
|
||||
updateMoveIndicator(mAccelX, mAccelY);
|
||||
doActionButtonFeedback();
|
||||
// is it time to update the screen?
|
||||
if (mElapsedSinceDraw > REDRAW_SCHED) {
|
||||
|
||||
//is it time to update motor movement?
|
||||
if (mElapsedSinceNXTCommand > MINDdroidCV.UPDATE_TIME) {
|
||||
//calculate and send command to move motors
|
||||
doMotorMovement(-mNumAcY, -mNumAcX);
|
||||
}
|
||||
|
||||
lockCanvasAndDraw();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doActionButtonFeedback() {
|
||||
if((mLastTime-mTimeActionDown)>SHORT_PRESS_MAX_DURATION && longPressCancel!=true) {
|
||||
vibrate();
|
||||
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
vibrate();
|
||||
longPressCancel=true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void lockCanvasAndDraw() {
|
||||
Canvas c = null;
|
||||
try {
|
||||
c = mSurfaceHolder.lockCanvas(null);
|
||||
synchronized (mSurfaceHolder) {
|
||||
doDraw(c);
|
||||
|
||||
}
|
||||
} finally {
|
||||
// do this in a finally so that if an exception is
|
||||
// thrown
|
||||
// during the above, we don't leave the Surface in an
|
||||
// inconsistent state
|
||||
if (c != null) {
|
||||
|
||||
mSurfaceHolder.unlockCanvasAndPost(c);
|
||||
|
||||
mElapsedSinceDraw = 0;// mLastTime set to current
|
||||
// moment in updateTime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump game state to the provided Bundle. Typically called when the
|
||||
* Activity is being suspended.
|
||||
*
|
||||
* @return Bundle with this view's state
|
||||
*/
|
||||
public Bundle saveState(Bundle map) {
|
||||
synchronized (mSurfaceHolder) {
|
||||
if (map != null) {
|
||||
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to signal the thread whether it should be running or not.
|
||||
* Passing true allows the thread to run; passing false will shut it
|
||||
* down if it's already running. Calling start() after this was most
|
||||
* recently called with false will result in an immediate shutdown.
|
||||
*
|
||||
* @param b
|
||||
* true to run, false to shut down
|
||||
*/
|
||||
public void setRunning(boolean b) {
|
||||
mRun = b;
|
||||
}
|
||||
|
||||
public void doMotorMovement(float pitch, float roll) {
|
||||
|
||||
|
||||
int left=0;
|
||||
int right=0;
|
||||
// only when phone is little bit tilted
|
||||
if ((Math.abs(pitch) > 10.0) || (Math.abs(roll) > 10.0)) {
|
||||
|
||||
// limit pitch and roll
|
||||
if (pitch > 33.3){
|
||||
pitch = (float) 33.3;
|
||||
}else if (pitch < -33.3){
|
||||
pitch = (float) -33.3;}
|
||||
|
||||
if (roll > 33.3){
|
||||
roll = (float) 33.3;
|
||||
}else if (roll < -33.3){
|
||||
roll = (float) -33.3;
|
||||
}
|
||||
|
||||
// when pitch is very small then do a special turning function
|
||||
if (Math.abs(pitch) > 10.0) {
|
||||
left = (int) Math.round(3.3 * pitch * (1.0 + roll / 60.0));
|
||||
right = (int) Math.round(3.3 * pitch * (1.0 - roll / 60.0));
|
||||
} else {
|
||||
left = (int) Math.round(3.3 * roll - Math.signum(roll) * 3.3 * Math.abs(pitch));
|
||||
right = -left;
|
||||
}
|
||||
|
||||
// limit the motor outputs
|
||||
if (left > 100)
|
||||
left = 100;
|
||||
else if (left < -100)
|
||||
left = -100;
|
||||
|
||||
if (right > 100)
|
||||
right = 100;
|
||||
else if (right < -100)
|
||||
right = -100;
|
||||
|
||||
// Reverse the motors when driving back at the shooterbot and the NXJ model
|
||||
if (pitch < -10 && (mActivity.mRobotType==R.id.robot_type_1 || mActivity.mRobotType==R.id.robot_type_4)) {
|
||||
|
||||
int back_left=right;
|
||||
int back_right=left;
|
||||
|
||||
left=back_left;
|
||||
right=back_right;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mActivity.updateMotorControl(left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the game mode. That is, whether we are running, paused, etc.
|
||||
*
|
||||
* @see #setState(int, CharSequence)
|
||||
* @param mode
|
||||
* one of the STATE_* constants
|
||||
*/
|
||||
public void setState(int mode) {
|
||||
synchronized (mSurfaceHolder) {
|
||||
setState(mode, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the game mode. That is, whether we are running, paused, in the
|
||||
* failure state, in the victory state, etc.
|
||||
*
|
||||
* @param mode
|
||||
* one of the STATE
|
||||
* @param message
|
||||
* string to add to screen or null
|
||||
*/
|
||||
public void setState(int mode, CharSequence message) {
|
||||
|
||||
synchronized (mSurfaceHolder) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Callback invoked when the surface dimensions change. */
|
||||
public void setSurfaceSize(int width, int height) {
|
||||
// synchronized to make sure these all change atomically
|
||||
synchronized (mSurfaceHolder) {
|
||||
mCanvasWidth = width;
|
||||
mCanvasHeight = height;
|
||||
float mAHeight = mActionButton.getHeight();
|
||||
float mAWidth = mActionButton.getWidth();
|
||||
mActionButton = Bitmap.createScaledBitmap(mActionButton, width, (Math.round((width * (mAHeight / mAWidth)))), true);
|
||||
mActionDownButton = Bitmap.createScaledBitmap(mActionDownButton, width, (Math.round((width * (mAHeight / mAWidth)))), true);
|
||||
// don't forget to resize the background image
|
||||
mBackgroundImage = Bitmap.createScaledBitmap(mBackgroundImage, width, height, true);
|
||||
|
||||
int temp_ratio = mCanvasWidth / 64;
|
||||
GOAL_WIDTH = mCanvasWidth / temp_ratio;
|
||||
|
||||
ICON_MAX_SIZE = (GOAL_WIDTH / 8) * 6;
|
||||
ICON_MIN_SIZE = (GOAL_WIDTH / 4);
|
||||
|
||||
temp_ratio = mCanvasHeight / 64;
|
||||
GOAL_HEIGHT = mCanvasHeight / temp_ratio;
|
||||
|
||||
mTarget = Bitmap.createScaledBitmap(mTarget, GOAL_WIDTH, GOAL_HEIGHT, true);
|
||||
mTargetInactive = Bitmap.createScaledBitmap(mTargetInactive, GOAL_WIDTH, GOAL_HEIGHT, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes from a pause.
|
||||
*/
|
||||
public void unpause() {
|
||||
// Move the real time clock up to now
|
||||
synchronized (mSurfaceHolder) {
|
||||
mLastTime = System.currentTimeMillis() + 100;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateTime() {// use for blinking
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
// Do nothing if mLastTime is in the future.
|
||||
// This allows the game-start to delay the start
|
||||
// by 100ms or whatever.
|
||||
if (mLastTime > now)
|
||||
return;
|
||||
|
||||
// double elapsed = (now - mLastTime) / 1000.0;
|
||||
long elapsed = now - mLastTime;
|
||||
// elapsedSincePulse += elapsed;
|
||||
mElapsedSinceDraw += elapsed;
|
||||
mElapsedSinceNXTCommand += elapsed;
|
||||
mLastTime = now;
|
||||
|
||||
}
|
||||
|
||||
public void vibrate() {
|
||||
mHapticFeedback.vibrate(HAPTIC_FEEDBACK_LENGTH);
|
||||
//mFeedbackEnd = System.currentTimeMillis() + HAPTIC_FEEDBACK_LENGTH + 15;
|
||||
|
||||
}
|
||||
|
||||
void updateMoveIndicator(float mAcX, float mAcY) {
|
||||
|
||||
// IIR filtering for x direction
|
||||
xX1 = xX0;
|
||||
xX0 = mAcX;
|
||||
xY1 = xY0;
|
||||
xY0 = (float) 0.07296293 * xX0 + (float) 0.07296293 * xX1 + (float) 0.8540807 * xY1;
|
||||
mAcX = xY0;
|
||||
|
||||
// IIR filtering for y direction
|
||||
yX1 = yX0;
|
||||
yX0 = mAcY;
|
||||
yY1 = yY0;
|
||||
yY0 = (float) 0.07296293 * yX0 + (float) 0.07296293 * yX1 + (float) 0.8540807 * yY1;
|
||||
mAcY = yY0;
|
||||
|
||||
mX = ((mCanvasWidth / 2)) + (int) ((mAcX / 10) * (mCanvasWidth / 10));
|
||||
|
||||
mNumAcX = mAcX;
|
||||
|
||||
mY = (((mCanvasHeight - mActionButton.getHeight()) / 2)) + (int) ((mAcY / 10) * ((mCanvasHeight - mActionButton.getHeight()) / 10));
|
||||
|
||||
mNumAcY = mAcY;
|
||||
|
||||
}
|
||||
|
||||
public boolean isInGoal() {
|
||||
|
||||
if ((mCanvasWidth - mTarget.getWidth()) / 2 > mX || (mCanvasWidth + mTarget.getWidth()) / 2 < mX) {// x is not within goal
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (((mCanvasHeight - (mActionButton.getHeight() + (GOAL_HEIGHT))) / 2 > mY || ((mCanvasHeight - mActionButton.getHeight()) + (GOAL_HEIGHT)) / 2 < mY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** used for logging */
|
||||
private static final String TAG = GameView.class.getName();;
|
||||
|
||||
private MINDdroidCV mActivity;
|
||||
|
||||
/** The thread that actually draws the animation */
|
||||
private GameThread thread;
|
||||
|
||||
private SensorManager mSensorManager;
|
||||
|
||||
/** orientation (tilt) readings */
|
||||
private float mAccelX = 0;
|
||||
private float mAccelY = 0;
|
||||
private float mAccelZ = 0; // heading
|
||||
/**time that action button was pressed - used to calc long or short press */
|
||||
long mTimeActionDown=0;
|
||||
|
||||
private final SensorEventListener mSensorAccelerometer = new SensorEventListener() {
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
|
||||
mAccelX = 0 - event.values[2];
|
||||
mAccelY = 0 - event.values[1];
|
||||
mAccelZ = event.values[0];
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
Context context;
|
||||
public GameView(Context context, MINDdroidCV uiActivity) {
|
||||
super(context);
|
||||
|
||||
mActivity = uiActivity;
|
||||
mSensorManager = (SensorManager) mActivity.getSystemService(Context.SENSOR_SERVICE);
|
||||
// register our interest in hearing about changes to our surface
|
||||
SurfaceHolder holder = getHolder();
|
||||
holder.setKeepScreenOn(true);
|
||||
holder.addCallback(this);
|
||||
this.context=context;
|
||||
// create thread only; it's started in surfaceCreated()
|
||||
thread = new GameThread(holder, context, (Vibrator) uiActivity.getSystemService(Context.VIBRATOR_SERVICE), new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message m) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
setFocusable(true); // make sure we get key events
|
||||
}
|
||||
|
||||
public GameThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
|
||||
if (event.getY() > this.getHeight() - getThread().mActionButton.getHeight()) {
|
||||
|
||||
switch (event.getAction()){
|
||||
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mTimeActionDown=System.currentTimeMillis();
|
||||
getThread().longPressCancel=false;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
long mTimeActionUp=System.currentTimeMillis();
|
||||
// short press
|
||||
if (mTimeActionUp-mTimeActionDown<SHORT_PRESS_MAX_DURATION) {
|
||||
getThread().longPressCancel=true;
|
||||
mActivity.actionButtonPressed();
|
||||
}
|
||||
// long press
|
||||
else {
|
||||
mActivity.actionButtonLongPress();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void registerListener() {
|
||||
List<Sensor> sensorList;
|
||||
// register orientation sensor
|
||||
sensorList = mSensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
|
||||
mSensorManager.registerListener(mSensorAccelerometer, sensorList.get(0), SensorManager.SENSOR_DELAY_GAME);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
getThread().setSurfaceSize(width, height);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
|
||||
if (getThread().getState()!=Thread.State.TERMINATED){
|
||||
|
||||
getThread().setRunning(true);
|
||||
|
||||
getThread().start();
|
||||
} else{
|
||||
thread = new GameThread(holder, context , (Vibrator) mActivity.getSystemService(Context.VIBRATOR_SERVICE), new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message m) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
getThread().setRunning(true);
|
||||
getThread().start();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.i (TAG,"surfaceDestroyed");
|
||||
boolean retry = true;
|
||||
getThread().setRunning(false);
|
||||
while (retry) {
|
||||
try {
|
||||
getThread().join();
|
||||
retry = false;
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterListener() {
|
||||
mSensorManager.unregisterListener(mSensorAccelerometer);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
317
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/LCPMessage.java
Normal file
317
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/LCPMessage.java
Normal file
@@ -0,0 +1,317 @@
|
||||
/**
|
||||
* Copyright 2010, 2011 Guenther Hoelzl, Shawn Brown
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
|
||||
/**
|
||||
* Class for composing the proper messages for simple
|
||||
* communication over bluetooth
|
||||
*/
|
||||
public class LCPMessage {
|
||||
|
||||
// the folowing constants were taken from the leJOS project (http://www.lejos.org)
|
||||
|
||||
// Command types constants. Indicates type of packet being sent or received.
|
||||
public static byte DIRECT_COMMAND_REPLY = 0x00;
|
||||
public static byte SYSTEM_COMMAND_REPLY = 0x01;
|
||||
public static byte REPLY_COMMAND = 0x02;
|
||||
public static byte DIRECT_COMMAND_NOREPLY = (byte)0x80; // Avoids ~100ms latency
|
||||
public static byte SYSTEM_COMMAND_NOREPLY = (byte)0x81; // Avoids ~100ms latency
|
||||
|
||||
// Direct Commands
|
||||
public static final byte START_PROGRAM = 0x00;
|
||||
public static final byte STOP_PROGRAM = 0x01;
|
||||
public static final byte PLAY_SOUND_FILE = 0x02;
|
||||
public static final byte PLAY_TONE = 0x03;
|
||||
public static final byte SET_OUTPUT_STATE = 0x04;
|
||||
public static final byte SET_INPUT_MODE = 0x05;
|
||||
public static final byte GET_OUTPUT_STATE = 0x06;
|
||||
public static final byte GET_INPUT_VALUES = 0x07;
|
||||
public static final byte RESET_SCALED_INPUT_VALUE = 0x08;
|
||||
public static final byte MESSAGE_WRITE = 0x09;
|
||||
public static final byte RESET_MOTOR_POSITION = 0x0A;
|
||||
public static final byte GET_BATTERY_LEVEL = 0x0B;
|
||||
public static final byte STOP_SOUND_PLAYBACK = 0x0C;
|
||||
public static final byte KEEP_ALIVE = 0x0D;
|
||||
public static final byte LS_GET_STATUS = 0x0E;
|
||||
public static final byte LS_WRITE = 0x0F;
|
||||
public static final byte LS_READ = 0x10;
|
||||
public static final byte GET_CURRENT_PROGRAM_NAME = 0x11;
|
||||
public static final byte MESSAGE_READ = 0x13;
|
||||
|
||||
// NXJ additions
|
||||
public static byte NXJ_DISCONNECT = 0x20;
|
||||
public static byte NXJ_DEFRAG = 0x21;
|
||||
|
||||
// MINDdroidConnector additions
|
||||
public static final byte SAY_TEXT = 0x30;
|
||||
public static final byte VIBRATE_PHONE = 0x31;
|
||||
public static final byte ACTION_BUTTON = 0x32;
|
||||
|
||||
// System Commands:
|
||||
public static final byte OPEN_READ = (byte)0x80;
|
||||
public static final byte OPEN_WRITE = (byte)0x81;
|
||||
public static final byte READ = (byte)0x82;
|
||||
public static final byte WRITE = (byte)0x83;
|
||||
public static final byte CLOSE = (byte)0x84;
|
||||
public static final byte DELETE = (byte)0x85;
|
||||
public static final byte FIND_FIRST = (byte)0x86;
|
||||
public static final byte FIND_NEXT = (byte)0x87;
|
||||
public static final byte GET_FIRMWARE_VERSION = (byte)0x88;
|
||||
public static final byte OPEN_WRITE_LINEAR = (byte)0x89;
|
||||
public static final byte OPEN_READ_LINEAR = (byte)0x8A;
|
||||
public static final byte OPEN_WRITE_DATA = (byte)0x8B;
|
||||
public static final byte OPEN_APPEND_DATA = (byte)0x8C;
|
||||
public static final byte BOOT = (byte)0x97;
|
||||
public static final byte SET_BRICK_NAME = (byte)0x98;
|
||||
public static final byte GET_DEVICE_INFO = (byte)0x9B;
|
||||
public static final byte DELETE_USER_FLASH = (byte)0xA0;
|
||||
public static final byte POLL_LENGTH = (byte)0xA1;
|
||||
public static final byte POLL = (byte)0xA2;
|
||||
|
||||
public static final byte NXJ_FIND_FIRST = (byte)0xB6;
|
||||
public static final byte NXJ_FIND_NEXT = (byte)0xB7;
|
||||
public static final byte NXJ_PACKET_MODE = (byte)0xff;
|
||||
|
||||
// Error codes
|
||||
public static final byte MAILBOX_EMPTY = (byte)0x40;
|
||||
public static final byte FILE_NOT_FOUND = (byte)0x86;
|
||||
public static final byte INSUFFICIENT_MEMORY = (byte) 0xFB;
|
||||
public static final byte DIRECTORY_FULL = (byte) 0xFC;
|
||||
public static final byte UNDEFINED_ERROR = (byte) 0x8A;
|
||||
public static final byte NOT_IMPLEMENTED = (byte) 0xFD;
|
||||
|
||||
// Firmware codes
|
||||
public static byte[] FIRMWARE_VERSION_LEJOSMINDDROID = { 0x6c, 0x4d, 0x49, 0x64 };
|
||||
|
||||
public static byte[] getBeepMessage(int frequency, int duration) {
|
||||
byte[] message = new byte[6];
|
||||
|
||||
message[0] = DIRECT_COMMAND_NOREPLY;
|
||||
message[1] = PLAY_TONE;
|
||||
// Frequency for the tone, Hz (UWORD); Range: 200-14000 Hz
|
||||
message[2] = (byte) frequency;
|
||||
message[3] = (byte) (frequency >> 8);
|
||||
// Duration of the tone, ms (UWORD)
|
||||
message[4] = (byte) duration;
|
||||
message[5] = (byte) (duration >> 8);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static byte[] getActionMessage(int actionNr) {
|
||||
byte[] message = new byte[3];
|
||||
|
||||
message[0] = DIRECT_COMMAND_NOREPLY;
|
||||
message[1] = ACTION_BUTTON;
|
||||
message[2] = (byte) actionNr;
|
||||
return message;
|
||||
}
|
||||
|
||||
public static byte[] getMotorMessage(int motor, int speed) {
|
||||
byte[] message = new byte[12];
|
||||
|
||||
message[0] = DIRECT_COMMAND_NOREPLY;
|
||||
message[1] = SET_OUTPUT_STATE;
|
||||
// Output port
|
||||
message[2] = (byte) motor;
|
||||
|
||||
if (speed == 0) {
|
||||
message[3] = 0;
|
||||
message[4] = 0;
|
||||
message[5] = 0;
|
||||
message[6] = 0;
|
||||
message[7] = 0;
|
||||
|
||||
} else {
|
||||
// Power set option (Range: -100 - 100)
|
||||
message[3] = (byte) speed;
|
||||
// Mode byte (Bit-field): MOTORON + BREAK
|
||||
message[4] = 0x03;
|
||||
// Regulation mode: REGULATION_MODE_MOTOR_SPEED
|
||||
message[5] = 0x01;
|
||||
// Turn Ratio (SBYTE; -100 - 100)
|
||||
message[6] = 0x00;
|
||||
// RunState: MOTOR_RUN_STATE_RUNNING
|
||||
message[7] = 0x20;
|
||||
}
|
||||
|
||||
// TachoLimit: run forever
|
||||
message[8] = 0;
|
||||
message[9] = 0;
|
||||
message[10] = 0;
|
||||
message[11] = 0;
|
||||
|
||||
return message;
|
||||
|
||||
}
|
||||
|
||||
public static byte[] getMotorMessage(int motor, int speed, int end) {
|
||||
byte[] message = getMotorMessage(motor, speed);
|
||||
|
||||
// TachoLimit
|
||||
message[8] = (byte) end;
|
||||
message[9] = (byte) (end >> 8);
|
||||
message[10] = (byte) (end >> 16);
|
||||
message[11] = (byte) (end >> 24);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static byte[] getResetMessage(int motor) {
|
||||
byte[] message = new byte[4];
|
||||
|
||||
message[0] = DIRECT_COMMAND_NOREPLY;
|
||||
message[1] = RESET_MOTOR_POSITION;
|
||||
// Output port
|
||||
message[2] = (byte) motor;
|
||||
// absolute position
|
||||
message[3] = 0;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static byte[] getStartProgramMessage(String programName) {
|
||||
byte[] message = new byte[22];
|
||||
|
||||
message[0] = DIRECT_COMMAND_NOREPLY;
|
||||
message[1] = START_PROGRAM;
|
||||
|
||||
// copy programName and end with 0 delimiter
|
||||
for (int pos=0; pos<programName.length(); pos++)
|
||||
message[2+pos] = (byte) programName.charAt(pos);
|
||||
|
||||
message[programName.length()+2] = 0;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static byte[] getStopProgramMessage() {
|
||||
byte[] message = new byte[2];
|
||||
|
||||
message[0] = DIRECT_COMMAND_NOREPLY;
|
||||
message[1] = STOP_PROGRAM;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static byte[] getProgramNameMessage() {
|
||||
byte[] message = new byte[2];
|
||||
|
||||
message[0] = DIRECT_COMMAND_REPLY;
|
||||
message[1] = GET_CURRENT_PROGRAM_NAME;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static byte[] getOutputStateMessage(int motor) {
|
||||
byte[] message = new byte[3];
|
||||
|
||||
message[0] = DIRECT_COMMAND_REPLY;
|
||||
message[1] = GET_OUTPUT_STATE;
|
||||
// Output port
|
||||
message[2] = (byte) motor;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static byte[] getFirmwareVersionMessage() {
|
||||
byte[] message = new byte[2];
|
||||
|
||||
message[0] = SYSTEM_COMMAND_REPLY;
|
||||
message[1] = GET_FIRMWARE_VERSION;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static byte[] getFindFilesMessage(boolean findFirst, int handle, String searchString) {
|
||||
byte[] message;
|
||||
|
||||
if (findFirst)
|
||||
message = new byte[22];
|
||||
|
||||
else
|
||||
message = new byte[3];
|
||||
|
||||
message[0] = SYSTEM_COMMAND_REPLY;
|
||||
|
||||
if (findFirst) {
|
||||
message[1] = FIND_FIRST;
|
||||
|
||||
// copy searchString and end with 0 delimiter
|
||||
for (int pos=0; pos<searchString.length(); pos++)
|
||||
message[2+pos] = (byte) searchString.charAt(pos);
|
||||
|
||||
message[searchString.length()+2] = 0;
|
||||
|
||||
} else {
|
||||
message[1] = FIND_NEXT;
|
||||
message[2] = (byte) handle;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static byte[] getOpenWriteMessage(String fileName, int fileLength) {
|
||||
byte[] message = new byte[26];
|
||||
|
||||
message[0] = SYSTEM_COMMAND_REPLY;
|
||||
message[1] = OPEN_WRITE;
|
||||
|
||||
// copy programName and end with 0 delimiter
|
||||
for (int pos=0; pos<fileName.length(); pos++)
|
||||
message[2+pos] = (byte) fileName.charAt(pos);
|
||||
|
||||
message[fileName.length()+2] = 0;
|
||||
// copy file size
|
||||
message[22] = (byte) fileLength;
|
||||
message[23] = (byte) (fileLength >>> 8);
|
||||
message[24] = (byte) (fileLength >>> 16);
|
||||
message[25] = (byte) (fileLength >>> 24);
|
||||
return message;
|
||||
}
|
||||
|
||||
public static byte[] getWriteMessage(int handle, byte[] data, int dataLength) {
|
||||
byte[] message = new byte[dataLength + 3];
|
||||
|
||||
message[0] = SYSTEM_COMMAND_REPLY;
|
||||
message[1] = WRITE;
|
||||
|
||||
// copy handle
|
||||
message[2] = (byte) handle;
|
||||
// copy data
|
||||
System.arraycopy(data, 0, message, 3, dataLength);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static byte[] getCloseMessage(int handle) {
|
||||
byte[] message = new byte[3];
|
||||
|
||||
message[0] = SYSTEM_COMMAND_REPLY;
|
||||
message[1] = CLOSE;
|
||||
|
||||
// copy handle
|
||||
message[2] = (byte) handle;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
128
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/Lama.java
Normal file
128
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/Lama.java
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
/**
|
||||
* Displays an LAMA ("LEGO Application MINDdroid Agreement") that the user has to accept before
|
||||
* using the application. Your application should call {@link Lama#show(android.app.Activity)}
|
||||
* in the onCreate() method of the first activity. If the user accepts the LAMA, it will never
|
||||
* be shown again. If the user refuses, {@link android.app.Activity#finish()} is invoked
|
||||
* on your activity.
|
||||
*/
|
||||
class Lama {
|
||||
private static final String ASSET_LAMA = "LAMA";
|
||||
private static final String PREFERENCE_LAMA_ACCEPTED = "lama.accepted";
|
||||
private static final String PREFERENCES_LAMA = "lama";
|
||||
|
||||
/**
|
||||
* callback to let the activity know when the user has accepted the LAMA.
|
||||
*/
|
||||
static interface OnLamaAgreedTo {
|
||||
|
||||
/**
|
||||
* Called when the user has accepted the lama and the dialog closes.
|
||||
*/
|
||||
void onLamaAgreedTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the LAMA if necessary. This method should be called from the onCreate()
|
||||
* method of your main Activity.
|
||||
*
|
||||
* @param activity The Activity to finish if the user rejects the LAMA.
|
||||
* @return Whether the user has agreed already.
|
||||
*/
|
||||
static boolean show(final Activity activity) {
|
||||
final SharedPreferences preferences = activity.getSharedPreferences(PREFERENCES_LAMA,
|
||||
Activity.MODE_PRIVATE);
|
||||
if (!preferences.getBoolean(PREFERENCE_LAMA_ACCEPTED, false)) {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setTitle(R.string.lama_title);
|
||||
builder.setCancelable(true);
|
||||
builder.setPositiveButton(R.string.lama_accept, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
accept(preferences);
|
||||
if (activity instanceof OnLamaAgreedTo) {
|
||||
((OnLamaAgreedTo) activity).onLamaAgreedTo();
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.lama_refuse, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
refuse(activity);
|
||||
}
|
||||
});
|
||||
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
refuse(activity);
|
||||
}
|
||||
});
|
||||
builder.setMessage(readLama(activity));
|
||||
builder.create().show();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void accept(SharedPreferences preferences) {
|
||||
preferences.edit().putBoolean(PREFERENCE_LAMA_ACCEPTED, true).commit();
|
||||
}
|
||||
|
||||
private static void refuse(Activity activity) {
|
||||
activity.finish();
|
||||
}
|
||||
|
||||
private static CharSequence readLama(Activity activity) {
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(activity.getAssets().open(ASSET_LAMA)));
|
||||
String line;
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
while ((line = in.readLine()) != null) buffer.append(line).append('\n');
|
||||
return buffer;
|
||||
} catch (IOException e) {
|
||||
return "";
|
||||
} finally {
|
||||
closeStream(in);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the specified stream.
|
||||
*
|
||||
* @param stream The stream to close.
|
||||
*/
|
||||
private static void closeStream(Closeable stream) {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
787
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/MINDdroidCV.java
Normal file
787
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/MINDdroidCV.java
Normal file
@@ -0,0 +1,787 @@
|
||||
/**
|
||||
* Copyright 2010, 2011 Guenther Hoelzl, Shawn Brown
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Modified by Richard Szabo to integrate OpenCV computer vision library functionality.
|
||||
* http://opencv.willowgarage.com/wiki/
|
||||
*
|
||||
* 2011
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Vibrator;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* This class is for talking to a LEGO NXT robot and controlling it
|
||||
* via bluetooth and the built in acceleration sensor.
|
||||
* The communication to the robot is done via LCP (LEGO communication protocol),
|
||||
* so no special software has to be installed on the robot.
|
||||
*/
|
||||
public class MINDdroidCV extends Activity implements BTConnectable, TextToSpeech.OnInitListener {
|
||||
|
||||
public static final int UPDATE_TIME = 200;
|
||||
public static final int MENU_TOGGLE_CONNECT = Menu.FIRST;
|
||||
public static final int MENU_QUIT = Menu.FIRST + 1;
|
||||
|
||||
private static final int REQUEST_CONNECT_DEVICE = 1000;
|
||||
private static final int REQUEST_ENABLE_BT = 2000;
|
||||
private BTCommunicator myBTCommunicator = null;
|
||||
private boolean connected = false;
|
||||
private ProgressDialog connectingProgressDialog;
|
||||
private Handler btcHandler;
|
||||
private Menu myMenu;
|
||||
private GameView mView;
|
||||
private Activity thisActivity;
|
||||
private boolean btErrorPending = false;
|
||||
private boolean pairing;
|
||||
private static boolean btOnByUs = false;
|
||||
int mRobotType;
|
||||
int motorLeft;
|
||||
private int directionLeft; // +/- 1
|
||||
int motorRight;
|
||||
private boolean stopAlreadySent = false;
|
||||
private int directionRight; // +/- 1
|
||||
private int motorAction;
|
||||
private int directionAction; // +/- 1
|
||||
private List<String> programList;
|
||||
private static final int MAX_PROGRAMS = 20;
|
||||
private String programToStart;
|
||||
private Toast reusableToast;
|
||||
|
||||
// experimental TTS support
|
||||
private TextToSpeech mTts;
|
||||
private final int TTS_CHECK_CODE = 9991;
|
||||
|
||||
/**
|
||||
* Asks if bluetooth was switched on during the runtime of the app. For saving
|
||||
* battery we switch it off when the app is terminated.
|
||||
* @return true, when bluetooth was switched on by the app
|
||||
*/
|
||||
public static boolean isBtOnByUs() {
|
||||
return btOnByUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a flag when bluetooth was switched on durin runtime
|
||||
* @param btOnByUs true, when bluetooth was switched on by the app
|
||||
*/
|
||||
public static void setBtOnByUs(boolean btOnByUs) {
|
||||
MINDdroidCV.btOnByUs = btOnByUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true, when currently pairing
|
||||
*/
|
||||
@Override
|
||||
public boolean isPairing() {
|
||||
return pairing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity is first created. Inititializes all the
|
||||
* graphical views.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
thisActivity = this;
|
||||
mRobotType = this.getIntent().getIntExtra(SplashMenu.MINDDROID_ROBOT_TYPE, R.id.robot_type_1);
|
||||
setUpByType();
|
||||
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
StartSound mySound = new StartSound(this);
|
||||
mySound.start();
|
||||
reusableToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
|
||||
if( mRobotType == R.id.robot_type_5 ) { // opencv usage
|
||||
showToast("opencv", Toast.LENGTH_SHORT);
|
||||
setContentView(new SampleView(getApplicationContext(), this));
|
||||
} else {
|
||||
showToast("no opencv", Toast.LENGTH_SHORT);
|
||||
// setup our view, give it focus and display.
|
||||
mView = new GameView(getApplicationContext(), this);
|
||||
mView.setFocusable(true);
|
||||
setContentView(mView);
|
||||
}
|
||||
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
||||
// experimental TTS support for the lejosMINDdroid project
|
||||
mTts = new TextToSpeech(this,
|
||||
this // TextToSpeech.OnInitListener
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization of the motor commands for the different robot types.
|
||||
*/
|
||||
private void setUpByType() {
|
||||
switch (mRobotType) {
|
||||
case R.id.robot_type_2:
|
||||
motorLeft = BTCommunicator.MOTOR_B;
|
||||
directionLeft = 1;
|
||||
motorRight = BTCommunicator.MOTOR_C;
|
||||
directionRight = 1;
|
||||
motorAction = BTCommunicator.MOTOR_A;
|
||||
directionAction = 1;
|
||||
break;
|
||||
case R.id.robot_type_3:
|
||||
motorLeft = BTCommunicator.MOTOR_C;
|
||||
directionLeft = -1;
|
||||
motorRight = BTCommunicator.MOTOR_B;
|
||||
directionRight = -1;
|
||||
motorAction = BTCommunicator.MOTOR_A;
|
||||
directionAction = 1;
|
||||
break;
|
||||
case R.id.robot_type_5:
|
||||
motorLeft = BTCommunicator.MOTOR_C;
|
||||
directionLeft = 1;
|
||||
motorRight = BTCommunicator.MOTOR_B;
|
||||
directionRight = 1;
|
||||
motorAction = BTCommunicator.MOTOR_A;
|
||||
directionAction = 1;
|
||||
break;
|
||||
default:
|
||||
// default
|
||||
motorLeft = BTCommunicator.MOTOR_B;
|
||||
directionLeft = 1;
|
||||
motorRight = BTCommunicator.MOTOR_C;
|
||||
directionRight = 1;
|
||||
motorAction = BTCommunicator.MOTOR_A;
|
||||
directionAction = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the menus and possible buttons when connection status changed.
|
||||
*/
|
||||
private void updateButtonsAndMenu() {
|
||||
|
||||
if (myMenu == null)
|
||||
return;
|
||||
|
||||
myMenu.removeItem(MENU_TOGGLE_CONNECT);
|
||||
|
||||
if (connected) {
|
||||
myMenu.add(0, MENU_TOGGLE_CONNECT, 1, getResources().getString(R.string.disconnect)).setIcon(R.drawable.ic_menu_connected);
|
||||
|
||||
} else {
|
||||
myMenu.add(0, MENU_TOGGLE_CONNECT, 1, getResources().getString(R.string.connect)).setIcon(R.drawable.ic_menu_connect);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new object for communication to the NXT robot via bluetooth and fetches the corresponding handler.
|
||||
*/
|
||||
private void createBTCommunicator() {
|
||||
// interestingly BT adapter needs to be obtained by the UI thread - so we pass it in in the constructor
|
||||
myBTCommunicator = new BTCommunicator(this, myHandler, BluetoothAdapter.getDefaultAdapter(), getResources());
|
||||
btcHandler = myBTCommunicator.getHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and starts the a thread for communication via bluetooth to the NXT robot.
|
||||
* @param mac_address The MAC address of the NXT robot.
|
||||
*/
|
||||
private void startBTCommunicator(String mac_address) {
|
||||
connected = false;
|
||||
connectingProgressDialog = ProgressDialog.show(this, "", getResources().getString(R.string.connecting_please_wait), true);
|
||||
|
||||
if (myBTCommunicator != null) {
|
||||
try {
|
||||
myBTCommunicator.destroyNXTconnection();
|
||||
}
|
||||
catch (IOException e) { }
|
||||
}
|
||||
createBTCommunicator();
|
||||
myBTCommunicator.setMACAddress(mac_address);
|
||||
myBTCommunicator.start();
|
||||
updateButtonsAndMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message for disconnecting to the communcation thread.
|
||||
*/
|
||||
public void destroyBTCommunicator() {
|
||||
|
||||
if (myBTCommunicator != null) {
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, BTCommunicator.DISCONNECT, 0, 0);
|
||||
myBTCommunicator = null;
|
||||
}
|
||||
|
||||
connected = false;
|
||||
updateButtonsAndMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current connection status.
|
||||
* @return the current connection status to the robot.
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for performing the appropriate action when the ACTION button is pressed shortly.
|
||||
*/
|
||||
public void actionButtonPressed() {
|
||||
if (myBTCommunicator != null) {
|
||||
if( mView != null ) {
|
||||
mView.getThread().mActionPressed = true;
|
||||
}
|
||||
|
||||
// Wolfgang Amadeus Mozart "Zauberfloete - Der Vogelfaenger bin ich ja"
|
||||
if (mRobotType != R.id.robot_type_4) {
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, BTCommunicator.DO_BEEP, 392, 100);
|
||||
sendBTCmessage(200, BTCommunicator.DO_BEEP, 440, 100);
|
||||
sendBTCmessage(400, BTCommunicator.DO_BEEP, 494, 100);
|
||||
sendBTCmessage(600, BTCommunicator.DO_BEEP, 523, 100);
|
||||
sendBTCmessage(800, BTCommunicator.DO_BEEP, 587, 300);
|
||||
sendBTCmessage(1200, BTCommunicator.DO_BEEP, 523, 300);
|
||||
sendBTCmessage(1600, BTCommunicator.DO_BEEP, 494, 300);
|
||||
}
|
||||
|
||||
// MOTOR ACTION: forth an back
|
||||
switch (mRobotType) {
|
||||
|
||||
case R.id.robot_type_3:
|
||||
// Robogator: bite the user ;-)
|
||||
for (int bite=0; bite<3; bite++) {
|
||||
sendBTCmessage(bite*400, motorAction, 75 * directionAction, 0);
|
||||
sendBTCmessage(bite*400+200, motorAction, -75 * directionAction, 0);
|
||||
}
|
||||
sendBTCmessage(3*400, motorAction, 0, 0);
|
||||
break;
|
||||
|
||||
case R.id.robot_type_4:
|
||||
// lejosMINDdroid: just send the message for button press
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, BTCommunicator.DO_ACTION, 0, 0);
|
||||
break;
|
||||
|
||||
case R.id.robot_type_5:
|
||||
//
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, BTCommunicator.DO_ACTION, 0, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
// other robots: 180 degrees forth and back
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, motorAction, 75 * directionAction, 0);
|
||||
sendBTCmessage(500, motorAction, -75 * directionAction, 0);
|
||||
sendBTCmessage(1000, motorAction, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for performing the appropriate action when the ACTION button is long pressed.
|
||||
*/
|
||||
public void actionButtonLongPress() {
|
||||
if (myBTCommunicator != null) {
|
||||
if( mView != null ) {
|
||||
mView.getThread().mActionPressed = true;
|
||||
}
|
||||
|
||||
if (programList.size() == 0)
|
||||
showToast(R.string.no_files_found, Toast.LENGTH_SHORT);
|
||||
|
||||
FileDialog myFileDialog = new FileDialog(this, programList);
|
||||
myFileDialog.show(mRobotType == R.id.robot_type_4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a program on the NXT robot.
|
||||
* @param name The program name to start. Has to end with .rxe on the LEGO firmware and with .nxj on the
|
||||
* leJOS NXJ firmware.
|
||||
*/
|
||||
public void startProgram(String name) {
|
||||
// for .rxe programs: get program name, eventually stop this and start the new one delayed
|
||||
// is handled in startRXEprogram()
|
||||
if (name.endsWith(".rxe")) {
|
||||
programToStart = name;
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, BTCommunicator.GET_PROGRAM_NAME, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// for .nxj programs: stop bluetooth communication after starting the program
|
||||
if (name.endsWith(".nxj")) {
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, BTCommunicator.START_PROGRAM, name);
|
||||
destroyBTCommunicator();
|
||||
return;
|
||||
}
|
||||
|
||||
// for all other programs: just start the program
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, BTCommunicator.START_PROGRAM, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on the status (whether the program runs already) we stop it, wait and restart it again.
|
||||
* @param status The current status, 0x00 means that the program is already running.
|
||||
*/
|
||||
public void startRXEprogram(byte status) {
|
||||
if (status == 0x00) {
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, BTCommunicator.STOP_PROGRAM, 0, 0);
|
||||
sendBTCmessage(1000, BTCommunicator.START_PROGRAM, programToStart);
|
||||
}
|
||||
else {
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, BTCommunicator.START_PROGRAM, programToStart);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the motor control values to the communcation thread.
|
||||
* @param left The power of the left motor from 0 to 100.
|
||||
* @param rigth The power of the right motor from 0 to 100.
|
||||
*/
|
||||
public void updateMotorControl(int left, int right) {
|
||||
|
||||
if (myBTCommunicator != null) {
|
||||
// don't send motor stop twice
|
||||
if ((left == 0) && (right == 0)) {
|
||||
if (stopAlreadySent)
|
||||
return;
|
||||
else
|
||||
stopAlreadySent = true;
|
||||
}
|
||||
else
|
||||
stopAlreadySent = false;
|
||||
|
||||
// send messages via the handler
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, motorLeft, left * directionLeft, 0);
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, motorRight, right * directionRight, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the message via the BTCommuncator to the robot.
|
||||
* @param delay time to wait before sending the message.
|
||||
* @param message the message type (as defined in BTCommucator)
|
||||
* @param value1 first parameter
|
||||
* @param value2 second parameter
|
||||
*/
|
||||
void sendBTCmessage(int delay, int message, int value1, int value2) {
|
||||
Bundle myBundle = new Bundle();
|
||||
myBundle.putInt("message", message);
|
||||
myBundle.putInt("value1", value1);
|
||||
myBundle.putInt("value2", value2);
|
||||
Message myMessage = myHandler.obtainMessage();
|
||||
myMessage.setData(myBundle);
|
||||
|
||||
if (delay == 0)
|
||||
btcHandler.sendMessage(myMessage);
|
||||
|
||||
else
|
||||
btcHandler.sendMessageDelayed(myMessage, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the message via the BTCommuncator to the robot.
|
||||
* @param delay time to wait before sending the message.
|
||||
* @param message the message type (as defined in BTCommucator)
|
||||
* @param String a String parameter
|
||||
*/
|
||||
void sendBTCmessage(int delay, int message, String name) {
|
||||
Bundle myBundle = new Bundle();
|
||||
myBundle.putInt("message", message);
|
||||
myBundle.putString("name", name);
|
||||
Message myMessage = myHandler.obtainMessage();
|
||||
myMessage.setData(myBundle);
|
||||
|
||||
if (delay == 0)
|
||||
btcHandler.sendMessage(myMessage);
|
||||
else
|
||||
btcHandler.sendMessageDelayed(myMessage, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if( mView != null ) {
|
||||
mView.registerListener();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
||||
if (!BluetoothAdapter.getDefaultAdapter().isEnabled()) {
|
||||
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
|
||||
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
|
||||
} else {
|
||||
selectNXT();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
destroyBTCommunicator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if( mView != null ) {
|
||||
mView.unregisterListener();
|
||||
}
|
||||
destroyBTCommunicator();
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle icicle) {
|
||||
super.onSaveInstanceState(icicle);
|
||||
if( mView != null ) {
|
||||
mView.unregisterListener();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the menu items
|
||||
*/
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
myMenu = menu;
|
||||
myMenu.add(0, MENU_TOGGLE_CONNECT, 1, getResources().getString(R.string.connect)).setIcon(R.drawable.ic_menu_connect);
|
||||
myMenu.add(0, MENU_QUIT, 2, getResources().getString(R.string.quit)).setIcon(R.drawable.ic_menu_exit);
|
||||
updateButtonsAndMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles item selections
|
||||
*/
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENU_TOGGLE_CONNECT:
|
||||
|
||||
if (myBTCommunicator == null || connected == false) {
|
||||
selectNXT();
|
||||
|
||||
} else {
|
||||
destroyBTCommunicator();
|
||||
updateButtonsAndMenu();
|
||||
}
|
||||
|
||||
return true;
|
||||
case MENU_QUIT:
|
||||
destroyBTCommunicator();
|
||||
finish();
|
||||
|
||||
if (btOnByUs)
|
||||
showToast(R.string.bt_off_message, Toast.LENGTH_SHORT);
|
||||
|
||||
SplashMenu.quitApplication();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a message as a toast
|
||||
* @param textToShow the message
|
||||
* @param length the length of the toast to display
|
||||
*/
|
||||
private void showToast(String textToShow, int length) {
|
||||
reusableToast.setText(textToShow);
|
||||
reusableToast.setDuration(length);
|
||||
reusableToast.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a message as a toast
|
||||
* @param resID the ressource ID to display
|
||||
* @param length the length of the toast to display
|
||||
*/
|
||||
private void showToast(int resID, int length) {
|
||||
reusableToast.setText(resID);
|
||||
reusableToast.setDuration(length);
|
||||
reusableToast.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive messages from the BTCommunicator
|
||||
*/
|
||||
final Handler myHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message myMessage) {
|
||||
switch (myMessage.getData().getInt("message")) {
|
||||
case BTCommunicator.DISPLAY_TOAST:
|
||||
showToast(myMessage.getData().getString("toastText"), Toast.LENGTH_SHORT);
|
||||
break;
|
||||
case BTCommunicator.STATE_CONNECTED:
|
||||
connected = true;
|
||||
programList = new ArrayList<String>();
|
||||
connectingProgressDialog.dismiss();
|
||||
updateButtonsAndMenu();
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, BTCommunicator.GET_FIRMWARE_VERSION, 0, 0);
|
||||
break;
|
||||
case BTCommunicator.MOTOR_STATE:
|
||||
|
||||
if (myBTCommunicator != null) {
|
||||
byte[] motorMessage = myBTCommunicator.getReturnMessage();
|
||||
int position = byteToInt(motorMessage[21]) + (byteToInt(motorMessage[22]) << 8) + (byteToInt(motorMessage[23]) << 16)
|
||||
+ (byteToInt(motorMessage[24]) << 24);
|
||||
showToast(getResources().getString(R.string.current_position) + position, Toast.LENGTH_SHORT);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BTCommunicator.STATE_CONNECTERROR_PAIRING:
|
||||
connectingProgressDialog.dismiss();
|
||||
destroyBTCommunicator();
|
||||
break;
|
||||
|
||||
case BTCommunicator.STATE_CONNECTERROR:
|
||||
connectingProgressDialog.dismiss();
|
||||
case BTCommunicator.STATE_RECEIVEERROR:
|
||||
case BTCommunicator.STATE_SENDERROR:
|
||||
|
||||
destroyBTCommunicator();
|
||||
if (btErrorPending == false) {
|
||||
btErrorPending = true;
|
||||
// inform the user of the error with an AlertDialog
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(thisActivity);
|
||||
builder.setTitle(getResources().getString(R.string.bt_error_dialog_title))
|
||||
.setMessage(getResources().getString(R.string.bt_error_dialog_message)).setCancelable(false)
|
||||
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
btErrorPending = false;
|
||||
dialog.cancel();
|
||||
selectNXT();
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BTCommunicator.FIRMWARE_VERSION:
|
||||
|
||||
if (myBTCommunicator != null) {
|
||||
byte[] firmwareMessage = myBTCommunicator.getReturnMessage();
|
||||
// check if we know the firmware
|
||||
boolean isLejosMindDroid = true;
|
||||
for (int pos=0; pos<4; pos++) {
|
||||
if (firmwareMessage[pos + 3] != LCPMessage.FIRMWARE_VERSION_LEJOSMINDDROID[pos]) {
|
||||
isLejosMindDroid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isLejosMindDroid) {
|
||||
mRobotType = R.id.robot_type_4;
|
||||
setUpByType();
|
||||
}
|
||||
// afterwards we search for all files on the robot
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, BTCommunicator.FIND_FILES, 0, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BTCommunicator.FIND_FILES:
|
||||
|
||||
if (myBTCommunicator != null) {
|
||||
byte[] fileMessage = myBTCommunicator.getReturnMessage();
|
||||
String fileName = new String(fileMessage, 4, 20);
|
||||
fileName = fileName.replaceAll("\0","");
|
||||
|
||||
if (mRobotType == R.id.robot_type_4 || fileName.endsWith(".nxj") || fileName.endsWith(".rxe")) {
|
||||
programList.add(fileName);
|
||||
}
|
||||
|
||||
// find next entry with appropriate handle,
|
||||
// limit number of programs (in case of error (endless loop))
|
||||
if (programList.size() <= MAX_PROGRAMS)
|
||||
sendBTCmessage(BTCommunicator.NO_DELAY, BTCommunicator.FIND_FILES,
|
||||
1, byteToInt(fileMessage[3]));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BTCommunicator.PROGRAM_NAME:
|
||||
if (myBTCommunicator != null) {
|
||||
byte[] returnMessage = myBTCommunicator.getReturnMessage();
|
||||
startRXEprogram(returnMessage[2]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BTCommunicator.SAY_TEXT:
|
||||
if (myBTCommunicator != null) {
|
||||
byte[] textMessage = myBTCommunicator.getReturnMessage();
|
||||
// evaluate control byte
|
||||
byte controlByte = textMessage[2];
|
||||
// BIT7: Language
|
||||
if ((controlByte & 0x80) == 0x00)
|
||||
mTts.setLanguage(Locale.US);
|
||||
else
|
||||
mTts.setLanguage(Locale.getDefault());
|
||||
// BIT6: Pitch
|
||||
if ((controlByte & 0x40) == 0x00)
|
||||
mTts.setPitch(1.0f);
|
||||
else
|
||||
mTts.setPitch(0.75f);
|
||||
// BIT0-3: Speech Rate
|
||||
switch (controlByte & 0x0f) {
|
||||
case 0x01:
|
||||
mTts.setSpeechRate(1.5f);
|
||||
break;
|
||||
case 0x02:
|
||||
mTts.setSpeechRate(0.75f);
|
||||
break;
|
||||
|
||||
default: mTts.setSpeechRate(1.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
String ttsText = new String(textMessage, 3, 19);
|
||||
ttsText = ttsText.replaceAll("\0","");
|
||||
showToast(ttsText, Toast.LENGTH_SHORT);
|
||||
mTts.speak(ttsText, TextToSpeech.QUEUE_FLUSH, null);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BTCommunicator.VIBRATE_PHONE:
|
||||
if (myBTCommunicator != null) {
|
||||
byte[] vibrateMessage = myBTCommunicator.getReturnMessage();
|
||||
Vibrator myVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
|
||||
myVibrator.vibrate(vibrateMessage[2]*10);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private int byteToInt(byte byteValue) {
|
||||
int intValue = (byteValue & (byte) 0x7f);
|
||||
|
||||
if ((byteValue & (byte) 0x80) != 0)
|
||||
intValue |= 0x80;
|
||||
|
||||
return intValue;
|
||||
}
|
||||
|
||||
void selectNXT() {
|
||||
Intent serverIntent = new Intent(this, DeviceListActivity.class);
|
||||
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_CONNECT_DEVICE:
|
||||
|
||||
// When DeviceListActivity returns with a device to connect
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
// Get the device MAC address and start a new bt communicator thread
|
||||
String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
|
||||
pairing = data.getExtras().getBoolean(DeviceListActivity.PAIRING);
|
||||
startBTCommunicator(address);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case REQUEST_ENABLE_BT:
|
||||
|
||||
// When the request to enable Bluetooth returns
|
||||
switch (resultCode) {
|
||||
case Activity.RESULT_OK:
|
||||
btOnByUs = true;
|
||||
selectNXT();
|
||||
break;
|
||||
case Activity.RESULT_CANCELED:
|
||||
showToast(R.string.bt_needs_to_be_enabled, Toast.LENGTH_SHORT);
|
||||
finish();
|
||||
break;
|
||||
default:
|
||||
showToast(R.string.problem_at_connecting, Toast.LENGTH_SHORT);
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// will not be called now, since the check intent is not generated
|
||||
case TTS_CHECK_CODE:
|
||||
if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
|
||||
// success, create the TTS instance
|
||||
mTts = new TextToSpeech(this, this);
|
||||
}
|
||||
else {
|
||||
// missing data, install it
|
||||
Intent installIntent = new Intent();
|
||||
installIntent.setAction(
|
||||
TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
|
||||
startActivity(installIntent);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializing of the TTS engine.
|
||||
*/
|
||||
public void onInit(int status) {
|
||||
// status can be either TextToSpeech.SUCCESS or TextToSpeech.ERROR.
|
||||
if (status == TextToSpeech.SUCCESS) {
|
||||
// Set preferred language to US english.
|
||||
// Note that a language may not be available, and the result will indicate this.
|
||||
int result = mTts.setLanguage(Locale.US);
|
||||
// Try this someday for some interesting results.
|
||||
if (result == TextToSpeech.LANG_MISSING_DATA ||
|
||||
result == TextToSpeech.LANG_NOT_SUPPORTED) {
|
||||
// Language data is missing or the language is not supported.
|
||||
if (mRobotType == R.id.robot_type_4)
|
||||
showToast(R.string.tts_language_not_supported, Toast.LENGTH_LONG);
|
||||
}
|
||||
} else {
|
||||
// Initialization failed.
|
||||
if (mRobotType == R.id.robot_type_4)
|
||||
showToast(R.string.tts_initialization_failure, Toast.LENGTH_LONG);
|
||||
}
|
||||
}
|
||||
}
|
||||
103
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/NXJFileDialog.java
Normal file
103
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/NXJFileDialog.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright 2011 Guenther Hoelzl
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
/**
|
||||
* Builds and shows a file dialog for selecting nxj-files
|
||||
* @return number of nxj-files in the directory
|
||||
*/
|
||||
class NXJFileDialog {
|
||||
|
||||
private final static String DIR_PATH = "/sdcard/download/";
|
||||
private final static String EXTENSION = ".nxj";
|
||||
|
||||
private Activity mActivity;
|
||||
private DialogListener mDialogListener;
|
||||
private String[] programs;
|
||||
private int preinstalledFiles;
|
||||
|
||||
public NXJFileDialog(Activity activity, DialogListener dialogListener) {
|
||||
mActivity = activity;
|
||||
mDialogListener = dialogListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the file list of the directory in DIR_PATH and builds
|
||||
* an array.
|
||||
* @param preinstalledList list of preinstalled nxj-files
|
||||
* @return number of nxj-files in the directory
|
||||
*/
|
||||
public int refreshFileList(String[] preinstalledList) {
|
||||
ArrayList<String> nxjPrograms = new ArrayList();
|
||||
|
||||
// internal nxj-files
|
||||
preinstalledFiles = preinstalledList.length;
|
||||
for (int index = 0; index < preinstalledFiles; index++)
|
||||
nxjPrograms.add(preinstalledList[index]);
|
||||
|
||||
// external nxj-files
|
||||
File file = new File(DIR_PATH);
|
||||
if (file != null) {
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (int fileNr = 0; fileNr < files.length; fileNr++) {
|
||||
if (files[fileNr].getName().toLowerCase().endsWith(EXTENSION))
|
||||
nxjPrograms.add(files[fileNr].getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
programs = new String[nxjPrograms.size()];
|
||||
programs = nxjPrograms.toArray(programs);
|
||||
return programs.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the dialog
|
||||
*/
|
||||
public void show() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
|
||||
builder.setTitle(mActivity.getResources().getString(R.string.nxj_file_dialog_title));
|
||||
builder.setItems(programs, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int item) {
|
||||
informDialogListener(item, programs[item]);
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the calling activity about the new selected file name
|
||||
* @param item nr in the list of the selected item
|
||||
* @param fileName the name of the file
|
||||
*/
|
||||
public void informDialogListener(int item, String fileName) {
|
||||
if (item >= preinstalledFiles)
|
||||
fileName = DIR_PATH.concat(fileName);
|
||||
mDialogListener.dialogUpdate(fileName);
|
||||
}
|
||||
|
||||
}
|
||||
399
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/NXJUploader.java
Normal file
399
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/NXJUploader.java
Normal file
@@ -0,0 +1,399 @@
|
||||
/**
|
||||
* Copyright 2011 Guenther Hoelzl
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* This class is for uploading programs to the NXT brick via Bluetooth.
|
||||
* Special programs will be able to communicate with MINDdroid, so no PC
|
||||
* is required for playing with a robot.
|
||||
*/
|
||||
public class NXJUploader extends Activity implements UploadThreadListener, DialogListener, BTConnectable
|
||||
{
|
||||
private static final int DIALOG_NXT = 0;
|
||||
private static final int DIALOG_FILE = 1;
|
||||
|
||||
// preinstalled modules on res/raw/directoy
|
||||
private static String[] preinstalledNXJstring = new String[]
|
||||
{ "AlphaRex.nxj",
|
||||
"MINDGameZ.nxj"
|
||||
};
|
||||
|
||||
private static final int REQUEST_CONNECT_DEVICE = 1000;
|
||||
private static final int REQUEST_ENABLE_BT = 2000;
|
||||
|
||||
private BTCommunicator mNXT;
|
||||
private UploadThread uploadThread;
|
||||
private Handler handler;
|
||||
private ProgressDialog progressDialog;
|
||||
private int uploadStatus;
|
||||
private int runningDialog;
|
||||
private boolean pairing;
|
||||
private boolean btErrorPending = false;
|
||||
private static boolean btOnByUs = false;
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.nxjuploader);
|
||||
initLayout();
|
||||
|
||||
// Create objects for communication
|
||||
mNXT = new BTCommunicator(this, null, BluetoothAdapter.getDefaultAdapter(), getResources());
|
||||
handler = new Handler();
|
||||
// Create and launch the upload thread
|
||||
uploadThread = new UploadThread(this, getResources());
|
||||
uploadThread.setBluetoothCommunicator(mNXT);
|
||||
uploadThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
||||
if (!BluetoothAdapter.getDefaultAdapter().isEnabled()) {
|
||||
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
|
||||
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity is destroyed.
|
||||
*/
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
// request the uploadthread to stop
|
||||
uploadThread.requestStop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks if bluetooth was switched on during the runtime of the app. For saving
|
||||
* battery we switch it off when the app is terminated.
|
||||
* @return true, when bluetooth was switched on by the app
|
||||
*/
|
||||
public static boolean isBtOnByUs() {
|
||||
return btOnByUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a flag when bluetooth was switched on durin runtime
|
||||
* @param btOnByUs true, when bluetooth was switched on by the app
|
||||
*/
|
||||
public static void setBtOnByUs(boolean btOnByUs) {
|
||||
NXJUploader.btOnByUs = btOnByUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true, when currently pairing
|
||||
*/
|
||||
@Override
|
||||
public boolean isPairing() {
|
||||
return pairing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a message as a toast
|
||||
*/
|
||||
public void showToast(String message) {
|
||||
Toast toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a message from resID as a toast
|
||||
*/
|
||||
public void showToast(int resID) {
|
||||
Toast toast = Toast.makeText(this, resID, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays resp. updates a progress dialog
|
||||
*/
|
||||
public void showProgressDialog(String message, int maxProgress, int currentProgress) {
|
||||
boolean initialized = false;
|
||||
if (progressDialog == null) {
|
||||
initialized = true;
|
||||
progressDialog = new ProgressDialog(this);
|
||||
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.setMessage(message);
|
||||
}
|
||||
progressDialog.setMax(maxProgress);
|
||||
progressDialog.setProgress(currentProgress);
|
||||
if (initialized)
|
||||
progressDialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays resp. updates a progress dialog
|
||||
*/
|
||||
public void showProgressDialog(String message) {
|
||||
if (progressDialog == null) {
|
||||
progressDialog = new ProgressDialog(this);
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.setMessage(message);
|
||||
progressDialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses an existing progress dialog
|
||||
*/
|
||||
public void dismissProgressDialog() {
|
||||
if (progressDialog != null) {
|
||||
progressDialog.dismiss();
|
||||
progressDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the values on the main screen
|
||||
*/
|
||||
private void initLayout() {
|
||||
initNXTButton();
|
||||
initFileButton();
|
||||
initUploadButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the "SELECT NXT" button
|
||||
*/
|
||||
private void initNXTButton() {
|
||||
Button fileButton = (Button) findViewById(R.id.nxt_button);
|
||||
fileButton.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
selectNXT();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the "SELECT FILE" button
|
||||
*/
|
||||
private void initFileButton() {
|
||||
Button fileButton = (Button) findViewById(R.id.file_button);
|
||||
fileButton.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
showFileDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the NXT selection activity
|
||||
*/
|
||||
private void selectNXT() {
|
||||
Intent serverIntent = new Intent(this, DeviceListActivity.class);
|
||||
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the file dialog
|
||||
*/
|
||||
private void showFileDialog() {
|
||||
NXJFileDialog fileDialog = new NXJFileDialog(this, this);
|
||||
if (fileDialog.refreshFileList(preinstalledNXJstring) == 0)
|
||||
showToast(R.string.nxj_no_files);
|
||||
else {
|
||||
runningDialog = DIALOG_FILE;
|
||||
fileDialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the method for returning values of dialogs
|
||||
* @param the selected text
|
||||
*/
|
||||
@Override
|
||||
public void dialogUpdate(String text) {
|
||||
TextView textView;
|
||||
switch (runningDialog) {
|
||||
case DIALOG_NXT:
|
||||
textView = (TextView) findViewById(R.id.nxt_name);
|
||||
textView.setText(text);
|
||||
break;
|
||||
|
||||
case DIALOG_FILE:
|
||||
textView = (TextView) findViewById(R.id.nxj_file_name);
|
||||
textView.setText(text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the "UPLOAD" button
|
||||
*/
|
||||
private void initUploadButton() {
|
||||
Button uploadButton = (Button) findViewById(R.id.upload_button);
|
||||
uploadButton.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
TextView nxtTextView = (TextView) findViewById(R.id.nxt_name);
|
||||
String macAddress = nxtTextView.getText().toString();
|
||||
if (macAddress.compareTo("") == 0) {
|
||||
showToast(R.string.nxj_please_select_nxt);
|
||||
return;
|
||||
}
|
||||
macAddress = macAddress.substring(macAddress.lastIndexOf('-')+1);
|
||||
TextView nxjTextView = (TextView) findViewById(R.id.nxj_file_name);
|
||||
String fileName = nxjTextView.getText().toString();
|
||||
if (fileName.compareTo("") == 0) {
|
||||
showToast(R.string.nxj_please_select_file);
|
||||
return;
|
||||
}
|
||||
uploadThread.enqueueUpload(macAddress, fileName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will be called by the UploadThread to signal an update of the
|
||||
* current status.
|
||||
* @param status The current state of the UploadThread
|
||||
*/
|
||||
@Override
|
||||
public void handleUploadThreadUpdate(final int status) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (status != uploadStatus) {
|
||||
dismissProgressDialog();
|
||||
uploadStatus = status;
|
||||
}
|
||||
showUploadStatus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the current status of the uploader either in
|
||||
* a progress bar or in toast in case of an error.
|
||||
*/
|
||||
private void showUploadStatus() {
|
||||
|
||||
switch (uploadStatus) {
|
||||
case UploadThread.CONNECTING:
|
||||
showProgressDialog(getResources().getString(R.string.nxj_connecting));
|
||||
break;
|
||||
case UploadThread.UPLOADING:
|
||||
showProgressDialog(getResources().getString(R.string.nxj_uploading),
|
||||
uploadThread.getFileLength(),
|
||||
uploadThread.getBytesUploaded());
|
||||
break;
|
||||
default:
|
||||
dismissProgressDialog();
|
||||
}
|
||||
|
||||
switch (uploadThread.getErrorCode()) {
|
||||
case UploadThread.NO_ERROR:
|
||||
break;
|
||||
case UploadThread.OPEN_BT_ERROR:
|
||||
if (pairing)
|
||||
showToast(R.string.nxj_bluetooth_pairing);
|
||||
else
|
||||
showBTErrorDialog();
|
||||
break;
|
||||
case UploadThread.CLOSE_BT_ERROR:
|
||||
showBTErrorDialog();
|
||||
break;
|
||||
case UploadThread.OPEN_FILE_ERROR:
|
||||
showToast(R.string.nxj_file_open_error);
|
||||
break;
|
||||
case UploadThread.UPLOAD_ERROR:
|
||||
showBTErrorDialog();
|
||||
break;
|
||||
default:
|
||||
showToast(R.string.nxj_other_error);
|
||||
}
|
||||
uploadThread.resetErrorCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows an error dialog when there's an error regarding
|
||||
* bluettooth transfer.
|
||||
*/
|
||||
private void showBTErrorDialog() {
|
||||
if (btErrorPending == false) {
|
||||
btErrorPending = true;
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(getResources().getString(R.string.bt_error_dialog_title))
|
||||
.setMessage(getResources().getString(R.string.bt_error_dialog_message)).setCancelable(false)
|
||||
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
btErrorPending = false;
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_CONNECT_DEVICE:
|
||||
// When DeviceListActivity returns with a device to connect
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
// Get the device infos
|
||||
String infos = data.getExtras().getString(DeviceListActivity.DEVICE_NAME_AND_ADDRESS);
|
||||
pairing = data.getExtras().getBoolean(DeviceListActivity.PAIRING);
|
||||
TextView textView = (TextView) findViewById(R.id.nxt_name);
|
||||
textView.setText(infos);
|
||||
}
|
||||
break;
|
||||
|
||||
case REQUEST_ENABLE_BT:
|
||||
// When the request to enable Bluetooth returns
|
||||
switch (resultCode) {
|
||||
case Activity.RESULT_OK:
|
||||
btOnByUs = true;
|
||||
break;
|
||||
case Activity.RESULT_CANCELED:
|
||||
showToast(R.string.bt_needs_to_be_enabled);
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
97
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/Options.java
Normal file
97
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/Options.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright 2010 Guenther Hoelzl, Shawn Brown
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.Window;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class Options {
|
||||
|
||||
private Dialog mDialog;
|
||||
String mSelectionMessage;
|
||||
SplashMenu splashMenu;
|
||||
|
||||
public Options(Activity myActivity) {
|
||||
this.splashMenu=(SplashMenu) myActivity;
|
||||
mDialog = new Dialog(myActivity);
|
||||
mDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
mDialog.setContentView(R.layout.options);
|
||||
|
||||
mSelectionMessage = myActivity.getString(R.string.model_type_selected);
|
||||
|
||||
final RadioButton robot_type_1 = (RadioButton) mDialog.findViewById(R.id.robot_type_1);
|
||||
final RadioButton robot_type_2 = (RadioButton) mDialog.findViewById(R.id.robot_type_2);
|
||||
final RadioButton robot_type_3 = (RadioButton) mDialog.findViewById(R.id.robot_type_3);
|
||||
final RadioButton robot_type_4 = (RadioButton) mDialog.findViewById(R.id.robot_type_4);
|
||||
final RadioButton robot_type_5 = (RadioButton) mDialog.findViewById(R.id.robot_type_5);
|
||||
|
||||
switch (splashMenu.getRobotType()) {
|
||||
|
||||
case R.id.robot_type_2:
|
||||
robot_type_2.setChecked(true);
|
||||
break;
|
||||
|
||||
case R.id.robot_type_3:
|
||||
robot_type_3.setChecked(true);
|
||||
break;
|
||||
|
||||
case R.id.robot_type_4:
|
||||
robot_type_4.setChecked(true);
|
||||
break;
|
||||
|
||||
case R.id.robot_type_5:
|
||||
robot_type_5.setChecked(true);
|
||||
Toast toast = Toast.makeText(myActivity, "version 90", Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
break;
|
||||
|
||||
default:
|
||||
robot_type_1.setChecked(true);
|
||||
break;
|
||||
}
|
||||
|
||||
robot_type_1.setOnClickListener(radio_listener);
|
||||
robot_type_2.setOnClickListener(radio_listener);
|
||||
robot_type_3.setOnClickListener(radio_listener);
|
||||
robot_type_4.setOnClickListener(radio_listener);
|
||||
robot_type_5.setOnClickListener(radio_listener);
|
||||
}
|
||||
|
||||
public void show() {
|
||||
mDialog.show();
|
||||
}
|
||||
|
||||
private OnClickListener radio_listener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Perform action on clicks
|
||||
RadioButton rb = (RadioButton) v;
|
||||
rb.setChecked(true);
|
||||
splashMenu.setRobotType(rb.getId());
|
||||
Toast.makeText(mDialog.getContext(), mSelectionMessage + " " + rb.getText(), Toast.LENGTH_SHORT).show();
|
||||
mDialog.dismiss();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.lego.minddroid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
class SampleView extends SampleViewBase {
|
||||
|
||||
public SampleView(Context context, MINDdroidCV uiActivity) {
|
||||
super(context,uiActivity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap processFrame(byte[] data) {
|
||||
int frameSize = getFrameWidth() * getFrameHeight();
|
||||
int[] rgba = new int[frameSize];
|
||||
|
||||
FindLight(getFrameWidth(), getFrameHeight(), data, rgba,buffer);
|
||||
|
||||
Bitmap bmp = Bitmap.createBitmap(getFrameWidth(), getFrameHeight(), Bitmap.Config.ARGB_8888);
|
||||
bmp.setPixels(rgba, 0/* offset */, getFrameWidth() /* stride */, 0, 0, getFrameWidth(), getFrameHeight());
|
||||
return bmp;
|
||||
}
|
||||
|
||||
public native void FindLight(int width, int height, byte yuv[], int[] rgba,double[] array);
|
||||
|
||||
static {
|
||||
System.loadLibrary("mixed_sample");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package com.lego.minddroid;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.Camera;
|
||||
import android.hardware.Camera.PreviewCallback;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
public abstract class SampleViewBase extends SurfaceView implements SurfaceHolder.Callback, Runnable {
|
||||
private static final String TAG = "Sample::SurfaceView";
|
||||
|
||||
private Camera mCamera;
|
||||
private SurfaceHolder mHolder;
|
||||
private int mFrameWidth;
|
||||
private int mFrameHeight;
|
||||
private byte[] mFrame;
|
||||
private boolean mThreadRun;
|
||||
protected MINDdroidCV mActivity;
|
||||
protected double[]buffer;
|
||||
protected int left, right;
|
||||
|
||||
public SampleViewBase(Context context, MINDdroidCV uiActivity) {
|
||||
super(context);
|
||||
mActivity = uiActivity;
|
||||
|
||||
mHolder = getHolder();
|
||||
mHolder.addCallback(this);
|
||||
buffer = new double[10];
|
||||
Log.i(TAG, "Instantiated new " + this.getClass());
|
||||
}
|
||||
|
||||
public int getFrameWidth() {
|
||||
return mFrameWidth;
|
||||
}
|
||||
|
||||
public int getFrameHeight() {
|
||||
return mFrameHeight;
|
||||
}
|
||||
|
||||
public void surfaceChanged(SurfaceHolder _holder, int format, int width, int height) {
|
||||
Log.i(TAG, "surfaceCreated");
|
||||
if (mCamera != null) {
|
||||
Camera.Parameters params = mCamera.getParameters();
|
||||
List<Camera.Size> sizes = params.getSupportedPreviewSizes();
|
||||
mFrameWidth = width;
|
||||
mFrameHeight = height;
|
||||
|
||||
// selecting optimal camera preview size
|
||||
{
|
||||
double minDiff = Double.MAX_VALUE;
|
||||
for (Camera.Size size : sizes) {
|
||||
if (Math.abs(size.height - height) < minDiff) {
|
||||
mFrameWidth = size.width;
|
||||
mFrameHeight = size.height;
|
||||
minDiff = Math.abs(size.height - height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params.setPreviewSize(getFrameWidth(), getFrameHeight());
|
||||
mCamera.setParameters(params);
|
||||
//mCamera.setDisplayOrientation(180);
|
||||
|
||||
try {
|
||||
mCamera.setPreviewDisplay(null);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "mCamera.setPreviewDisplay fails: " + e);
|
||||
}
|
||||
mCamera.startPreview();
|
||||
}
|
||||
}
|
||||
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.i(TAG, "surfaceCreated");
|
||||
mCamera = Camera.open();
|
||||
mCamera.setPreviewCallback(new PreviewCallback() {
|
||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||
synchronized (SampleViewBase.this) {
|
||||
mFrame = data;
|
||||
SampleViewBase.this.notify();
|
||||
}
|
||||
}
|
||||
});
|
||||
(new Thread(this)).start();
|
||||
}
|
||||
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.i(TAG, "surfaceDestroyed");
|
||||
mThreadRun = false;
|
||||
if (mCamera != null) {
|
||||
synchronized (this) {
|
||||
mCamera.stopPreview();
|
||||
mCamera.setPreviewCallback(null);
|
||||
mCamera.release();
|
||||
mCamera = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Bitmap processFrame(byte[] data);
|
||||
|
||||
public void run() {
|
||||
mThreadRun = true;
|
||||
Log.i(TAG, "Starting processing thread");
|
||||
while (mThreadRun) {
|
||||
Bitmap bmp = null;
|
||||
|
||||
synchronized (this) {
|
||||
try {
|
||||
this.wait();
|
||||
bmp = processFrame(mFrame);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (bmp != null) {
|
||||
Canvas canvas = mHolder.lockCanvas();
|
||||
calculateMove();
|
||||
if (canvas != null) {
|
||||
canvas.drawBitmap(bmp,0.0f,0.0f,null); //(canvas.getWidth() - getFrameWidth()) / 2, (canvas.getHeight() - getFrameHeight()) / 2, null);
|
||||
|
||||
//drawText(canvas,buffer);
|
||||
mHolder.unlockCanvasAndPost(canvas);
|
||||
}
|
||||
bmp.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawText(Canvas canvas,double[] buffer) {
|
||||
Paint paint = new Paint();
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
paint.setColor(Color.WHITE);
|
||||
DecimalFormat twoPlaces = new DecimalFormat("0.00");
|
||||
String todraw = ":" + twoPlaces.format(buffer[0]) + ":" +
|
||||
twoPlaces.format(buffer[1]) + ":" +
|
||||
twoPlaces.format(buffer[2]) + ":" +
|
||||
left + ":" + right + ":";
|
||||
Paint bpaint = new Paint();
|
||||
bpaint.setStyle(Paint.Style.FILL);
|
||||
bpaint.setColor(Color.BLUE);
|
||||
Rect rect = new Rect(0,0,250,50);
|
||||
canvas.drawRect(rect , bpaint);
|
||||
canvas.drawText(todraw, 0, todraw.length(), 10.0f, 10.0f, paint);
|
||||
}
|
||||
|
||||
void calculateMove() {
|
||||
// buffer[1] holds the light direction info if the phone is in landscape format
|
||||
// small values -> turn left
|
||||
// large values -> turn right
|
||||
// in portrait mode buffer[2] should be used
|
||||
// large values -> turn right
|
||||
// small values -> turn left
|
||||
if( buffer[0] > 100 ) { // light is visible
|
||||
int forwardSpeed = 50;
|
||||
double upScale = 40;
|
||||
//double direction = (buffer[1] - getFrameWidth()/2)/getFrameWidth();
|
||||
double direction = -1.0 * (buffer[2] - getFrameHeight()/2)/getFrameHeight();
|
||||
left = (int)(upScale * direction) + forwardSpeed;
|
||||
right = (int)(-1.0 * upScale * direction) + forwardSpeed;
|
||||
} else {
|
||||
left = 0;
|
||||
right = 0;
|
||||
}
|
||||
left = Math.min(Math.max(left,0),100);
|
||||
right = Math.min(Math.max(right,0),100);
|
||||
|
||||
mActivity.updateMotorControl(left,right);
|
||||
}
|
||||
|
||||
}
|
||||
160
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/SplashMenu.java
Normal file
160
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/SplashMenu.java
Normal file
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Copyright 2010 Guenther Hoelzl, Shawn Brown
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
|
||||
public class SplashMenu extends Activity {
|
||||
|
||||
public static final int MENU_OPTIONS = Menu.FIRST;
|
||||
public static final int MENU_UPLOAD = Menu.FIRST + 1;
|
||||
public static final int MENU_ABOUT = Menu.FIRST + 2;
|
||||
public static final int MENU_QUIT = Menu.FIRST + 3;
|
||||
public static final String MINDDROID_PREFS = "Mprefs";
|
||||
public static final String MINDDROID_ROBOT_TYPE = "MrobotType";
|
||||
private int mRobotType;
|
||||
|
||||
|
||||
public static void quitApplication() {
|
||||
if (MINDdroidCV.isBtOnByUs() || NXJUploader.isBtOnByUs()) {
|
||||
BluetoothAdapter.getDefaultAdapter().disable();
|
||||
MINDdroidCV.setBtOnByUs(false);
|
||||
NXJUploader.setBtOnByUs(false);
|
||||
}
|
||||
splashMenu.finish();
|
||||
|
||||
}
|
||||
|
||||
private View splashMenuView;
|
||||
|
||||
private static Activity splashMenu;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
// show Lama and write nxj-files to SD-card
|
||||
Lama.show(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
mRobotType=lookupRobotType();
|
||||
splashMenuView = new SplashMenuView(getApplicationContext(), this);
|
||||
setContentView(splashMenuView);
|
||||
splashMenu = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if (MINDdroidCV.isBtOnByUs() || NXJUploader.isBtOnByUs()) {
|
||||
BluetoothAdapter.getDefaultAdapter().disable();
|
||||
MINDdroidCV.setBtOnByUs(false);
|
||||
NXJUploader.setBtOnByUs(false);
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
// TODO Auto-generated method stub
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the menu items
|
||||
*/
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
menu.add(0, MENU_OPTIONS, 1, getResources().getString(R.string.options)).setIcon(R.drawable.ic_menu_preferences);
|
||||
menu.add(0, MENU_UPLOAD, 2, getResources().getString(R.string.upload)).setIcon(R.drawable.ic_menu_nxj);
|
||||
menu.add(0, MENU_ABOUT, 3, getResources().getString(R.string.about)).setIcon(R.drawable.ic_menu_about);
|
||||
menu.add(0, MENU_QUIT, 4, getResources().getString(R.string.quit)).setIcon(R.drawable.ic_menu_exit);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disables the menu items
|
||||
*/
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
boolean displayMenu;
|
||||
displayMenu = super.onPrepareOptionsMenu(menu);
|
||||
if (displayMenu) {
|
||||
menu.getItem(1).setEnabled(getRobotType() == R.id.robot_type_4);
|
||||
}
|
||||
return displayMenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles item selections
|
||||
*/
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENU_OPTIONS:
|
||||
Options options = new Options(this);
|
||||
options.show();
|
||||
return true;
|
||||
case MENU_UPLOAD:
|
||||
Intent nxjUpload = new Intent(this.getBaseContext(), NXJUploader.class);
|
||||
this.startActivity(nxjUpload);
|
||||
return true;
|
||||
case MENU_ABOUT:
|
||||
About about = new About();
|
||||
about.show(this);
|
||||
return true;
|
||||
case MENU_QUIT:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setRobotType(int mRobotType) {
|
||||
SharedPreferences mUserPreferences = getSharedPreferences(MINDDROID_PREFS, Context.MODE_PRIVATE);
|
||||
Editor mPrefsEditor = mUserPreferences.edit();
|
||||
mPrefsEditor.putInt(MINDDROID_ROBOT_TYPE, mRobotType);
|
||||
mPrefsEditor.commit();
|
||||
this.mRobotType = mRobotType;
|
||||
}
|
||||
|
||||
public int lookupRobotType() {
|
||||
SharedPreferences mUserPreferences;
|
||||
mUserPreferences = getSharedPreferences(MINDDROID_PREFS, Context.MODE_PRIVATE);
|
||||
return mUserPreferences.getInt(MINDDROID_ROBOT_TYPE, R.id.robot_type_1);
|
||||
}
|
||||
|
||||
public int getRobotType() {
|
||||
return mRobotType;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Copyright 2010 Guenther Hoelzl, Shawn Brown
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
public class SplashMenuView extends View {
|
||||
|
||||
int mScreenWidth;
|
||||
int mScreenHeight;
|
||||
int startButtonYStart;
|
||||
int tutorialButtonYStart;
|
||||
Activity splashMenuActivity;
|
||||
|
||||
Resources res;
|
||||
Bitmap ic_splash_tutorial;
|
||||
Bitmap ic_splash_start;
|
||||
Bitmap ic_splash_legal;
|
||||
Bitmap logo_splash_minddroid;
|
||||
Bitmap mBackgroundImage;
|
||||
|
||||
public SplashMenuView(Context context, Activity splashMenuActivity) {
|
||||
super(context);
|
||||
this.splashMenuActivity = splashMenuActivity;
|
||||
res = context.getResources();
|
||||
}
|
||||
|
||||
private int calcImgHeight(float originalImageHeight, float originalImageWidth) {
|
||||
float screenWidth = mScreenWidth;
|
||||
return (int) (originalImageHeight * (screenWidth / originalImageWidth));
|
||||
}
|
||||
|
||||
private float calcStartPos() {
|
||||
|
||||
float remainingSpace;
|
||||
remainingSpace = mScreenHeight - logo_splash_minddroid.getHeight() - ic_splash_legal.getHeight() - ic_splash_start.getHeight()
|
||||
- ic_splash_tutorial.getHeight();
|
||||
float divider = remainingSpace / 5;
|
||||
startButtonYStart = (int) getStartButtonYPos(divider);
|
||||
return getStartButtonYPos(divider);
|
||||
}
|
||||
|
||||
private float calcTutorialPos() {
|
||||
float remainingSpace;
|
||||
remainingSpace = mScreenHeight - logo_splash_minddroid.getHeight() - ic_splash_legal.getHeight() - ic_splash_start.getHeight()
|
||||
- ic_splash_tutorial.getHeight();
|
||||
|
||||
float divider = remainingSpace / 5;
|
||||
tutorialButtonYStart = (int) getTutorialButtonYPos(divider);
|
||||
return getTutorialButtonYPos(divider);
|
||||
}
|
||||
|
||||
public float getStartButtonYPos(float divider) {
|
||||
return (logo_splash_minddroid.getHeight() + ic_splash_start.getHeight() + (divider * 3));
|
||||
}
|
||||
|
||||
public float getTutorialButtonYPos(float divider) {
|
||||
return (logo_splash_minddroid.getHeight() + (divider * 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
canvas.drawBitmap(mBackgroundImage, 0, 0, null);
|
||||
canvas.drawBitmap(logo_splash_minddroid, 0, 0, null);
|
||||
canvas.drawBitmap(ic_splash_start, 0, calcStartPos(), null);
|
||||
canvas.drawBitmap(ic_splash_tutorial, 0, calcTutorialPos(), null);
|
||||
canvas.drawBitmap(ic_splash_legal, 0, mScreenHeight - ic_splash_legal.getHeight(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
mScreenHeight = h;
|
||||
mScreenWidth = w;
|
||||
setupBitmaps();
|
||||
invalidate();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
|
||||
if (event.getY() > tutorialButtonYStart && event.getY() <= tutorialButtonYStart + ic_splash_tutorial.getHeight()) {
|
||||
Tutorial tutorial = new Tutorial(mScreenWidth, mScreenWidth);
|
||||
tutorial.show(splashMenuActivity);
|
||||
} else if (event.getY() > startButtonYStart && event.getY() <= startButtonYStart + ic_splash_start.getHeight()) {
|
||||
Intent playGame = new Intent(splashMenuActivity.getBaseContext(), MINDdroidCV.class);
|
||||
playGame.putExtra(SplashMenu.MINDDROID_ROBOT_TYPE, ((SplashMenu)splashMenuActivity).getRobotType());
|
||||
splashMenuActivity.startActivity(playGame);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setupBitmaps() {
|
||||
|
||||
ic_splash_tutorial = BitmapFactory.decodeResource(res, R.drawable.ic_splash_tutorial);
|
||||
ic_splash_tutorial = Bitmap.createScaledBitmap(ic_splash_tutorial, mScreenWidth,
|
||||
calcImgHeight(ic_splash_tutorial.getHeight(), ic_splash_tutorial.getWidth()), true);
|
||||
|
||||
ic_splash_start = BitmapFactory.decodeResource(res, R.drawable.ic_splash_start);
|
||||
ic_splash_start = Bitmap.createScaledBitmap(ic_splash_start, mScreenWidth,
|
||||
calcImgHeight(ic_splash_start.getHeight(), ic_splash_start.getWidth()), true);
|
||||
|
||||
ic_splash_legal = BitmapFactory.decodeResource(res, R.drawable.ic_splash_legal);
|
||||
ic_splash_legal = Bitmap.createScaledBitmap(ic_splash_legal, mScreenWidth,
|
||||
calcImgHeight(ic_splash_legal.getHeight(), ic_splash_legal.getWidth()), true);
|
||||
|
||||
logo_splash_minddroid = BitmapFactory.decodeResource(res, R.drawable.logo_splash_minddroid);
|
||||
logo_splash_minddroid = Bitmap.createScaledBitmap(logo_splash_minddroid, mScreenWidth,
|
||||
calcImgHeight(logo_splash_minddroid.getHeight(), logo_splash_minddroid.getWidth()), true);
|
||||
|
||||
mBackgroundImage = BitmapFactory.decodeResource(res, R.drawable.background_1);
|
||||
mBackgroundImage = Bitmap.createScaledBitmap(mBackgroundImage, mScreenWidth, mScreenHeight, true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright 2010 Guenther Hoelzl, Shawn Brown
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
|
||||
public class StartSound extends Thread {
|
||||
private Context myContext;
|
||||
AudioManager myAudioManager;
|
||||
|
||||
public StartSound(Context myContext) {
|
||||
this.myContext = myContext;
|
||||
myAudioManager = (AudioManager) myContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (myAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
|
||||
int ringVolume = myAudioManager.getStreamVolume(AudioManager.STREAM_RING);
|
||||
MediaPlayer myMediaPlayer = MediaPlayer.create(myContext, R.raw.startdroid);
|
||||
myMediaPlayer.start();
|
||||
myMediaPlayer.setVolume( ((float) ringVolume)/10f, ((float) ringVolume)/10f);
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
myMediaPlayer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
123
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/Tutorial.java
Normal file
123
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/Tutorial.java
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Copyright 2010 Guenther Hoelzl, Shawn Brown
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.Window;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
class Tutorial {
|
||||
|
||||
private Dialog dialog;
|
||||
private int currentScene = 0;
|
||||
private Activity myActivity;
|
||||
private int myScreenWidth;
|
||||
private int myScreenHeight;
|
||||
|
||||
// the following array holds the scenes, each scene consists of the following properties
|
||||
// layout, text/image#1, text/image#2, text/image#3
|
||||
private int[] sceneProperties = new int[] {
|
||||
R.layout.tutorial_01, R.string.tutorial_welcome_droid, 0, 0,
|
||||
R.layout.tutorial_02, R.drawable.tutorial_01, R.string.tutorial_bubble_01, 0,
|
||||
R.layout.tutorial_03, R.string.tutorial_a, 0, 0,
|
||||
R.layout.tutorial_02, R.drawable.tutorial_02, R.string.tutorial_bubble_02, 0,
|
||||
R.layout.tutorial_04, 0, 0, 0,
|
||||
R.layout.tutorial_02, R.drawable.tutorial_03, R.string.tutorial_bubble_03, 0,
|
||||
R.layout.tutorial_03, R.string.tutorial_e, 0, 0,
|
||||
R.layout.tutorial_02, R.drawable.tutorial_04, R.string.tutorial_bubble_04, 0,
|
||||
R.layout.tutorial_03, R.string.tutorial_f, 0, 0,
|
||||
R.layout.tutorial_02, R.drawable.tutorial_05, R.string.tutorial_bubble_05, 0,
|
||||
};
|
||||
|
||||
public Tutorial(int screenWidth, int screenHeight) {
|
||||
myScreenWidth = screenWidth;
|
||||
myScreenHeight = screenHeight;
|
||||
}
|
||||
|
||||
public void show(final Activity myActivity) {
|
||||
this.myActivity = myActivity;
|
||||
dialog = new Dialog(myActivity);
|
||||
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
setNewContent();
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public void setNewContent() {
|
||||
// end of the show
|
||||
if (currentScene >= (sceneProperties.length / 4)) {
|
||||
dialog.dismiss();
|
||||
return;
|
||||
}
|
||||
|
||||
TextView myTextView;
|
||||
ImageView myImageView;
|
||||
|
||||
int layout = sceneProperties[currentScene*4];
|
||||
int resource0 = sceneProperties[currentScene*4+1];
|
||||
int resource1 = sceneProperties[currentScene*4+2];
|
||||
int resource2 = sceneProperties[currentScene*4+3];
|
||||
|
||||
// rehide the current dialog shortly
|
||||
if (dialog.isShowing())
|
||||
dialog.dismiss();
|
||||
dialog.setContentView(layout);
|
||||
switch (layout) {
|
||||
case R.layout.tutorial_01:
|
||||
case R.layout.tutorial_03:
|
||||
myTextView = (TextView) dialog.findViewById(R.id.TutorialTextView);
|
||||
myTextView.setText(myActivity.getResources().getString(resource0));
|
||||
case R.layout.tutorial_04:
|
||||
Button buttonOK = (Button) dialog.findViewById(R.id.nextButton);
|
||||
buttonOK.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v)
|
||||
{
|
||||
currentScene++;
|
||||
setNewContent();
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case R.layout.tutorial_02:
|
||||
myImageView = (ImageView) dialog.findViewById(R.id.TutorialImageView);
|
||||
myImageView.setImageResource(resource0);
|
||||
myImageView.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v)
|
||||
{
|
||||
currentScene++;
|
||||
setNewContent();
|
||||
}
|
||||
});
|
||||
|
||||
myTextView = (TextView) dialog.findViewById(R.id.TutorialTextView);
|
||||
myTextView.setTextSize(20);
|
||||
myTextView.setText(myActivity.getResources().getString(resource1));
|
||||
|
||||
break;
|
||||
}
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
254
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/UploadThread.java
Normal file
254
MINDdroidCV_MINDSTORMS/src/com/lego/minddroid/UploadThread.java
Normal file
@@ -0,0 +1,254 @@
|
||||
/**
|
||||
* Copyright 2011 Guenther Hoelzl
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
/**
|
||||
* The tasks have to be done in this thread, so the user interface
|
||||
* isn't blocked.
|
||||
*/
|
||||
public final class UploadThread extends Thread {
|
||||
|
||||
// errorcodes during the task
|
||||
public static final int NO_ERROR = 0;
|
||||
public static final int OPEN_BT_ERROR = 1;
|
||||
public static final int CLOSE_BT_ERROR = 2;
|
||||
public static final int OPEN_FILE_ERROR = 3;
|
||||
public static final int UPLOAD_ERROR = 4;
|
||||
|
||||
// status of the thread
|
||||
public static final int IDLE = 0;
|
||||
public static final int CONNECTING = 1;
|
||||
public static final int UPLOADING = 2;
|
||||
|
||||
private static final int MAX_BUFFER_SIZE = 58;
|
||||
|
||||
private Handler handler;
|
||||
|
||||
private UploadThreadListener listener;
|
||||
|
||||
private Resources resources;
|
||||
|
||||
private BTCommunicator mBTCommunicator;
|
||||
|
||||
private int mFileLength;
|
||||
|
||||
private int mUploaded;
|
||||
|
||||
private int errorCode;
|
||||
|
||||
public UploadThread(UploadThreadListener listener, Resources resources) {
|
||||
this.listener = listener;
|
||||
this.resources = resources;
|
||||
}
|
||||
|
||||
public void setBluetoothCommunicator(BTCommunicator communicator) {
|
||||
mBTCommunicator = communicator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares and starts the looper of the thread
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Looper.prepare();
|
||||
handler = new Handler();
|
||||
Looper.loop();
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a new request for stopping the looper into the queue
|
||||
*/
|
||||
public synchronized void requestStop() {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Looper.myLooper().quit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a new request for uploading into the queue handled by the looper
|
||||
* @param fileName the name of the file to upload including the path
|
||||
*/
|
||||
public synchronized void enqueueUpload(final String nxtAddress, final String fileName) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean uploading = false;
|
||||
try {
|
||||
signalUpdate(CONNECTING);
|
||||
mBTCommunicator.setMACAddress(nxtAddress);
|
||||
mBTCommunicator.createNXTconnection();
|
||||
signalUpdate(UPLOADING);
|
||||
uploading = true;
|
||||
uploadFile(fileName);
|
||||
signalUpdate(IDLE);
|
||||
} catch (FileNotFoundException e) {
|
||||
errorCode = OPEN_FILE_ERROR;
|
||||
} catch (IOException e) {
|
||||
errorCode = uploading ? UPLOAD_ERROR : OPEN_BT_ERROR;
|
||||
} finally {
|
||||
try {
|
||||
mBTCommunicator.destroyNXTconnection();
|
||||
} catch (IOException e) {
|
||||
errorCode = CLOSE_BT_ERROR;
|
||||
}
|
||||
signalUpdate(IDLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maximum number of bytes to be uploaded
|
||||
*/
|
||||
public int getFileLength() {
|
||||
return mFileLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of bytes already uploaded
|
||||
*/
|
||||
public int getBytesUploaded() {
|
||||
return mUploaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the error after an action
|
||||
*/
|
||||
public int getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the error status
|
||||
*/
|
||||
public void resetErrorCode() {
|
||||
errorCode = NO_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a file with the given filename and uplodads it to the robot
|
||||
* @param fileName the name of the file to upload including the path
|
||||
*/
|
||||
private void uploadFile(String fileName) throws FileNotFoundException, IOException {
|
||||
byte[] data = new byte[MAX_BUFFER_SIZE];
|
||||
int readLength;
|
||||
byte handle;
|
||||
InputStream inputStream = null;
|
||||
byte[] message;
|
||||
|
||||
|
||||
// internal file: no path given
|
||||
if (fileName.indexOf('/') == -1) {
|
||||
int dotPosition = fileName.lastIndexOf('.');
|
||||
String resourceName = fileName.substring(0, dotPosition).toLowerCase();
|
||||
int id = resources.getIdentifier(resourceName, "raw", "com.lego.minddroid");
|
||||
inputStream = resources.openRawResource(id);
|
||||
// read stream once for getting it's size and reopen it afterwards
|
||||
mFileLength = 0;
|
||||
while ((readLength = inputStream.read(data)) > 0)
|
||||
mFileLength += readLength;
|
||||
inputStream = resources.openRawResource(id);
|
||||
}
|
||||
// external file: with full path
|
||||
else {
|
||||
File file = new File(fileName);
|
||||
if (!file.exists())
|
||||
throw new FileNotFoundException();
|
||||
inputStream = new FileInputStream(file);
|
||||
mFileLength = (int) file.length();
|
||||
}
|
||||
|
||||
mUploaded = 0;
|
||||
// extract fileName without path
|
||||
int lastSlashPos = fileName.lastIndexOf('/');
|
||||
fileName = fileName.substring(lastSlashPos + 1, fileName.length());
|
||||
|
||||
// send OpenWriteMessage
|
||||
message = LCPMessage.getOpenWriteMessage(fileName, mFileLength);
|
||||
mBTCommunicator.sendMessage(message);
|
||||
// get reply message with handle
|
||||
message = mBTCommunicator.receiveMessage();
|
||||
// check message and get handle
|
||||
if (message == null ||
|
||||
message.length != 4 ||
|
||||
message[0] != LCPMessage.REPLY_COMMAND ||
|
||||
message[1] != LCPMessage.OPEN_WRITE ||
|
||||
message[2] != 0)
|
||||
throw new IOException();
|
||||
|
||||
handle = message[3];
|
||||
while ((readLength = inputStream.read(data)) > 0) {
|
||||
// send WriteMessage and receive reply with next handle
|
||||
message = LCPMessage.getWriteMessage(handle, data, readLength);
|
||||
mBTCommunicator.sendMessage(message);
|
||||
// get reply message and with handle
|
||||
message = mBTCommunicator.receiveMessage();
|
||||
// check message and get handle
|
||||
if (message == null ||
|
||||
message.length != 6 ||
|
||||
message[0] != LCPMessage.REPLY_COMMAND ||
|
||||
message[1] != LCPMessage.WRITE ||
|
||||
message[2] != 0)
|
||||
throw new IOException();
|
||||
|
||||
handle = message[3];
|
||||
mUploaded += readLength;
|
||||
signalUpdate(UPLOADING);
|
||||
}
|
||||
// send CloseFile(handle);
|
||||
message = LCPMessage.getCloseMessage(handle);
|
||||
mBTCommunicator.sendMessage(message);
|
||||
// get reply message with handle
|
||||
message = mBTCommunicator.receiveMessage();
|
||||
// check message
|
||||
if (message == null ||
|
||||
message.length != 4 ||
|
||||
message[0] != LCPMessage.REPLY_COMMAND ||
|
||||
message[1] != LCPMessage.CLOSE ||
|
||||
message[2] != 0)
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the listener activity to make an update at the screen.
|
||||
* @param status the current status of the thread
|
||||
*/
|
||||
private void signalUpdate(int status) {
|
||||
if (listener != null)
|
||||
listener.handleUploadThreadUpdate(status);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright 2011 Guenther Hoelzl
|
||||
*
|
||||
* This file is part of MINDdroid.
|
||||
*
|
||||
* MINDdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MINDdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with MINDdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
package com.lego.minddroid;
|
||||
|
||||
public interface UploadThreadListener {
|
||||
|
||||
/**
|
||||
* This will be called by the UploadThread to signal an update of the
|
||||
* current status.
|
||||
* @param status The current state of the UploadThread
|
||||
*/
|
||||
void handleUploadThreadUpdate(final int status);
|
||||
}
|
||||
Reference in New Issue
Block a user