diff --git a/README.md b/README.md index 69a1891a..8feab329 100644 --- a/README.md +++ b/README.md @@ -342,12 +342,18 @@ Function `list` lists the paired Bluetooth devices. The success callback is cal Example list passed to success callback. See [BluetoothDevice](http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName\(\)) and [BluetoothClass#getDeviceClass](http://developer.android.com/reference/android/bluetooth/BluetoothClass.html#getDeviceClass\(\)). [{ - "class": 276, + "classMinor": 276, + "classMajor": 1276, + "rssi": -75, + "type": "Classic", "id": "10:BF:48:CB:00:00", "address": "10:BF:48:CB:00:00", "name": "Nexus 7" }, { - "class": 7936, + "classMinor": 7936, + "classMajor": 5276, + "rssi": -32, + "type": "BLE", "id": "00:06:66:4D:00:00", "address": "00:06:66:4D:00:00", "name": "RN42" @@ -546,17 +552,23 @@ Function `discoverUnpaired` discovers unpaired Bluetooth devices. The success ca Example list passed to success callback. [{ - "class": 276, + "classMinor": 276, + "classMajor": 1276, + "rssi": -75, + "type": "Classic", "id": "10:BF:48:CB:00:00", "address": "10:BF:48:CB:00:00", "name": "Nexus 7" }, { - "class": 7936, + "classMinor": 7936, + "classMajor": 5276, + "rssi": -32, + "type": "BLE", "id": "00:06:66:4D:00:00", "address": "00:06:66:4D:00:00", "name": "RN42" }] - + The discovery process takes a while to happen. You can register notify callback with [setDeviceDiscoveredListener](#setdevicediscoveredlistener). You may also want to show a progress indicator while waiting for the discover proces to finish, and the sucess callback to be invoked. @@ -592,12 +604,15 @@ There can be only one registered callback. Example object passed to notify callback. { - "class": 276, + "classMinor": 276, + "classMajor": 1276, + "rssi": -75, + "type": "Classic", "id": "10:BF:48:CB:00:00", "address": "10:BF:48:CB:00:00", "name": "Nexus 7" } - + #### iOS & Windows Phone See [discoverUnpaired](#discoverunpaired). diff --git a/src/android/com/megster/cordova/BluetoothSerial.java b/src/android/com/megster/cordova/BluetoothSerial.java index 77fa6119..ab42df1d 100644 --- a/src/android/com/megster/cordova/BluetoothSerial.java +++ b/src/android/com/megster/cordova/BluetoothSerial.java @@ -51,13 +51,16 @@ public class BluetoothSerial extends CordovaPlugin { private static final String SET_NAME = "setName"; private static final String SET_DISCOVERABLE = "setDiscoverable"; + // Add support for readRSSI + private static final String READ_RSSI = "readRSSI"; + // callbacks private CallbackContext connectCallback; private CallbackContext dataAvailableCallback; private CallbackContext rawDataAvailableCallback; private CallbackContext enableBluetoothCallback; private CallbackContext deviceDiscoveredCallback; - + private BluetoothAdapter bluetoothAdapter; private BluetoothSerialService bluetoothSerialService; @@ -72,15 +75,32 @@ public class BluetoothSerial extends CordovaPlugin { public static final int MESSAGE_DEVICE_NAME = 4; public static final int MESSAGE_TOAST = 5; public static final int MESSAGE_READ_RAW = 6; - + + public static final int MESSAGE_DEVICE_ADDRESS = 7; + + // Device types + public static final int DEVICE_TYPE_UNKNOWN = 0; + public static final int DEVICE_TYPE_CLASSIC = 1; + public static final int DEVICE_TYPE_LE = 2; + public static final int DEVICE_TYPE_DUAL = 3; + // Key names received from the BluetoothChatService Handler public static final String DEVICE_NAME = "device_name"; public static final String TOAST = "toast"; + + public static final String DEVICE_ADDRESS = "device_address"; StringBuffer buffer = new StringBuffer(); private String delimiter; private static final int REQUEST_ENABLE_BLUETOOTH = 1; - + + // ??? + //private static final int REQUEST_CONNECT_DEVICE = 1; + //private static final int REQUEST_ENABLE_BT = 2; + + private String mConnectedName = null; + private String mConnectedAddress = null; + @Override public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException { @@ -228,6 +248,11 @@ public boolean execute(String action, CordovaArgs args, CallbackContext callback discoverIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, discoverableDuration); cordova.getActivity().startActivity(discoverIntent); + } else if (action.equals(READ_RSSI)) { + + boolean secure = true; + readRssi(args, secure, callbackContext); + } else { validAction = false; @@ -246,7 +271,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { if (enableBluetoothCallback != null) { enableBluetoothCallback.success(); } - } else { + } else { Log.d(TAG, "User did *NOT* enable Bluetooth"); if (enableBluetoothCallback != null) { enableBluetoothCallback.error("User did not enable Bluetooth"); @@ -254,7 +279,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } enableBluetoothCallback = null; - } + } } @Override @@ -287,8 +312,9 @@ public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE); try { - JSONObject o = deviceToJSON(device); + JSONObject o = deviceToJSON(device, rssi); unpairedDevices.put(o); if (ddc != null) { PluginResult res = new PluginResult(PluginResult.Status.OK, o); @@ -302,6 +328,7 @@ public void onReceive(Context context, Intent intent) { } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { callbackContext.success(unpairedDevices); cordova.getActivity().unregisterReceiver(this); + bluetoothAdapter.cancelDiscovery(); } } }; @@ -313,17 +340,93 @@ public void onReceive(Context context, Intent intent) { } private JSONObject deviceToJSON(BluetoothDevice device) throws JSONException { + return deviceToJSON(device, (short)0); + } + + private JSONObject deviceToJSON(BluetoothDevice device, short rssi) throws JSONException { JSONObject json = new JSONObject(); + String deviceType; + switch (device.getType()) + { + case DEVICE_TYPE_CLASSIC: deviceType = "Classic"; + break; + case DEVICE_TYPE_LE: deviceType = "BLE"; + break; + case DEVICE_TYPE_DUAL: deviceType = "Dual"; + break; + default: deviceType = "Unknown"; + break; + } + json.put("name", device.getName()); json.put("address", device.getAddress()); json.put("id", device.getAddress()); + json.put("type", deviceType); if (device.getBluetoothClass() != null) { - json.put("class", device.getBluetoothClass().getDeviceClass()); + json.put("classMajor", device.getBluetoothClass().getMajorDeviceClass()); + json.put("classMinor", device.getBluetoothClass().getDeviceClass()); } + json.put("rssi", Integer.toString((int)rssi, 10)); return json; } + private void readRssi(CordovaArgs args, boolean secure, final CallbackContext callbackContext) throws JSONException { + String macAddress = null; + BluetoothDevice device = null; + + if (args.isNull(0)) { + if (mConnectedAddress != null) { + Set bondedDevices = bluetoothAdapter.getBondedDevices(); + for (BluetoothDevice deviceBT : bondedDevices) { + if (mConnectedAddress.equals(deviceBT.getAddress())) { + macAddress = deviceBT.getAddress(); + device = deviceBT; + } + } + } else + callbackContext.error("Read RSSI devices not connected"); + } else { + macAddress = args.getString(0); + device = bluetoothAdapter.getRemoteDevice(macAddress); + } + + final BroadcastReceiver connectReceiver = new BroadcastReceiver() { + + private JSONArray rssiDevices = new JSONArray(); + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) { + BluetoothDevice deviceBT = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + // Doesn't seem to work... always returns -32768 for RSSI + short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE); + try { + JSONObject o = deviceToJSON(deviceBT, rssi); + rssiDevices.put(o); + } catch (JSONException e) { + // This shouldn't happen, log and ignore + Log.e(TAG, "Problem converting device to JSON", e); + } + callbackContext.success(rssiDevices); + cordova.getActivity().unregisterReceiver(this); + } + } + }; + + Activity activity = cordova.getActivity(); + activity.registerReceiver(connectReceiver, new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED)); + + if (device != null) { + connectCallback = callbackContext; + bluetoothSerialService.connect(device, secure); + } else { + callbackContext.error("Read RSSI could not connect to " + macAddress); + } + } + private void connect(CordovaArgs args, boolean secure, CallbackContext callbackContext) throws JSONException { + String macAddress = args.getString(0); BluetoothDevice device = bluetoothAdapter.getRemoteDevice(macAddress); @@ -377,6 +480,8 @@ public void handleMessage(Message msg) { break; case BluetoothSerialService.STATE_NONE: Log.i(TAG, "BluetoothSerialService.STATE_NONE"); + mConnectedName = null; + mConnectedAddress = null; break; } break; @@ -387,6 +492,11 @@ public void handleMessage(Message msg) { break; case MESSAGE_DEVICE_NAME: Log.i(TAG, msg.getData().getString(DEVICE_NAME)); + mConnectedName = msg.getData().getString(DEVICE_NAME); + break; + case MESSAGE_DEVICE_ADDRESS: + Log.i(TAG, msg.getData().getString(DEVICE_ADDRESS)); + mConnectedAddress = msg.getData().getString(DEVICE_ADDRESS); break; case MESSAGE_TOAST: String message = msg.getData().getString(TOAST); diff --git a/src/android/com/megster/cordova/BluetoothSerialService.java b/src/android/com/megster/cordova/BluetoothSerialService.java index 63ce6c53..b279967c 100644 --- a/src/android/com/megster/cordova/BluetoothSerialService.java +++ b/src/android/com/megster/cordova/BluetoothSerialService.java @@ -49,7 +49,7 @@ public class BluetoothSerialService { private ConnectThread mConnectThread; private ConnectedThread mConnectedThread; private int mState; - + // Constants that indicate the current connection state public static final int STATE_NONE = 0; // we're doing nothing public static final int STATE_LISTEN = 1; // now listening for incoming connections @@ -73,7 +73,7 @@ public BluetoothSerialService(Handler handler) { private synchronized void setState(int state) { if (D) Log.d(TAG, "setState() " + mState + " -> " + state); mState = state; - + // Give the new state to the Handler so the UI Activity can update mHandler.obtainMessage(BluetoothSerial.MESSAGE_STATE_CHANGE, state, -1).sendToTarget(); } @@ -83,7 +83,7 @@ private synchronized void setState(int state) { public synchronized int getState() { return mState; } - + /** * Start the chat service. Specifically start AcceptThread to begin a * session in listening (server) mode. Called by the Activity onResume() */ @@ -168,6 +168,13 @@ public synchronized void connected(BluetoothSocket socket, BluetoothDevice devic bundle.putString(BluetoothSerial.DEVICE_NAME, device.getName()); msg.setData(bundle); mHandler.sendMessage(msg); + + // Send the MAC address of the connected device back to the UI Activity + Message msg2 = mHandler.obtainMessage(BluetoothSerial.MESSAGE_DEVICE_ADDRESS); + Bundle bundle2 = new Bundle(); + bundle2.putString(BluetoothSerial.DEVICE_ADDRESS, device.getAddress()); + msg2.setData(bundle2); + mHandler.sendMessage(msg2); setState(STATE_CONNECTED); } diff --git a/www/bluetoothSerial.js b/www/bluetoothSerial.js index 250b8aab..93e071b9 100644 --- a/www/bluetoothSerial.js +++ b/www/bluetoothSerial.js @@ -96,8 +96,13 @@ module.exports = { }, // reads the RSSI of the *connected* peripherial - readRSSI: function (success, failure) { - cordova.exec(success, failure, "BluetoothSerial", "readRSSI", []); + //readRSSI: function (success, failure) { + // cordova.exec(success, failure, "BluetoothSerial", "readRSSI", []); + //}, + + // address is optional for Android but prefered... iOS reads *connected* device and doesn't need address + readRSSI: function (macAddress, success, failure) { + cordova.exec(success, failure, "BluetoothSerial", "readRSSI", [macAddress]); }, showBluetoothSettings: function (success, failure) {