Recently I was working on this telemedicine project, where I was supposed to get 180 seconds single lead ecg data from the user who is using our Bluetooth enabled Event Loop Recorders through his cell phones Bluetooth receiver and transmit it over to our server along with sending a mail to the doctor all in realtime. Now a difficult aspect of it was surprisingly the Bluetooth part, especially the device discovery part.
After struggling for about 2 days, I finally wrapped it into an activity class and made it reusable, activity class because, tomorrow I might want to have a more good looking Device Discovery screen than what I have today.
Here’s the complete listing of the class,
package com.BTWrapper;
import java.io.IOException;
import java.util.Set;
import java.util.UUID;
import android.app.ListActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
// =============== SOM, This class is meant for wrapping the Bluetooth connecting interface provided by android devices.......
// =============== Basically is a state machine, and the communications will happen in a separate thread so as not to obstruct the main UI
public class BTWrapperActivity extends ListActivity{
static public final int REQUEST_CONNECT_BT = 0x2300;
static private final int REQUEST_ENABLE_BT = 0x1000;
static private BluetoothAdapter mBluetoothAdapter = null;
static private ArrayAdapter<String> mArrayAdapter = null;
//static private Set<BluetoothDevice> btDevices = null;
static private ArrayAdapter<BluetoothDevice> btDevices = null;//BluetoothDevice[] btDevices = null;
// Unique UUID for this application, Basically the SPP Profile
private static final UUID SPP_UUID =
// UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
static private BluetoothSocket mbtSocket = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Change the title of the activity
setTitle("Bluetooth Devices");
// Get the List of paired and available devices
try {
if(initDevicesList() != 0) {
this.finish();
return;
}
} catch(Exception ex) {
this.finish();
return;
}
// Register the Broadcast receiver for handling new BT device discovery
IntentFilter btIntentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mBTReceiver, btIntentFilter);
}
public static BluetoothSocket getSocket() {
return mbtSocket;
}
private void flushData() {
try {
if(mbtSocket != null) {
mbtSocket.close();
mbtSocket = null;
}
if(mBluetoothAdapter != null) {
mBluetoothAdapter.cancelDiscovery();
}
if(btDevices != null) {
btDevices.clear();
btDevices = null;
}
if(mArrayAdapter != null) {
mArrayAdapter.clear();
mArrayAdapter.notifyDataSetChanged();
mArrayAdapter.notifyDataSetInvalidated();
mArrayAdapter = null;
}
finalize();
} catch(Exception ex){}
catch (Throwable e) {}
}
// Som ==== This method will Connect to our SPP Bluetooth Device after discovering and pairing if required
// Do not forget to add the permission for Bluetooth to use this method
// Also this method is very tightly coupled with the above method, for getting the status of bt connection
private int initDevicesList() {
// Flush any Pending Data
flushData();
// Get the Bluetooth Adaptor of the device
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter == null) {
// Weird Condition ==== Implies that the device does not have a bluetooth module ?????
Toast.makeText(getApplicationContext(), "Bluetooth not supported!!", Toast.LENGTH_LONG).show();
return -1;
}
if(mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
mArrayAdapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1);
setListAdapter(mArrayAdapter);
// get the list of devices already paired
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
try {
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} catch(Exception ex){
//ex.getStackTrace();
return -2;
}
Toast.makeText(getApplicationContext(), "Getting all available Bluetooth Devices", Toast.LENGTH_SHORT).show();
return 0;
} // End getDeviceList
@Override
protected void onActivityResult(int reqCode, int resultCode, Intent intent) {
super.onActivityResult(reqCode, resultCode, intent);
switch(reqCode) {
case REQUEST_ENABLE_BT:
if(resultCode == RESULT_OK) {
// Start getting the paired devices list
Set<BluetoothDevice> btDeviceList = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
try {
if (btDeviceList.size() > 0) {
// Loop through paired devices
for (BluetoothDevice device : btDeviceList) {
if(btDeviceList.contains(device) == false) {
btDevices.add(device); // Add the device to the device list
// Add the name and address to an array adapter to show in a ListView
mArrayAdapter.add(device.getName() + "\n" + device.getAddress() /*+ "\n" + "Status : Paired"*/);
//mArrayAdapter.notifyDataSetChanged();
mArrayAdapter.notifyDataSetInvalidated();
}
}
}
} catch(Exception ex){}
}
break;
}
// Also register for new devices which are discovered
mBluetoothAdapter.startDiscovery();
} // End onActivityResult
private final BroadcastReceiver mBTReceiver = 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);
try{
// No paired device found
if(btDevices == null )
{
btDevices = new ArrayAdapter<BluetoothDevice>(getApplicationContext(), android.R.id.text1);
}
// Ensure non repetability
if( btDevices.getPosition(device) < 0 ) {
btDevices.add(device);
// Add the name and address to an array adapter to show in a ListView
mArrayAdapter.add(device.getName() + "\n" + device.getAddress() +"\n" /*+ "Status : Unpaired"*/);
//mArrayAdapter.notifyDataSetChanged();
mArrayAdapter.notifyDataSetInvalidated();
}
} catch(Exception ex)
{
//ex.fillInStackTrace();
}
}
}
};
@Override
protected void onListItemClick(ListView l, View v, final int position, long id) {
super.onListItemClick(l, v, position, id);
// Dont proceed if the bluetooth adapter is not valid
if(mBluetoothAdapter == null) {
return;
}
// Cancel the dicovery if still going on
if(mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
// Try to connect with the selected device,
Toast.makeText(getApplicationContext(), "Connecting to " + btDevices.getItem(position).getName() + "," + btDevices.getItem(position).getAddress(), Toast.LENGTH_SHORT).show();
// made the thread different as the connecting proceedure might break down the system
Thread connectThread = new Thread(new Runnable() {
@Override
public void run() {
try {
mbtSocket = btDevices.getItem(position).createRfcommSocketToServiceRecord(SPP_UUID);
mbtSocket.connect();
} catch(IOException ex) {
//Toast.makeText(getApplicationContext(), "Unable to connect to " + btDevices.getItem(position).getName(), Toast.LENGTH_LONG).show();
runOnUiThread(socketErrorRunnable);
//Handler myHandler = new Handler();
//myHandler.post(socketErrorRunnable);
try {
mbtSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
mbtSocket = null;
return;
} finally {
runOnUiThread(new Runnable() {
@Override
public void run() {
//flushData();
finish();
}
});
}
}
});
connectThread.start();
}
private Runnable socketErrorRunnable = new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "Cannot establish connection", Toast.LENGTH_SHORT).show();
mBluetoothAdapter.startDiscovery();
}
};
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
// Add the menu options
menu.add(0, Menu.FIRST, Menu.NONE, "Refresh Scanning");
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch(item.getItemId()) {
case Menu.FIRST:
initDevicesList();
break;
}
return true;
}
} // End of class definition
Here’s an example of using it:
1. Invoking the activity
// Global declarations
private static BluetoothSocket mbtSocket;
private static InputStream mbtInputStream;
private static OutputStream mbtOutputStream;
// Do this when you want to start discovering
Intent BTIntent = new Intent(this, BTWrapperActivity.class);
this.startActivityForResult(BTIntent, BTWrapperActivity.REQUEST_CONNECT_BT);
2. Getting the connected socket(Remember as found in a variety of other phones, the Bluetooth connection appears as a socket stream)
protected void onActivityResult(int reqCode, int resultCode, Intent intent) {
super.onActivityResult(reqCode, resultCode, intent);
switch(reqCode) {
case BTWrapperActivity.REQUEST_CONNECT_BT:
mbtSocket = BTWrapperActivity.getSocket();
}
3. Transferring data to the remote device
private int sendBTData(int[] dataBytes, int dataLen) {
int dataCount = 0;
try {
for(dataCount = 0; dataCount < dataLen; dataCount++) {
mbtOutputStream.write(dataBytes[dataCount]);
}
}catch(Exception e) {
return -1;
}
return 0;
}
5. Reading data from the remote device
while(true) {
try {
if(mbtInputStream == null || mbtOutputStream == null) {
break;
}
dataBytes[dataCount] = mbtInputStream.read(); //This call will block until it gets something from the stream, will return -1 at
// end of Stream or throw an IOEXception
} catch(IOException ex) {}
}
Can you publish full source code of a sample app that uses this? I got it to compile, but I get an error when I call the "startActivityForResult" method in step 1 from my main activity.
ReplyDeleteDid you set the permissions
ReplyDeleteHi!
ReplyDeleteThis is an amazing project, I have one question, when I try to list the devices nothing comes up, did you have this error? I cant seem to solve it.
Thank you!