From 7e2443bf8f6ec6c80ec85ba13ba5433d5f565008 Mon Sep 17 00:00:00 2001 From: Giuseppe Lanzi Date: Wed, 23 Nov 2022 08:40:04 +0100 Subject: [PATCH 01/10] android 12 compatibility --- LICENSE.txt | 2 +- package.json | 2 +- plugin.xml | 6 ++-- .../com/megster/cordova/BluetoothSerial.java | 29 ++++++++++++++----- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index cc5700d5..7ee36451 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright 2013-6 Don Coleman +Copyright 2022-11 Giuseppe Lanzi Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/package.json b/package.json index 97efe712..ec14b174 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-plugin-bluetooth-serial", - "version": "0.4.7", + "version": "0.4.8", "description": "Bluetooth Serial Communication Plugin", "main": "./www/bluetoothSerial.js", "cordova": { diff --git a/plugin.xml b/plugin.xml index 19558bbf..8bae342c 100644 --- a/plugin.xml +++ b/plugin.xml @@ -30,9 +30,9 @@ target-dir="src/com/megster/cordova"/> - - - + + + diff --git a/src/android/com/megster/cordova/BluetoothSerial.java b/src/android/com/megster/cordova/BluetoothSerial.java index 8be5575f..2d748dbc 100644 --- a/src/android/com/megster/cordova/BluetoothSerial.java +++ b/src/android/com/megster/cordova/BluetoothSerial.java @@ -84,8 +84,10 @@ public class BluetoothSerial extends CordovaPlugin { private String delimiter; private static final int REQUEST_ENABLE_BLUETOOTH = 1; - // Android 23 requires user to explicitly grant permission for location to discover unpaired - private static final String ACCESS_COARSE_LOCATION = Manifest.permission.ACCESS_COARSE_LOCATION; + // Android 23 requires user to explicitly grant permission for bluetooth to discover unpaired + private static final String BLUETOOTH_SCAN = Manifest.permission.BLUETOOTH_SCAN; + private static final String BLUETOOTH_CONNECT = Manifest.permission.BLUETOOTH_CONNECT; + private static final String BLUETOOTH_ADVERTISE = Manifest.permission.BLUETOOTH_ADVERTISE; private static final int CHECK_PERMISSIONS_REQ_CODE = 2; private CallbackContext permissionCallback; @@ -102,6 +104,19 @@ public boolean execute(String action, CordovaArgs args, CallbackContext callback bluetoothSerialService = new BluetoothSerialService(mHandler); } + if (!cordova.hasPermission(BLUETOOTH_SCAN)) { + LOG.d("RP", "ask permission: scan"); + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_SCAN); + } + if (!cordova.hasPermission(BLUETOOTH_ADVERTISE)) { + LOG.d("RP", "ask permission: advertise"); + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_ADVERTISE); + } + if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { + LOG.d("RP", "ask permission: connect"); + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); + } + boolean validAction = true; if (action.equals(LIST)) { @@ -213,11 +228,11 @@ public boolean execute(String action, CordovaArgs args, CallbackContext callback } else if (action.equals(DISCOVER_UNPAIRED)) { - if (cordova.hasPermission(ACCESS_COARSE_LOCATION)) { + if (cordova.hasPermission(BLUETOOTH_CONNECT)) { discoverUnpairedDevices(callbackContext); } else { permissionCallback = callbackContext; - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, ACCESS_COARSE_LOCATION); + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); } } else if (action.equals(SET_DEVICE_DISCOVERED_LISTENER)) { @@ -471,10 +486,10 @@ public void onRequestPermissionResult(int requestCode, String[] permissions, for(int result:grantResults) { if(result == PackageManager.PERMISSION_DENIED) { - LOG.d(TAG, "User *rejected* location permission"); + LOG.d(TAG, "User *rejected* bluetooth permission"); this.permissionCallback.sendPluginResult(new PluginResult( PluginResult.Status.ERROR, - "Location permission is required to discover unpaired devices.") + "Bluetooth permission is required to discover unpaired devices.") ); return; } @@ -482,7 +497,7 @@ public void onRequestPermissionResult(int requestCode, String[] permissions, switch(requestCode) { case CHECK_PERMISSIONS_REQ_CODE: - LOG.d(TAG, "User granted location permission"); + LOG.d(TAG, "User granted bluetooth permission"); discoverUnpairedDevices(permissionCallback); break; } From 2e3aff7c0e3c1f7b1ad076eec59d5193ee75038f Mon Sep 17 00:00:00 2001 From: Giuseppe Lanzi Date: Wed, 23 Nov 2022 18:16:37 +0100 Subject: [PATCH 02/10] android 12 fixing --- plugin.xml | 8 +- .../com/megster/cordova/BluetoothSerial.java | 158 +++++++----------- 2 files changed, 63 insertions(+), 103 deletions(-) diff --git a/plugin.xml b/plugin.xml index 8bae342c..6345b6a2 100644 --- a/plugin.xml +++ b/plugin.xml @@ -30,9 +30,13 @@ target-dir="src/com/megster/cordova"/> - - + + + + + + diff --git a/src/android/com/megster/cordova/BluetoothSerial.java b/src/android/com/megster/cordova/BluetoothSerial.java index 2d748dbc..e907a90c 100644 --- a/src/android/com/megster/cordova/BluetoothSerial.java +++ b/src/android/com/megster/cordova/BluetoothSerial.java @@ -14,6 +14,9 @@ import android.os.Message; import android.provider.Settings; import android.util.Log; + +import androidx.core.app.ActivityCompat; + import org.apache.cordova.CordovaArgs; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CallbackContext; @@ -85,6 +88,7 @@ public class BluetoothSerial extends CordovaPlugin { private static final int REQUEST_ENABLE_BLUETOOTH = 1; // Android 23 requires user to explicitly grant permission for bluetooth to discover unpaired + private static final String BLUETOOTH_ADMIN = Manifest.permission.BLUETOOTH_ADMIN; private static final String BLUETOOTH_SCAN = Manifest.permission.BLUETOOTH_SCAN; private static final String BLUETOOTH_CONNECT = Manifest.permission.BLUETOOTH_CONNECT; private static final String BLUETOOTH_ADVERTISE = Manifest.permission.BLUETOOTH_ADVERTISE; @@ -93,182 +97,141 @@ public class BluetoothSerial extends CordovaPlugin { @Override public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException { - LOG.d(TAG, "action = " + action); - - if (bluetoothAdapter == null) { + // + if (bluetoothAdapter == null) bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - } - - if (bluetoothSerialService == null) { + if (bluetoothSerialService == null) bluetoothSerialService = new BluetoothSerialService(mHandler); + // + // Ask permissions, to target SDK31+ all those are mandatory (and some have to be requested to the user) + boolean permissionOk = true; + if (!cordova.hasPermission(BLUETOOTH_ADMIN)) { + permissionOk = false; + LOG.d("RP", "ask permission: " + BLUETOOTH_ADMIN); + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_ADMIN); } - - if (!cordova.hasPermission(BLUETOOTH_SCAN)) { - LOG.d("RP", "ask permission: scan"); - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_SCAN); + if (permissionOk && !cordova.hasPermission(BLUETOOTH_CONNECT)) { + permissionOk = false; + LOG.d("RP", "ask permission: " + BLUETOOTH_CONNECT); + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); } - if (!cordova.hasPermission(BLUETOOTH_ADVERTISE)) { - LOG.d("RP", "ask permission: advertise"); - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_ADVERTISE); + if (permissionOk && !cordova.hasPermission(BLUETOOTH_SCAN)) { + permissionOk = false; + LOG.d("RP", "ask permission: " + BLUETOOTH_SCAN); + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_SCAN); } - if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { - LOG.d("RP", "ask permission: connect"); - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); + if (!permissionOk) { + callbackContext.error("User has not given all permission yet, cannot proceed."); + return false; } - + // boolean validAction = true; - + // if (action.equals(LIST)) { - listBondedDevices(callbackContext); - } else if (action.equals(CONNECT)) { - boolean secure = true; connect(args, secure, callbackContext); - } else if (action.equals(CONNECT_INSECURE)) { - // see Android docs about Insecure RFCOMM http://goo.gl/1mFjZY boolean secure = false; connect(args, secure, callbackContext); - } else if (action.equals(DISCONNECT)) { - connectCallback = null; bluetoothSerialService.stop(); callbackContext.success(); - } else if (action.equals(WRITE)) { - byte[] data = args.getArrayBuffer(0); bluetoothSerialService.write(data); callbackContext.success(); - } else if (action.equals(AVAILABLE)) { - callbackContext.success(available()); - } else if (action.equals(READ)) { - callbackContext.success(read()); - } else if (action.equals(READ_UNTIL)) { - String interesting = args.getString(0); callbackContext.success(readUntil(interesting)); - } else if (action.equals(SUBSCRIBE)) { - delimiter = args.getString(0); dataAvailableCallback = callbackContext; - + // PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT); result.setKeepCallback(true); callbackContext.sendPluginResult(result); - } else if (action.equals(UNSUBSCRIBE)) { - delimiter = null; - + // // send no result, so Cordova won't hold onto the data available callback anymore PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT); dataAvailableCallback.sendPluginResult(result); dataAvailableCallback = null; - + // callbackContext.success(); - } else if (action.equals(SUBSCRIBE_RAW)) { - rawDataAvailableCallback = callbackContext; - + // PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT); result.setKeepCallback(true); callbackContext.sendPluginResult(result); - } else if (action.equals(UNSUBSCRIBE_RAW)) { - rawDataAvailableCallback = null; - + // callbackContext.success(); - } else if (action.equals(IS_ENABLED)) { - if (bluetoothAdapter.isEnabled()) { callbackContext.success(); } else { callbackContext.error("Bluetooth is disabled."); } - } else if (action.equals(IS_CONNECTED)) { - if (bluetoothSerialService.getState() == BluetoothSerialService.STATE_CONNECTED) { callbackContext.success(); } else { callbackContext.error("Not connected."); } - } else if (action.equals(CLEAR)) { - buffer.setLength(0); callbackContext.success(); - } else if (action.equals(SETTINGS)) { - Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS); cordova.getActivity().startActivity(intent); callbackContext.success(); - } else if (action.equals(ENABLE)) { - enableBluetoothCallback = callbackContext; Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); cordova.startActivityForResult(this, intent, REQUEST_ENABLE_BLUETOOTH); - } else if (action.equals(DISCOVER_UNPAIRED)) { - - if (cordova.hasPermission(BLUETOOTH_CONNECT)) { - discoverUnpairedDevices(callbackContext); - } else { - permissionCallback = callbackContext; - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); - } - + permissionCallback = callbackContext; + discoverUnpairedDevices(callbackContext); } else if (action.equals(SET_DEVICE_DISCOVERED_LISTENER)) { - this.deviceDiscoveredCallback = callbackContext; - } else if (action.equals(CLEAR_DEVICE_DISCOVERED_LISTENER)) { - this.deviceDiscoveredCallback = null; - } else if (action.equals(SET_NAME)) { - String newName = args.getString(0); bluetoothAdapter.setName(newName); callbackContext.success(); - } else if (action.equals(SET_DISCOVERABLE)) { - + if (!cordova.hasPermission(BLUETOOTH_ADVERTISE)) { + LOG.d("RP", "ask permission: advertise"); + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_ADVERTISE); + } + // int discoverableDuration = args.getInt(0); Intent discoverIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, discoverableDuration); cordova.getActivity().startActivity(discoverIntent); - } else { validAction = false; - } - + // return validAction; } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == REQUEST_ENABLE_BLUETOOTH) { - if (resultCode == Activity.RESULT_OK) { Log.d(TAG, "User enabled Bluetooth"); if (enableBluetoothCallback != null) { @@ -280,7 +243,6 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { enableBluetoothCallback.error("User did not enable Bluetooth"); } } - enableBluetoothCallback = null; } } @@ -296,7 +258,7 @@ public void onDestroy() { private void listBondedDevices(CallbackContext callbackContext) throws JSONException { JSONArray deviceList = new JSONArray(); Set bondedDevices = bluetoothAdapter.getBondedDevices(); - + // for (BluetoothDevice device : bondedDevices) { deviceList.put(deviceToJSON(device)); } @@ -304,19 +266,16 @@ private void listBondedDevices(CallbackContext callbackContext) throws JSONExcep } private void discoverUnpairedDevices(final CallbackContext callbackContext) throws JSONException { - final CallbackContext ddc = deviceDiscoveredCallback; - final BroadcastReceiver discoverReceiver = new BroadcastReceiver() { - private JSONArray unpairedDevices = new JSONArray(); - public void onReceive(Context context, Intent intent) { String action = intent.getAction(); + LOG.d(TAG, "onReceive " + BluetoothDevice.ACTION_FOUND); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); try { - JSONObject o = deviceToJSON(device); + JSONObject o = deviceToJSON(device); unpairedDevices.put(o); if (ddc != null) { PluginResult res = new PluginResult(PluginResult.Status.OK, o); @@ -327,17 +286,22 @@ public void onReceive(Context context, Intent intent) { // This shouldn't happen, log and ignore Log.e(TAG, "Problem converting device to JSON", e); } + } else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) { + LOG.d(TAG, "discovery started..."); } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { + LOG.d(TAG, "Discovery finished!"); callbackContext.success(unpairedDevices); cordova.getActivity().unregisterReceiver(this); } } }; - + // Activity activity = cordova.getActivity(); activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); + activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED)); activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)); - bluetoothAdapter.startDiscovery(); + boolean discoveryStarted = bluetoothAdapter.startDiscovery(); + LOG.d(TAG, "startDiscovery result: "+ discoveryStarted); } private JSONObject deviceToJSON(BluetoothDevice device) throws JSONException { @@ -354,16 +318,15 @@ private JSONObject deviceToJSON(BluetoothDevice device) throws JSONException { private void connect(CordovaArgs args, boolean secure, CallbackContext callbackContext) throws JSONException { String macAddress = args.getString(0); BluetoothDevice device = bluetoothAdapter.getRemoteDevice(macAddress); - + // if (device != null) { connectCallback = callbackContext; bluetoothSerialService.connect(device, secure); buffer.setLength(0); - + // PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT); result.setKeepCallback(true); callbackContext.sendPluginResult(result); - } else { callbackContext.error("Could not connect to " + macAddress); } @@ -378,11 +341,9 @@ public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_READ: buffer.append((String)msg.obj); - if (dataAvailableCallback != null) { sendDataToSubscriber(); } - break; case MESSAGE_READ_RAW: if (rawDataAvailableCallback != null) { @@ -391,7 +352,6 @@ public void handleMessage(Message msg) { } break; case MESSAGE_STATE_CHANGE: - if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1); switch (msg.arg1) { case BluetoothSerialService.STATE_CONNECTED: @@ -481,9 +441,7 @@ private String readUntil(String c) { } @Override - public void onRequestPermissionResult(int requestCode, String[] permissions, - int[] grantResults) throws JSONException { - + public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException { for(int result:grantResults) { if(result == PackageManager.PERMISSION_DENIED) { LOG.d(TAG, "User *rejected* bluetooth permission"); @@ -494,12 +452,10 @@ public void onRequestPermissionResult(int requestCode, String[] permissions, return; } } - - switch(requestCode) { - case CHECK_PERMISSIONS_REQ_CODE: - LOG.d(TAG, "User granted bluetooth permission"); - discoverUnpairedDevices(permissionCallback); - break; + // + if (!cordova.hasPermission(BLUETOOTH_ADMIN) || !cordova.hasPermission(BLUETOOTH_SCAN) || !cordova.hasPermission(BLUETOOTH_CONNECT)) { + LOG.d(TAG, "User has not given all permission yet, cannot proceed."); + return; } } } From c9f9bab8ccd5b368b9ba26b795afca36ea8c205c Mon Sep 17 00:00:00 2001 From: Giuseppe Lanzi Date: Wed, 23 Nov 2022 18:43:24 +0100 Subject: [PATCH 03/10] fix --- plugin.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin.xml b/plugin.xml index 6345b6a2..6c38feaa 100644 --- a/plugin.xml +++ b/plugin.xml @@ -30,8 +30,8 @@ target-dir="src/com/megster/cordova"/> - - + + From 85746e7a441d6153211938d0f5ba80324689d37f Mon Sep 17 00:00:00 2001 From: giuseppelanzi Date: Thu, 24 Nov 2022 08:53:11 +0100 Subject: [PATCH 04/10] Update README.md --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 559dd26b..899f3cbb 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ # Bluetooth Serial Plugin for PhoneGap -This plugin enables serial communication over Bluetooth. It was written for communicating between Android or iOS and an Arduino. +This plugin enables serial communication over Bluetooth. The original one was written for communicating between Android or iOS and an Arduino. This fork is meant to be able to connect and operate to a Bluetooth Printer -Android and Windows Phone use Classic Bluetooth. iOS uses Bluetooth Low Energy. +Android Classic Bluetooth. iOS uses Bluetooth Low Energy. ## Supported Platforms * Android * iOS with [RedBearLab](http://redbearlab.com) BLE hardware, [Adafruit Bluefruit LE](http://www.adafruit.com/products/1697), [Laird BL600](http://www.lairdtech.com/Products/Embedded-Wireless-Solutions/Bluetooth-Radio-Modules/BL600-Series/#.VBI7AS5dUzI), [BlueGiga](https://bluegiga.zendesk.com/entries/29185293--BGScript-spp-over-ble-AT-command-SPP-implementation-for-BLE), or [HC-02](http://www.hc01.com/productdetail?productid=20180314021) -* Windows Phone 8 * Browser (Testing only. See [comments](https://github.com/don/BluetoothSerial/blob/master/src/browser/bluetoothSerial.js).) [Supporting other Bluetooth Low Energy hardware](#supporting-other-ble-hardware) @@ -26,11 +25,11 @@ Install with Cordova cli $ cordova plugin add cordova-plugin-bluetooth-serial -Note that this plugin's id changed from `com.megster.cordova.bluetoothserial` to `cordova-plugin-bluetooth-serial` as part of the migration from the [Cordova plugin repo](http://plugins.cordova.io/) to [npm](https://www.npmjs.com/). +Note that this plugin's id changed from `com.megster.cordova.bluetoothserial` to `cordova-plugin-bluetooth-serial` and then to `cordova-plugin-bluetooth-serial-2` as part of the migration from the [Cordova plugin repo](http://plugins.cordova.io/) to [npm](https://www.npmjs.com/). Then it was change to be able to install the new version via npm. # Examples -There are some [sample projects](https://github.com/don/BluetoothSerial/tree/master/examples) included with the plugin. +There are some [sample projects](https://github.com/giuseppelanzi/BluetoothSerial/tree/master/examples) included with the plugin. # API From b63660ae3df33704ba53a7bd21d75fd0367e10ea Mon Sep 17 00:00:00 2001 From: giuseppelanzi Date: Thu, 24 Nov 2022 08:56:27 +0100 Subject: [PATCH 05/10] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 899f3cbb..313a425b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Android Classic Bluetooth. iOS uses Bluetooth Low Energy. ## Supported Platforms -* Android +* Android with [Mini Thermal Printer POS-5809DD](https://www.amazon.com/Fesjoy-Portable-Wireless-Rechargeable-Plug%EF%BC%88100-240V%EF%BC%89/dp/B094QW8QCQ) * iOS with [RedBearLab](http://redbearlab.com) BLE hardware, [Adafruit Bluefruit LE](http://www.adafruit.com/products/1697), [Laird BL600](http://www.lairdtech.com/Products/Embedded-Wireless-Solutions/Bluetooth-Radio-Modules/BL600-Series/#.VBI7AS5dUzI), [BlueGiga](https://bluegiga.zendesk.com/entries/29185293--BGScript-spp-over-ble-AT-command-SPP-implementation-for-BLE), or [HC-02](http://www.hc01.com/productdetail?productid=20180314021) * Browser (Testing only. See [comments](https://github.com/don/BluetoothSerial/blob/master/src/browser/bluetoothSerial.js).) From f0d5a8d87d59ea82b36dc48de8f29b7e11099c27 Mon Sep 17 00:00:00 2001 From: Giuseppe Lanzi Date: Sun, 4 Dec 2022 12:00:20 +0100 Subject: [PATCH 06/10] old android 9- and fineLocation permissions option --- README.md | 3 +- plugin.xml | 5 +- .../com/megster/cordova/BluetoothSerial.java | 145 ++++++++++++------ www/bluetoothSerial.js | 4 +- 4 files changed, 109 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 559dd26b..997e2501 100644 --- a/README.md +++ b/README.md @@ -535,7 +535,7 @@ If `enable` is called when Bluetooth is already enabled, the user will not promp Discover unpaired devices - bluetoothSerial.discoverUnpaired(success, failure); + bluetoothSerial.discoverUnpaired(fineLocation, success, failure); ### Description @@ -572,6 +572,7 @@ Calling `connect` on an unpaired Bluetooth device should begin the Android pairi ### Parameters +- __fineLocation__: True to enable fine geolocalization, set it as true only if you need to determine the physical position of the bluetooth devices. [optional] - __success__: Success callback function that is invoked with a list of unpaired devices. - __failure__: Error callback function, invoked when error occurs. [optional] diff --git a/plugin.xml b/plugin.xml index 6c38feaa..f6c66cc2 100644 --- a/plugin.xml +++ b/plugin.xml @@ -30,13 +30,16 @@ target-dir="src/com/megster/cordova"/> - + + + + diff --git a/src/android/com/megster/cordova/BluetoothSerial.java b/src/android/com/megster/cordova/BluetoothSerial.java index e907a90c..c9727613 100644 --- a/src/android/com/megster/cordova/BluetoothSerial.java +++ b/src/android/com/megster/cordova/BluetoothSerial.java @@ -92,6 +92,8 @@ public class BluetoothSerial extends CordovaPlugin { private static final String BLUETOOTH_SCAN = Manifest.permission.BLUETOOTH_SCAN; private static final String BLUETOOTH_CONNECT = Manifest.permission.BLUETOOTH_CONNECT; private static final String BLUETOOTH_ADVERTISE = Manifest.permission.BLUETOOTH_ADVERTISE; + private static final String ACCESS_COARSE_LOCATION = Manifest.permission.ACCESS_COARSE_LOCATION; + private static final String ACCESS_FINE_LOCATION = Manifest.permission.ACCESS_FINE_LOCATION; private static final int CHECK_PERMISSIONS_REQ_CODE = 2; private CallbackContext permissionCallback; @@ -104,32 +106,27 @@ public boolean execute(String action, CordovaArgs args, CallbackContext callback if (bluetoothSerialService == null) bluetoothSerialService = new BluetoothSerialService(mHandler); // - // Ask permissions, to target SDK31+ all those are mandatory (and some have to be requested to the user) - boolean permissionOk = true; - if (!cordova.hasPermission(BLUETOOTH_ADMIN)) { - permissionOk = false; - LOG.d("RP", "ask permission: " + BLUETOOTH_ADMIN); - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_ADMIN); - } - if (permissionOk && !cordova.hasPermission(BLUETOOTH_CONNECT)) { - permissionOk = false; - LOG.d("RP", "ask permission: " + BLUETOOTH_CONNECT); - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); - } - if (permissionOk && !cordova.hasPermission(BLUETOOTH_SCAN)) { - permissionOk = false; - LOG.d("RP", "ask permission: " + BLUETOOTH_SCAN); - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_SCAN); - } - if (!permissionOk) { - callbackContext.error("User has not given all permission yet, cannot proceed."); - return false; - } - // boolean validAction = true; // if (action.equals(LIST)) { - listBondedDevices(callbackContext); + if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + if (cordova.hasPermission(BLUETOOTH_CONNECT) && cordova.hasPermission(BLUETOOTH_SCAN)) { + listBondedDevices(callbackContext); + } + else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { + LOG.d("RP", "ask permission: " + BLUETOOTH_CONNECT); + permissionCallback = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); + } + else if (!cordova.hasPermission(BLUETOOTH_SCAN)) { + LOG.d("RP", "ask permission: " + BLUETOOTH_SCAN); + permissionCallback = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_SCAN); + } + } + else { + listBondedDevices(callbackContext); + } } else if (action.equals(CONNECT)) { boolean secure = true; connect(args, secure, callbackContext); @@ -198,12 +195,51 @@ public boolean execute(String action, CordovaArgs args, CallbackContext callback cordova.getActivity().startActivity(intent); callbackContext.success(); } else if (action.equals(ENABLE)) { - enableBluetoothCallback = callbackContext; - Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); - cordova.startActivityForResult(this, intent, REQUEST_ENABLE_BLUETOOTH); + if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + if (cordova.hasPermission(BLUETOOTH_SCAN) && cordova.hasPermission(BLUETOOTH_CONNECT)) { + enableBluetoothCallback = callbackContext; + Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + cordova.startActivityForResult(this, intent, REQUEST_ENABLE_BLUETOOTH); + } + else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { + permissionCallback = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); + } else if (!cordova.hasPermission(BLUETOOTH_SCAN)) { + permissionCallback = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_SCAN); + } + } else { + enableBluetoothCallback = callbackContext; + Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + cordova.startActivityForResult(this, intent, REQUEST_ENABLE_BLUETOOTH); + } } else if (action.equals(DISCOVER_UNPAIRED)) { - permissionCallback = callbackContext; - discoverUnpairedDevices(callbackContext); + // Android.Q and above needs different permissions + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + // I want to ask ACCESS_FINE_LOCATION only if I need it. + // Elseway it would be awkward asking an unneeded permission + boolean fineLocation = false; // args.getBoolean(0); + if (!fineLocation || (cordova.hasPermission(ACCESS_FINE_LOCATION) && fineLocation) && cordova.hasPermission(BLUETOOTH_SCAN) && cordova.hasPermission(BLUETOOTH_CONNECT)) { + discoverUnpairedDevices(callbackContext); + } else if (!cordova.hasPermission(ACCESS_FINE_LOCATION)) { + permissionCallback = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, ACCESS_FINE_LOCATION); + } else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { + permissionCallback = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); + } else if (!cordova.hasPermission(BLUETOOTH_SCAN)) { + permissionCallback = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_SCAN); + } + } + else { + if (cordova.hasPermission(ACCESS_COARSE_LOCATION)) { + discoverUnpairedDevices(callbackContext); + } else { + permissionCallback = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, ACCESS_COARSE_LOCATION); + } + } } else if (action.equals(SET_DEVICE_DISCOVERED_LISTENER)) { this.deviceDiscoveredCallback = callbackContext; } else if (action.equals(CLEAR_DEVICE_DISCOVERED_LISTENER)) { @@ -286,9 +322,11 @@ public void onReceive(Context context, Intent intent) { // This shouldn't happen, log and ignore Log.e(TAG, "Problem converting device to JSON", e); } - } else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) { + } + else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) { LOG.d(TAG, "discovery started..."); - } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { + } + else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { LOG.d(TAG, "Discovery finished!"); callbackContext.success(unpairedDevices); cordova.getActivity().unregisterReceiver(this); @@ -296,12 +334,27 @@ public void onReceive(Context context, Intent intent) { } }; // - Activity activity = cordova.getActivity(); - activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); - activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED)); - activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)); - boolean discoveryStarted = bluetoothAdapter.startDiscovery(); - LOG.d(TAG, "startDiscovery result: "+ discoveryStarted); + // Android.Q and above require different permissions + if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + if (cordova.hasPermission(BLUETOOTH_SCAN) && cordova.hasPermission(BLUETOOTH_CONNECT)) { + Activity activity = cordova.getActivity(); + activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); + activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)); + bluetoothAdapter.startDiscovery(); + } else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { + permissionCallback = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); + } else if (!cordova.hasPermission(BLUETOOTH_SCAN)) { + permissionCallback = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_SCAN); + } + } + else { + Activity activity = cordova.getActivity(); + activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); + activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)); + bluetoothAdapter.startDiscovery(); + } } private JSONObject deviceToJSON(BluetoothDevice device) throws JSONException { @@ -441,21 +494,25 @@ private String readUntil(String c) { } @Override - public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException { + public void onRequestPermissionResult(int requestCode, String[] permissions, + int[] grantResults) throws JSONException { + for(int result:grantResults) { if(result == PackageManager.PERMISSION_DENIED) { - LOG.d(TAG, "User *rejected* bluetooth permission"); + LOG.d(TAG, "User *rejected* location permission"); this.permissionCallback.sendPluginResult(new PluginResult( PluginResult.Status.ERROR, - "Bluetooth permission is required to discover unpaired devices.") - ); + "Location permission is required to discover unpaired devices.") + ); return; } } - // - if (!cordova.hasPermission(BLUETOOTH_ADMIN) || !cordova.hasPermission(BLUETOOTH_SCAN) || !cordova.hasPermission(BLUETOOTH_CONNECT)) { - LOG.d(TAG, "User has not given all permission yet, cannot proceed."); - return; + + switch(requestCode) { + case CHECK_PERMISSIONS_REQ_CODE: + LOG.d(TAG, "User granted location permission"); + discoverUnpairedDevices(permissionCallback); + break; } } } diff --git a/www/bluetoothSerial.js b/www/bluetoothSerial.js index 250b8aab..6d35c1f2 100644 --- a/www/bluetoothSerial.js +++ b/www/bluetoothSerial.js @@ -108,8 +108,8 @@ module.exports = { cordova.exec(success, failure, "BluetoothSerial", "enable", []); }, - discoverUnpaired: function (success, failure) { - cordova.exec(success, failure, "BluetoothSerial", "discoverUnpaired", []); + discoverUnpaired: function (fineLocation, success, failure) { + cordova.exec(success, failure, "BluetoothSerial", "discoverUnpaired", [fineLocation]); }, setDeviceDiscoveredListener: function (notify) { From 522c901d547bbb73a969f8a8eab67fffe1444faf Mon Sep 17 00:00:00 2001 From: Giuseppe Lanzi Date: Sun, 4 Dec 2022 12:10:59 +0100 Subject: [PATCH 07/10] missing fineLocation options fix --- src/android/com/megster/cordova/BluetoothSerial.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/android/com/megster/cordova/BluetoothSerial.java b/src/android/com/megster/cordova/BluetoothSerial.java index c9727613..344c0ae4 100644 --- a/src/android/com/megster/cordova/BluetoothSerial.java +++ b/src/android/com/megster/cordova/BluetoothSerial.java @@ -218,7 +218,7 @@ else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { // I want to ask ACCESS_FINE_LOCATION only if I need it. // Elseway it would be awkward asking an unneeded permission - boolean fineLocation = false; // args.getBoolean(0); + boolean fineLocation = args.getBoolean(0) || false; if (!fineLocation || (cordova.hasPermission(ACCESS_FINE_LOCATION) && fineLocation) && cordova.hasPermission(BLUETOOTH_SCAN) && cordova.hasPermission(BLUETOOTH_CONNECT)) { discoverUnpairedDevices(callbackContext); } else if (!cordova.hasPermission(ACCESS_FINE_LOCATION)) { From dca3c0a91fb667f90cd1a15c34acd6d8903e60d6 Mon Sep 17 00:00:00 2001 From: Giuseppe Lanzi Date: Wed, 29 Mar 2023 17:00:28 +0200 Subject: [PATCH 08/10] fix permission for fineLocation and .S instead of .Q --- .../com/megster/cordova/BluetoothSerial.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/android/com/megster/cordova/BluetoothSerial.java b/src/android/com/megster/cordova/BluetoothSerial.java index 344c0ae4..17983f21 100644 --- a/src/android/com/megster/cordova/BluetoothSerial.java +++ b/src/android/com/megster/cordova/BluetoothSerial.java @@ -109,7 +109,7 @@ public boolean execute(String action, CordovaArgs args, CallbackContext callback boolean validAction = true; // if (action.equals(LIST)) { - if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { if (cordova.hasPermission(BLUETOOTH_CONNECT) && cordova.hasPermission(BLUETOOTH_SCAN)) { listBondedDevices(callbackContext); } @@ -195,7 +195,7 @@ else if (!cordova.hasPermission(BLUETOOTH_SCAN)) { cordova.getActivity().startActivity(intent); callbackContext.success(); } else if (action.equals(ENABLE)) { - if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { if (cordova.hasPermission(BLUETOOTH_SCAN) && cordova.hasPermission(BLUETOOTH_CONNECT)) { enableBluetoothCallback = callbackContext; Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); @@ -214,11 +214,12 @@ else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { cordova.startActivityForResult(this, intent, REQUEST_ENABLE_BLUETOOTH); } } else if (action.equals(DISCOVER_UNPAIRED)) { - // Android.Q and above needs different permissions - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + // Android.S and above needs different permissions + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { // I want to ask ACCESS_FINE_LOCATION only if I need it. // Elseway it would be awkward asking an unneeded permission - boolean fineLocation = args.getBoolean(0) || false; + boolean fineLocation = false; + try {fineLocation = args.getBoolean(0) || false;} catch (Exception e1) {} if (!fineLocation || (cordova.hasPermission(ACCESS_FINE_LOCATION) && fineLocation) && cordova.hasPermission(BLUETOOTH_SCAN) && cordova.hasPermission(BLUETOOTH_CONNECT)) { discoverUnpairedDevices(callbackContext); } else if (!cordova.hasPermission(ACCESS_FINE_LOCATION)) { @@ -334,8 +335,8 @@ else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { } }; // - // Android.Q and above require different permissions - if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + // Android.S and above require different permissions + if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { if (cordova.hasPermission(BLUETOOTH_SCAN) && cordova.hasPermission(BLUETOOTH_CONNECT)) { Activity activity = cordova.getActivity(); activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); From 1514fffbe79fb7cc3dd5d15fc53fddf8acf0f4c4 Mon Sep 17 00:00:00 2001 From: Giuseppe Lanzi Date: Thu, 30 Mar 2023 10:33:32 +0200 Subject: [PATCH 09/10] revert bad copyright change --- LICENSE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.txt b/LICENSE.txt index 7ee36451..cc5700d5 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright 2022-11 Giuseppe Lanzi +Copyright 2013-6 Don Coleman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 30f0d6d229dc76d6a24cc3f7f27b345d25c4f893 Mon Sep 17 00:00:00 2001 From: Giuseppe Lanzi Date: Thu, 13 Apr 2023 15:30:36 +0200 Subject: [PATCH 10/10] changed the permissionCallback method --- .../com/megster/cordova/BluetoothSerial.java | 178 +++++++++++++----- 1 file changed, 126 insertions(+), 52 deletions(-) diff --git a/src/android/com/megster/cordova/BluetoothSerial.java b/src/android/com/megster/cordova/BluetoothSerial.java index 17983f21..ba667d34 100644 --- a/src/android/com/megster/cordova/BluetoothSerial.java +++ b/src/android/com/megster/cordova/BluetoothSerial.java @@ -15,8 +15,6 @@ import android.provider.Settings; import android.util.Log; -import androidx.core.app.ActivityCompat; - import org.apache.cordova.CordovaArgs; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CallbackContext; @@ -94,8 +92,14 @@ public class BluetoothSerial extends CordovaPlugin { private static final String BLUETOOTH_ADVERTISE = Manifest.permission.BLUETOOTH_ADVERTISE; private static final String ACCESS_COARSE_LOCATION = Manifest.permission.ACCESS_COARSE_LOCATION; private static final String ACCESS_FINE_LOCATION = Manifest.permission.ACCESS_FINE_LOCATION; - private static final int CHECK_PERMISSIONS_REQ_CODE = 2; - private CallbackContext permissionCallback; + private static final int CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_CONNECT = 3; + private static final int CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_SCAN = 4; + private static final int CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_ADVERTISE = 5; + private static final int CHECK_PERMISSIONS_REQ_CODE_ACCESS_FINE_LOCATION = 6; + private static final int CHECK_PERMISSIONS_REQ_CODE_ACCESS_COARSE_LOCATION = 7; + private CallbackContext permissionCallbackContext; + private String permissionGrantedExecuteAction; + private CordovaArgs permissionGrantedExecuteArgs; @Override public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException { @@ -107,21 +111,29 @@ public boolean execute(String action, CordovaArgs args, CallbackContext callback bluetoothSerialService = new BluetoothSerialService(mHandler); // boolean validAction = true; + boolean hadPermissions = true; // if (action.equals(LIST)) { if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { if (cordova.hasPermission(BLUETOOTH_CONNECT) && cordova.hasPermission(BLUETOOTH_SCAN)) { + hadPermissions = true; listBondedDevices(callbackContext); } else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { LOG.d("RP", "ask permission: " + BLUETOOTH_CONNECT); - permissionCallback = callbackContext; - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); + hadPermissions = false; + permissionGrantedExecuteAction = action; + permissionGrantedExecuteArgs = args; + permissionCallbackContext = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_CONNECT, BLUETOOTH_CONNECT); } else if (!cordova.hasPermission(BLUETOOTH_SCAN)) { LOG.d("RP", "ask permission: " + BLUETOOTH_SCAN); - permissionCallback = callbackContext; - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_SCAN); + hadPermissions = false; + permissionGrantedExecuteAction = action; + permissionGrantedExecuteArgs = args; + permissionCallbackContext = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_SCAN, BLUETOOTH_SCAN); } } else { @@ -202,11 +214,17 @@ else if (!cordova.hasPermission(BLUETOOTH_SCAN)) { cordova.startActivityForResult(this, intent, REQUEST_ENABLE_BLUETOOTH); } else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { - permissionCallback = callbackContext; - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); + hadPermissions = false; + permissionGrantedExecuteAction = action; + permissionGrantedExecuteArgs = args; + permissionCallbackContext = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_CONNECT, BLUETOOTH_CONNECT); } else if (!cordova.hasPermission(BLUETOOTH_SCAN)) { - permissionCallback = callbackContext; - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_SCAN); + hadPermissions = false; + permissionGrantedExecuteAction = action; + permissionGrantedExecuteArgs = args; + permissionCallbackContext = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_SCAN, BLUETOOTH_SCAN); } } else { enableBluetoothCallback = callbackContext; @@ -220,25 +238,37 @@ else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { // Elseway it would be awkward asking an unneeded permission boolean fineLocation = false; try {fineLocation = args.getBoolean(0) || false;} catch (Exception e1) {} - if (!fineLocation || (cordova.hasPermission(ACCESS_FINE_LOCATION) && fineLocation) && cordova.hasPermission(BLUETOOTH_SCAN) && cordova.hasPermission(BLUETOOTH_CONNECT)) { + if ((!fineLocation || (cordova.hasPermission(ACCESS_FINE_LOCATION) && fineLocation)) && cordova.hasPermission(BLUETOOTH_SCAN) && cordova.hasPermission(BLUETOOTH_CONNECT)) { discoverUnpairedDevices(callbackContext); - } else if (!cordova.hasPermission(ACCESS_FINE_LOCATION)) { - permissionCallback = callbackContext; - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, ACCESS_FINE_LOCATION); + } else if (fineLocation && !cordova.hasPermission(ACCESS_FINE_LOCATION)) { + hadPermissions = false; + permissionGrantedExecuteAction = action; + permissionGrantedExecuteArgs = args; + permissionCallbackContext = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE_ACCESS_FINE_LOCATION, ACCESS_FINE_LOCATION); } else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { - permissionCallback = callbackContext; - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); + hadPermissions = false; + permissionGrantedExecuteAction = action; + permissionGrantedExecuteArgs = args; + permissionCallbackContext = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_CONNECT, BLUETOOTH_CONNECT); } else if (!cordova.hasPermission(BLUETOOTH_SCAN)) { - permissionCallback = callbackContext; - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_SCAN); + hadPermissions = false; + permissionGrantedExecuteAction = action; + permissionGrantedExecuteArgs = args; + permissionCallbackContext = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_SCAN, BLUETOOTH_SCAN); } } else { if (cordova.hasPermission(ACCESS_COARSE_LOCATION)) { discoverUnpairedDevices(callbackContext); } else { - permissionCallback = callbackContext; - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, ACCESS_COARSE_LOCATION); + hadPermissions = false; + permissionGrantedExecuteAction = action; + permissionGrantedExecuteArgs = args; + permissionCallbackContext = callbackContext; + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE_ACCESS_COARSE_LOCATION, ACCESS_COARSE_LOCATION); } } } else if (action.equals(SET_DEVICE_DISCOVERED_LISTENER)) { @@ -251,8 +281,9 @@ else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { callbackContext.success(); } else if (action.equals(SET_DISCOVERABLE)) { if (!cordova.hasPermission(BLUETOOTH_ADVERTISE)) { + hadPermissions = false; LOG.d("RP", "ask permission: advertise"); - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_ADVERTISE); + cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_ADVERTISE, BLUETOOTH_ADVERTISE); } // int discoverableDuration = args.getInt(0); @@ -263,6 +294,12 @@ else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { validAction = false; } // + if (hadPermissions) { + permissionCallbackContext = null; + permissionGrantedExecuteAction = null; + permissionGrantedExecuteArgs = null; + } + // return validAction; } @@ -335,27 +372,12 @@ else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { } }; // - // Android.S and above require different permissions - if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { - if (cordova.hasPermission(BLUETOOTH_SCAN) && cordova.hasPermission(BLUETOOTH_CONNECT)) { - Activity activity = cordova.getActivity(); - activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); - activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)); - bluetoothAdapter.startDiscovery(); - } else if (!cordova.hasPermission(BLUETOOTH_CONNECT)) { - permissionCallback = callbackContext; - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_CONNECT); - } else if (!cordova.hasPermission(BLUETOOTH_SCAN)) { - permissionCallback = callbackContext; - cordova.requestPermission(this, CHECK_PERMISSIONS_REQ_CODE, BLUETOOTH_SCAN); - } - } - else { - Activity activity = cordova.getActivity(); - activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); - activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)); - bluetoothAdapter.startDiscovery(); - } + // Permissions has already been given by exec method + // Start discovering + Activity activity = cordova.getActivity(); + activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); + activity.registerReceiver(discoverReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)); + bluetoothAdapter.startDiscovery(); } private JSONObject deviceToJSON(BluetoothDevice device) throws JSONException { @@ -497,23 +519,75 @@ private String readUntil(String c) { @Override public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException { - + // Based on the result and the given permission, return different things. + // Errors are raised for the proper permission. + String logMessage = ""; + String pluginResultMessage = ""; + // for(int result:grantResults) { if(result == PackageManager.PERMISSION_DENIED) { - LOG.d(TAG, "User *rejected* location permission"); - this.permissionCallback.sendPluginResult(new PluginResult( + switch(requestCode) { + case CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_SCAN: + logMessage = "User *rejected* scan permission"; + pluginResultMessage = "Scan permission is required."; + break; + case CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_CONNECT: + logMessage = "User *rejected* connect permission"; + pluginResultMessage = "Connect permission is required."; + break; + case CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_ADVERTISE: + logMessage = "User *rejected* advertise permission"; + pluginResultMessage = "Advertise permission is required."; + break; + case CHECK_PERMISSIONS_REQ_CODE_ACCESS_FINE_LOCATION: + logMessage = "User *rejected* fine location permission"; + pluginResultMessage = "Fine location permission is required."; + break; + case CHECK_PERMISSIONS_REQ_CODE_ACCESS_COARSE_LOCATION: + logMessage = "User *rejected* coarse location permission"; + pluginResultMessage = "Coarse location permission is required."; + break; + } + // + LOG.d(TAG, logMessage); + this.permissionCallbackContext.sendPluginResult(new PluginResult( PluginResult.Status.ERROR, - "Location permission is required to discover unpaired devices.") + pluginResultMessage) ); return; } } - + // + // Permission was granted, generate different message for each permission. switch(requestCode) { - case CHECK_PERMISSIONS_REQ_CODE: - LOG.d(TAG, "User granted location permission"); - discoverUnpairedDevices(permissionCallback); + case CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_SCAN: + logMessage = "User *granted* scan permission"; + pluginResultMessage = "Scan permission is granted."; + break; + case CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_CONNECT: + logMessage = "User *granted* connect permission"; + pluginResultMessage = "Connect permission is granted."; + break; + case CHECK_PERMISSIONS_REQ_CODE_BLUETOOTH_ADVERTISE: + logMessage = "User *granted* advertise permission"; + pluginResultMessage = "Advertise permission is granted."; + break; + case CHECK_PERMISSIONS_REQ_CODE_ACCESS_FINE_LOCATION: + logMessage = "User *granted* fine location permission"; + pluginResultMessage = "Fine location permission is granted."; + break; + case CHECK_PERMISSIONS_REQ_CODE_ACCESS_COARSE_LOCATION: + logMessage = "User *rejected* coarse location permission"; + pluginResultMessage = "Coarse location permission is granted."; break; } + // + // Log the granted permission + LOG.d(TAG, logMessage); + // + // Launch again the "execute" method. + // This way the plugin can ask for all the permissions and launch the proper method when granted, base on why the + // permission was requested (list, discover, ecc.) + execute(permissionGrantedExecuteAction, permissionGrantedExecuteArgs, permissionCallbackContext); } }