deviceList = new ArrayList<>();
private DiscoveredDeviceListener discoveredDeviceListener;
- private final BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
- @Override
- public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
- onDeviceFound(device, rssi, scanRecord);
- }
- };
+ private BleScanner scanner;
+ private BleScanner.ScanCallback scanCallback = this::onDeviceFound;
public BluetoothServer(Context context) {
this.context = context;
}
- private BluetoothAdapter bluetoothAdapter() {
+ private BluetoothAdapter getBluetoothAdapter() {
if (bluetoothAdapter == null) {
BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
@@ -104,22 +102,35 @@ private LeScanResult onDeviceFound(BluetoothDevice device, int rssi, byte[] scan
}
public void scanStart() {
- Timber.d("BLE scan started...");
- bluetoothAdapter().startLeScan(leScanCallback);
+ Timber.d("BLE startScan started...");
+
+ if (scanner == null) {
+ scanner = getScanner();
+ }
+
+ scanner.startScan();
+
final Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
-// "Never scan on a loop, and set a time limit on your scan. " - https://developer.android.com/guide/topics/connectivity/bluetooth-le.html#find
- bluetoothAdapter().stopLeScan(leScanCallback);
- Timber.d("BLE scan stopped on timeout " + BluetoothServer.COMMAND_SCAN_DELAY / 1000 + " sec");
- }
+ handler.postDelayed(() -> {
+
+// "Never startScan on a loop, and set a time limit on your startScan. " - https://developer.android.com/guide/topics/connectivity/bluetooth-le.html#find
+ scanner.stopScan();
+ Timber.d("BLE startScan stopped on timeout " + BluetoothServer.COMMAND_SCAN_DELAY / 1000 + " sec");
+
}, BluetoothServer.COMMAND_SCAN_DELAY);
}
+ private BleScanner getScanner() {
+ if (scanner == null) {
+ scanner = BleHelpersFactory.getScanner(scanCallback, getBluetoothAdapter());
+ }
+
+ return scanner;
+ }
+
public void scanStop() {
Log.d(TAG, "Stop BLE Scan");
- bluetoothAdapter().stopLeScan(leScanCallback);
+ scanner.stopScan();
}
protected void addDevice(final LeScanResult device) {
@@ -195,18 +206,15 @@ public void fail(String message) {
@Override
public void call(BluetoothDevice device) {
Timber.d("device found. connecting");
- connectAndSave(address, device, new InteractiveGattCallback.OnConnectedListener() {
- @Override
- public void call() {
- Timber.d("calling operation on successfull connection");
- applyForConnection(address, operation);
- }
+ connectAndSave(address, device, () -> {
+ Timber.d("calling operation on successfull connection");
+ applyForConnection(address, operation);
});
}
@Override
public void fail(String message) {
- Timber.d("device " + address + " not found - try to scan once more");
+ Timber.d("device " + address + " not found - try to startScan once more");
operation.fail(message);
}
});
@@ -447,28 +455,26 @@ public interface DiscoveredDeviceListener {
private class ScanCallbacks {
private String address;
private ConnectionOperation operation;
- private BluetoothAdapter.LeScanCallback localCallback;
+ private BleScanner.ScanCallback localCallback;
+ private BleScanner scanner;
public ScanCallbacks(final String address, final ConnectionOperation operation) {
this.address = address;
this.operation = operation;
- this.localCallback = new BluetoothAdapter.LeScanCallback() {
+
+ this.localCallback = new BleScanner.ScanCallback() {
private boolean found = false;
@Override
- public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
-
- if (!found && bluetoothDevice.getAddress().equals(address)) {
+ public void onDeviceFound(BluetoothDevice device, int rssi, byte[] scanRecord) {
+ if (!found && device.getAddress().equals(address)) {
found = true;
stop();
- onDeviceFound(bluetoothDevice, i, bytes);
+ onDeviceFound(device, rssi, scanRecord);
- connectAndSave(address, bluetoothDevice, new InteractiveGattCallback.OnConnectedListener() {
- @Override
- public void call() {
- Timber.d("on connected - calling operation");
- applyForConnection(address, operation);
- }
+ connectAndSave(address, device, () -> {
+ Timber.d("on connected - calling operation");
+ applyForConnection(address, operation);
});
}
}
@@ -477,25 +483,23 @@ public void call() {
public void start() {
Timber.d("no device or connection - scanning for device");
-// scan for device, add it to discovered devices, connect and call operation
+// startScan for device, add it to discovered devices, connect and call operation
- bluetoothAdapter().startLeScan(localCallback);
+ scanner = BleHelpersFactory.getScanner(localCallback, getBluetoothAdapter());
+ scanner.startScan();
final Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
-// "Never scan on a loop, and set a time limit on your scan. " - https://developer.android.com/guide/topics/connectivity/bluetooth-le.html#find
- Timber.d("on timeout");
- stop();
- operation.fail(BTLEApplication.getApplication().getString(R.string.status_notfound_timeout));
- }
+ handler.postDelayed(() -> {
+// "Never startScan on a loop, and set a time limit on your startScan. " - https://developer.android.com/guide/topics/connectivity/bluetooth-le.html#find
+ Timber.d("on timeout");
+ stop();
+ operation.fail(BTLEApplication.getApplication().getString(R.string.status_notfound_timeout));
}, BluetoothServer.COMMAND_SCAN_DELAY);
}
public void stop() {
Timber.d("stop");
- bluetoothAdapter().stopLeScan(localCallback);
+ scanner.stopScan();
}
}
diff --git a/app/src/main/java/com/dataart/btle_android/btle_gateway/build.gradle b/app/src/main/java/com/dataart/btle_android/btle_gateway/build.gradle
deleted file mode 100644
index e69de29..0000000
diff --git a/app/src/main/java/com/dataart/btle_android/btle_gateway/gateway_helpers/ValidationHelper.java b/app/src/main/java/com/dataart/btle_android/btle_gateway/gateway_helpers/ValidationHelper.java
new file mode 100644
index 0000000..e2815d3
--- /dev/null
+++ b/app/src/main/java/com/dataart/btle_android/btle_gateway/gateway_helpers/ValidationHelper.java
@@ -0,0 +1,74 @@
+package com.dataart.btle_android.btle_gateway.gateway_helpers;
+
+import android.content.Context;
+
+import com.dataart.android.devicehive.device.future.CmdResFuture;
+import com.dataart.btle_android.R;
+import com.dataart.btle_android.btle_gateway.gatt_callbacks.CmdResult;
+import com.google.common.base.Optional;
+
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Created by Constantine Mars on 6/14/16.
+ *
+ * Validation utils
+ */
+
+@RequiredArgsConstructor
+public class ValidationHelper {
+ private static final String VALUE_REGEX = "([a-fA-F0-9]{2}){1,}";
+ private static final String ADDRESS_REGEX = "(([a-fA-F0-9]{2}:){5})([a-fA-F0-9]{2})";
+ private static final String SERVICE_CHARACTERISTIC_UUID_REGEX = "([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})|([a-fA-F0-9]{4})";
+ private final Context context;
+
+ public Optional validateAddress(final String command, final String address) {
+ return validate(command, R.string.cmd_requires_address, address, ADDRESS_REGEX);
+ }
+
+ public Optional validateServiceUUID(final String command, final String serviceUUID) {
+ return validate(command, R.string.cmd_requires_service_uuid, serviceUUID, SERVICE_CHARACTERISTIC_UUID_REGEX);
+ }
+
+ public Optional validateCharacteristicUUID(final String command, final String serviceUUID) {
+ return validate(command, R.string.cmd_requires_characteristic_uuid, serviceUUID, SERVICE_CHARACTERISTIC_UUID_REGEX);
+ }
+
+ public Optional validateValue(final String command, final String value) {
+ return validate(command, R.string.cmd_requires_value, value, VALUE_REGEX);
+ }
+
+ public Optional validate(final String command, int messageResId, final String value, final String regex) {
+ if (value == null || !value.matches(regex)) {
+ return Optional.of(new CmdResFuture(
+ CmdResult.failWithStatus(context.getString(messageResId, command))
+ ));
+ }
+
+ return Optional.absent();
+ }
+
+ public Optional validateCharacteristics(final String command, final String address, final String serviceUUID) {
+ return validateAddress(command, address)
+ .or(validateServiceUUID(command, serviceUUID));
+ }
+
+ public Optional validateNotifications(final String command, final String address, final String serviceUUID) {
+ return validateAddress(command, address)
+ .or(validateServiceUUID(command, serviceUUID));
+ }
+
+ public Optional validateRead(final String command, final String address, final String serviceUUID, final String characteristicUUID) {
+ return validateAddress(command, address)
+ .or(validateServiceUUID(command, serviceUUID))
+ .or(validateCharacteristicUUID(command, characteristicUUID));
+ }
+
+
+ public Optional validateWrite(String command, String address, String serviceUUID, String characteristicUUID, String value) {
+ return validateAddress(command, address)
+ .or(validateServiceUUID(command, serviceUUID))
+ .or(validateCharacteristicUUID(command, characteristicUUID))
+ .or(validateValue(command, value));
+ }
+}
diff --git a/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt/callbacks/CmdResult.java b/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt_callbacks/CmdResult.java
similarity index 97%
rename from app/src/main/java/com/dataart/btle_android/btle_gateway/gatt/callbacks/CmdResult.java
rename to app/src/main/java/com/dataart/btle_android/btle_gateway/gatt_callbacks/CmdResult.java
index d3ca8d5..4b545d2 100644
--- a/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt/callbacks/CmdResult.java
+++ b/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt_callbacks/CmdResult.java
@@ -1,4 +1,4 @@
-package com.dataart.btle_android.btle_gateway.gatt.callbacks;
+package com.dataart.btle_android.btle_gateway.gatt_callbacks;
import android.content.Context;
@@ -16,6 +16,7 @@ public class CmdResult {
protected String serviceUUID;
protected String characteristicUUID;
protected String device;
+ protected Context context;
public CmdResult(String serviceUUID, String characteristicUUID, String device, Context context) {
this.serviceUUID = serviceUUID;
@@ -24,12 +25,6 @@ public CmdResult(String serviceUUID, String characteristicUUID, String device, C
this.context = context;
}
- protected Context context;
-
- private String jsonFullStatus(int statusStringId) {
- return jsonFullStatus(context.getString(statusStringId));
- }
-
private static String jsonStatusOk() {
return new Gson().toJson(StatusJson.Status.statusOk());
}
@@ -50,6 +45,30 @@ private static String jsonStatus(String status){
return new Gson().toJson(new StatusJson.Status(status));
}
+ public static CommandResult success() {
+ return new CommandResult(CommandResult.STATUS_COMLETED, jsonStatusOk());
+ }
+
+ public static CommandResult successWithObject(Object object) {
+ return new CommandResult(CommandResult.STATUS_COMLETED, jsonStatusWithObject(object));
+ }
+
+ public static CommandResult failWithStatus(String status) {
+ return new CommandResult(CommandResult.STATUS_FAILED, jsonStatus(status));
+ }
+
+ public static CommandResult failWithStatus(int strResId) {
+ return new CommandResult(CommandResult.STATUS_FAILED, jsonStatus(strResId));
+ }
+
+ public static CommandResult failTimeoutReached() {
+ return new CommandResult(CommandResult.STATUS_FAILED, jsonStatusTimeoutReached());
+ }
+
+ private String jsonFullStatus(int statusStringId) {
+ return jsonFullStatus(context.getString(statusStringId));
+ }
+
private String jsonFullStatus(String status) {
return new Gson().toJson(new StatusJson.FullStatus(
status,
@@ -73,30 +92,10 @@ private String jsonFullStatusWithVal(int statusResId, String val) {
return jsonFullStatusWithVal(BTLEApplication.getApplication().getString(statusResId), val);
}
- public static CommandResult success() {
- return new CommandResult(CommandResult.STATUS_COMLETED, jsonStatusOk());
- }
-
- public static CommandResult successWithObject(Object object) {
- return new CommandResult(CommandResult.STATUS_COMLETED, jsonStatusWithObject(object));
- }
-
- public static CommandResult failWithStatus(String status) {
- return new CommandResult(CommandResult.STATUS_FAILED, jsonStatus(status));
- }
-
protected CommandResult withStatusAndVal(int statusResId, String val) {
return new CommandResult(CommandResult.STATUS_FAILED, jsonFullStatusWithVal(BTLEApplication.getApplication().getString(statusResId), val));
}
- public static CommandResult failWithStatus(int strResId) {
- return new CommandResult(CommandResult.STATUS_FAILED, jsonStatus(strResId));
- }
-
- public static CommandResult failTimeoutReached() {
- return new CommandResult(CommandResult.STATUS_FAILED, jsonStatusTimeoutReached());
- }
-
protected CommandResult sucessFull() {
return new CommandResult(CommandResult.STATUS_COMLETED, jsonFullStatus(R.string.status_json_success));
}
diff --git a/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt/callbacks/DeviceConnection.java b/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt_callbacks/DeviceConnection.java
similarity index 86%
rename from app/src/main/java/com/dataart/btle_android/btle_gateway/gatt/callbacks/DeviceConnection.java
rename to app/src/main/java/com/dataart/btle_android/btle_gateway/gatt_callbacks/DeviceConnection.java
index 5ace258..fd608bb 100644
--- a/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt/callbacks/DeviceConnection.java
+++ b/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt_callbacks/DeviceConnection.java
@@ -1,7 +1,6 @@
-package com.dataart.btle_android.btle_gateway.gatt.callbacks;
+package com.dataart.btle_android.btle_gateway.gatt_callbacks;
import android.bluetooth.BluetoothGatt;
-import android.bluetooth.BluetoothGattCallback;
/**
* Created by Constantine Mars on 3/27/15.
diff --git a/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt/callbacks/InteractiveGattCallback.java b/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt_callbacks/InteractiveGattCallback.java
similarity index 98%
rename from app/src/main/java/com/dataart/btle_android/btle_gateway/gatt/callbacks/InteractiveGattCallback.java
rename to app/src/main/java/com/dataart/btle_android/btle_gateway/gatt_callbacks/InteractiveGattCallback.java
index 6726d9b..9a309b9 100644
--- a/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt/callbacks/InteractiveGattCallback.java
+++ b/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt_callbacks/InteractiveGattCallback.java
@@ -1,4 +1,4 @@
-package com.dataart.btle_android.btle_gateway.gatt.callbacks;
+package com.dataart.btle_android.btle_gateway.gatt_callbacks;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
@@ -403,11 +403,11 @@ public void call(BluetoothGatt gatt) {
// before execute call, we need convert uuids to long format because Android BLE Api understands only last
// converstion can't be done in constructor because at that moment services might be not discovered
if ((serviceUUID = getFullServiceUuid(serviceUUID)) == null) {
- future.call(CmdResult.failWithStatus(context.getString(R.string.status_service_uuid_nf)));
+ future.call(failWithStatus(context.getString(R.string.status_service_uuid_nf)));
return;
}
if ((characteristicUUID = getFullCharacteristicUuid(characteristicUUID)) == null) {
- future.call(CmdResult.failWithStatus(context.getString(R.string.status_char_uuid_nf)));
+ future.call(failWithStatus(context.getString(R.string.status_char_uuid_nf)));
return;
}
// call
@@ -415,7 +415,7 @@ public void call(BluetoothGatt gatt) {
try {
service = gatt.getService(UUID.fromString(serviceUUID));
} catch (Exception e) {
- future.call(CmdResult.failWithStatus("gatt.getService(uuid) crashed: " + e.getMessage()));
+ future.call(failWithStatus("gatt.getService(uuid) crashed: " + e.getMessage()));
return;
}
diff --git a/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt/callbacks/StatusJson.java b/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt_callbacks/StatusJson.java
similarity index 68%
rename from app/src/main/java/com/dataart/btle_android/btle_gateway/gatt/callbacks/StatusJson.java
rename to app/src/main/java/com/dataart/btle_android/btle_gateway/gatt_callbacks/StatusJson.java
index b5e2843..2c07bc1 100644
--- a/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt/callbacks/StatusJson.java
+++ b/app/src/main/java/com/dataart/btle_android/btle_gateway/gatt_callbacks/StatusJson.java
@@ -1,28 +1,34 @@
-package com.dataart.btle_android.btle_gateway.gatt.callbacks;
-
-import android.os.Parcel;
-import android.os.Parcelable;
+package com.dataart.btle_android.btle_gateway.gatt_callbacks;
import com.dataart.btle_android.BTLEApplication;
import com.dataart.btle_android.R;
-import org.apache.commons.codec.binary.Hex;
-
/**
* Created by Constantine Mars on 4/3/15.
*
* Wraps data for json conversion
*/
-public abstract class StatusJson {
- public static class Status {
+abstract class StatusJson {
+ static String bytes2String(byte[] value) {
+ String s = "";
+ for (byte b : value) {
+ if (!s.isEmpty()) {
+ s += ", ";
+ }
+ s += String.format("0x%02X", b);
+ }
+ return s;
+ }
+
+ static class Status {
private String status;
- public Status(String status) {
+ Status(String status) {
this.status = status;
}
- public static Status statusOk() {
- return new Status(BTLEApplication.getApplication().getString(R.string.status_ok));
+ static Status statusOk() {
+ return new Status(BTLEApplication.getApplication().getString(android.R.string.ok));
}
public static Status statusOkWithVal(String val) {
@@ -30,14 +36,14 @@ public static Status statusOkWithVal(String val) {
}
public static Status statusFail() {
- return new Status(BTLEApplication.getApplication().getString(R.string.status_ok));
+ return new Status(BTLEApplication.getApplication().getString(android.R.string.ok));
}
public static Status statusFailWithVal(String val) {
return new Status(val);
}
- public static Status statusTimeoutReached() {
+ static Status statusTimeoutReached() {
return new Status(BTLEApplication.getApplication().getString(R.string.status_timeout));
}
@@ -46,40 +52,31 @@ public String getStatus() {
}
}
- public static class StatusWithObject {
+ static class StatusWithObject {
private Object result;
- public StatusWithObject(Object result) {
+ StatusWithObject(Object result) {
this.result = result;
}
- public static StatusWithObject statusWithObject(Object object){
+ static StatusWithObject statusWithObject(Object object) {
return new StatusWithObject(object);
}
}
- public static class FullStatusWithVal extends FullStatus {
+ static class FullStatusWithVal extends FullStatus {
private String value;
- public FullStatusWithVal(String status, String device, String serviceUUID, String characteristicUUID, String value) {
+ FullStatusWithVal(String status, String device, String serviceUUID, String characteristicUUID, String value) {
super(status, device, serviceUUID, characteristicUUID);
this.value = value;
}
}
- public static class FullStatus {
+ static class FullStatus {
private String status;
private String device;
private String serviceUUID;
-
- public String getDevice() {
- return device;
- }
-
- public String getStatus() {
- return status;
- }
-
private String characteristicUUID;
public FullStatus(String status, String device, String serviceUUID, String characteristicUUID) {
@@ -88,6 +85,14 @@ public FullStatus(String status, String device, String serviceUUID, String chara
this.serviceUUID = serviceUUID;
this.characteristicUUID = characteristicUUID;
}
+
+ public String getDevice() {
+ return device;
+ }
+
+ public String getStatus() {
+ return status;
+ }
}
public static class FullStatusWithByteArray extends FullStatus {
@@ -98,15 +103,4 @@ public FullStatusWithByteArray(String status, byte[] value, String device, Strin
this.value = value;
}
}
-
- public static String bytes2String(byte[] value){
- String s = "";
- for(byte b:value){
- if(!s.isEmpty()){
- s+=", ";
- }
- s+=String.format("0x%02X",b);
- }
- return s;
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/dataart/btle_android/devicehive/BTLEDeviceHive.java b/app/src/main/java/com/dataart/btle_android/devicehive/BTLEDeviceHive.java
index 4320e26..82efc36 100644
--- a/app/src/main/java/com/dataart/btle_android/devicehive/BTLEDeviceHive.java
+++ b/app/src/main/java/com/dataart/btle_android/devicehive/BTLEDeviceHive.java
@@ -12,6 +12,7 @@
import com.dataart.android.devicehive.device.CommandResult;
import com.dataart.android.devicehive.device.Device;
import com.dataart.android.devicehive.device.future.SimpleCallableFuture;
+import com.dataart.btle_android.BuildConfig;
import java.util.LinkedList;
import java.util.List;
@@ -48,7 +49,7 @@ public BTLEDeviceHive(Context context) {
private static DeviceData getTestDeviceData() {
final Network network = new Network("AndroidBTLE", "");
- final DeviceClass deviceClass = new DeviceClass("AndroidBTLE Device", "1.0");
+ final DeviceClass deviceClass = new DeviceClass("Android BTLE Device", BuildConfig.VERSION_NAME);
return new DeviceData(
new BTLEDevicePreferences().getGatewayId(),
diff --git a/app/src/main/java/com/dataart/btle_android/helpers/BleHelpersFactory.java b/app/src/main/java/com/dataart/btle_android/helpers/BleHelpersFactory.java
new file mode 100644
index 0000000..fb36f9d
--- /dev/null
+++ b/app/src/main/java/com/dataart/btle_android/helpers/BleHelpersFactory.java
@@ -0,0 +1,48 @@
+package com.dataart.btle_android.helpers;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.os.Build;
+
+import com.dataart.btle_android.helpers.ble.base.BleInitializer;
+import com.dataart.btle_android.helpers.ble.base.BleScanner;
+import com.dataart.btle_android.helpers.ble.initializers.BleInitializerJ;
+import com.dataart.btle_android.helpers.ble.initializers.BleInitializerL;
+import com.dataart.btle_android.helpers.ble.initializers.BleInitializerM;
+import com.dataart.btle_android.helpers.ble.scanners.BleScannerJ;
+import com.dataart.btle_android.helpers.ble.scanners.BleScannerL;
+
+/**
+ * Created by Constantine Mars on 12/13/15.
+ * Factory for creating BLE init helper according to OS version
+ */
+public class BleHelpersFactory {
+
+ public static BleInitializer getInitializer(Activity activity, BleInitializer.InitCompletionCallback initCompletionCallback) {
+ final int osVersion = Build.VERSION.SDK_INT;
+
+ if (osVersion >= Build.VERSION_CODES.M) {
+ return new BleInitializerM(activity, initCompletionCallback);
+ } else if (osVersion >= Build.VERSION_CODES.LOLLIPOP) {
+ return new BleInitializerL(activity, initCompletionCallback);
+ } else if (osVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ return new BleInitializerJ(activity, initCompletionCallback);
+ }
+
+// There is no BLE support in Android versions below Jelly Bean
+ return null;
+ }
+
+ public static BleScanner getScanner(BleScanner.ScanCallback scanCallback, BluetoothAdapter bluetoothAdapter) {
+ final int osVersion = Build.VERSION.SDK_INT;
+
+ if (osVersion >= Build.VERSION_CODES.LOLLIPOP) {
+ return new BleScannerL(scanCallback, bluetoothAdapter);
+ } else if (osVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ return new BleScannerJ(scanCallback, bluetoothAdapter);
+ }
+
+// There is no BLE support in Android versions below Jelly Bean
+ return null;
+ }
+}
diff --git a/app/src/main/java/com/dataart/btle_android/helpers/LocationHelper.java b/app/src/main/java/com/dataart/btle_android/helpers/LocationHelper.java
new file mode 100644
index 0000000..11f6309
--- /dev/null
+++ b/app/src/main/java/com/dataart/btle_android/helpers/LocationHelper.java
@@ -0,0 +1,209 @@
+package com.dataart.btle_android.helpers;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.location.Location;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.dataart.btle_android.R;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GooglePlayServicesUtil;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.PendingResult;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.common.api.Status;
+import com.google.android.gms.location.LocationListener;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationServices;
+import com.google.android.gms.location.LocationSettingsRequest;
+import com.google.android.gms.location.LocationSettingsResult;
+import com.google.android.gms.location.LocationSettingsStatusCodes;
+
+import timber.log.Timber;
+
+/**
+ * Created by Constantine Mars on 12/6/15.
+ * Android M with Google Play Services requires Location enabled before starting BLE devices discovery
+ * This helper implements turning on Location services programmatically
+ */
+public class LocationHelper implements ResultCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
+ private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
+ private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
+ UPDATE_INTERVAL_IN_MILLISECONDS / 2;
+ private static final int REQUEST_CHECK_SETTINGS = 100;
+ private LocationSettingsRequest mLocationSettingsRequest;
+ private GoogleApiClient mGoogleApiClient;
+ private LocationRequest mLocationRequest;
+ private LocationEnabledListener listener;
+ private Activity activity;
+ private boolean waitForResume = false;
+
+ public LocationHelper(LocationEnabledListener listener, Activity activity) {
+ this.listener = listener;
+ this.activity = activity;
+ buildGoogleApiClient();
+ createLocationRequest();
+ buildLocationSettingsRequest();
+ }
+
+ public void onStart() {
+ mGoogleApiClient.connect();
+ }
+
+ public void onStop() {
+ mGoogleApiClient.disconnect();
+ }
+
+ /**
+ * Build request to enable Location
+ */
+ private void buildLocationSettingsRequest() {
+ LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
+ builder.addLocationRequest(mLocationRequest);
+ mLocationSettingsRequest = builder.build();
+ }
+
+ private void buildGoogleApiClient() {
+ mGoogleApiClient = new GoogleApiClient.Builder(activity)
+ .addConnectionCallbacks(this)
+ .addOnConnectionFailedListener(this)
+ .addApi(LocationServices.API)
+ .build();
+ }
+
+ /**
+ * Location request is necessary for defining which exactly permissions must be enabled
+ */
+ protected void createLocationRequest() {
+ mLocationRequest = new LocationRequest();
+ mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
+ mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
+ mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
+ }
+
+ private void doJob() {
+ listener.onLocationEnabled();
+ }
+
+ @Override
+ public void onResult(@NonNull LocationSettingsResult locationSettingsResult) {
+ final Status status = locationSettingsResult.getStatus();
+ switch (status.getStatusCode()) {
+ case LocationSettingsStatusCodes.SUCCESS:
+ Timber.i(activity.getString(R.string.location_settings_ok));
+ doJob();
+ break;
+ case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
+ Timber.e(activity.getString(R.string.location_fail));
+
+ try {
+ // Show the dialog by calling startResolutionForResult(), and check the result
+ // in onActivityResult().
+ status.startResolutionForResult(activity, REQUEST_CHECK_SETTINGS);
+ } catch (IntentSender.SendIntentException e) {
+ Timber.e(activity.getString(R.string.pi_fail));
+ }
+ break;
+ case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
+ Timber.e(activity.getString(R.string.loc_settings_inadequate));
+ break;
+ }
+ }
+
+ @Override
+ public void onConnected(@Nullable Bundle bundle) {
+
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+
+ }
+
+ @Override
+ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
+
+ }
+
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ // Check for the integer request code originally supplied to startResolutionForResult().
+ case REQUEST_CHECK_SETTINGS:
+ switch (resultCode) {
+ case Activity.RESULT_OK:
+ Timber.i(activity.getString(R.string.location_ok));
+ doJob();
+ break;
+ case Activity.RESULT_CANCELED:
+ Timber.e(activity.getString(R.string.location_cancelled));
+ break;
+ }
+ break;
+ }
+ }
+
+ private boolean isLocationEnabled() {
+ LocationManager lm = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
+ boolean gps_enabled = false;
+ boolean network_enabled = false;
+
+ try {
+ gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
+ } catch (Exception ignored) {
+ }
+
+ try {
+ network_enabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
+ } catch (Exception ignored) {
+ }
+
+ return gps_enabled || network_enabled;
+ }
+
+ public void checkLocationEnabled() {
+ if (isLocationEnabled()) {
+ doJob();
+ return;
+ }
+
+// If location isn't enabled - check whether we can call Google Play Services or user to manually
+// switch Location
+ final int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(activity.getApplicationContext());
+ if (status == ConnectionResult.SUCCESS) {
+ PendingResult result =
+ LocationServices.SettingsApi.checkLocationSettings(
+ mGoogleApiClient,
+ mLocationSettingsRequest
+ );
+ result.setResultCallback(this);
+ } else {
+// If no services available - the only thing we can do is to
+// ask user to switch Location manually
+ activity.startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
+ waitForResume = true;
+ }
+ }
+
+ public void onResume() {
+ if (waitForResume) {
+ checkLocationEnabled();
+ }
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+
+ }
+
+ public interface LocationEnabledListener {
+ void onLocationEnabled();
+ }
+
+}
+
diff --git a/app/src/main/java/com/dataart/btle_android/helpers/PermissionsHelper.java b/app/src/main/java/com/dataart/btle_android/helpers/PermissionsHelper.java
new file mode 100644
index 0000000..f64feb8
--- /dev/null
+++ b/app/src/main/java/com/dataart/btle_android/helpers/PermissionsHelper.java
@@ -0,0 +1,66 @@
+package com.dataart.btle_android.helpers;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.pm.PackageManager;
+import android.os.Build;
+
+import com.dataart.btle_android.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import rx.functions.Action1;
+
+/**
+ * Created by Constantine Mars on 12/15/15.
+ */
+@TargetApi(Build.VERSION_CODES.M)
+public class PermissionsHelper {
+ private static final int PERMISSION_REQUEST = 2001;
+
+ public static boolean checkPermissions(Activity activity, String[] permissions, String message) {
+ List permissionsToCheck = new ArrayList<>();
+ rx.Observable.from(permissions)
+ .filter(permission -> activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED)
+ .forEach(permissionsToCheck::add);
+
+ if (permissionsToCheck.isEmpty()) {
+ return true;
+ }
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setTitle(R.string.needs_permissions);
+ builder.setMessage(message);
+ builder.setPositiveButton(android.R.string.ok, null);
+ builder.setOnDismissListener(dialog -> activity.requestPermissions(
+ permissionsToCheck.toArray(
+ new String[permissionsToCheck.size()]
+ ), PERMISSION_REQUEST));
+ builder.show();
+
+ return false;
+ }
+
+ public static void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults,
+ Action1 onSuccess, Action1 onError) {
+ switch (requestCode) {
+ case PERMISSION_REQUEST: {
+ List permissionsNotGranted = new ArrayList<>();
+
+ for (String permission : permissions) {
+ if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+ permissionsNotGranted.add(permission);
+ }
+ }
+
+ if (permissionsNotGranted.size() > 0) {
+ onError.call(permissionsNotGranted.toArray(new String[permissionsNotGranted.size()]));
+ } else {
+ onSuccess.call("All required permissions granted");
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/dataart/btle_android/helpers/ble/base/BleInitializer.java b/app/src/main/java/com/dataart/btle_android/helpers/ble/base/BleInitializer.java
new file mode 100644
index 0000000..3564555
--- /dev/null
+++ b/app/src/main/java/com/dataart/btle_android/helpers/ble/base/BleInitializer.java
@@ -0,0 +1,97 @@
+package com.dataart.btle_android.helpers.ble.base;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
+import android.content.Intent;
+
+import com.dataart.btle_android.R;
+
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+import timber.log.Timber;
+
+/**
+ * Created by Constantine Mars on 12/13/15.
+ *
+ * Base BLE init helper logic, common for all Android versions, starting with JellyBean MR2
+ */
+@Data
+@RequiredArgsConstructor
+public abstract class BleInitializer {
+ private static final int REQUEST_ENABLE_BT = 3001;
+
+ protected final Activity activity;
+ protected final InitCompletionCallback initCompletionCallback;
+ protected BluetoothAdapter bluetoothAdapter;
+ /**
+ * Enabling Bluetooth should be always the last step of initialization,
+ * after which initCompletionCallback will be called
+ */
+ private boolean declined = false;
+
+ /**
+ * Init delivers BluetoothAdapter for all platform-specific versions of initializer
+ */
+ protected void init() {
+ final BluetoothManager bluetoothManager = (BluetoothManager) activity.getSystemService(Context.BLUETOOTH_SERVICE);
+ bluetoothAdapter = bluetoothManager.getAdapter();
+ }
+
+ /**
+ * Start must be called before beginning scanning or any other opeartions on bluetooth
+ */
+ public abstract void start();
+
+ public boolean enableBluetooth() {
+ if (bluetoothAdapter == null) {
+ Timber.e(activity.getString(R.string.bt_not_supported));
+ return false;
+ }
+
+ if (!bluetoothAdapter.isEnabled() && !declined) {
+ Timber.i(activity.getString(R.string.bt_enabling_started));
+
+ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+ activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
+ return false;
+ }
+
+ Timber.i(activity.getString(R.string.bt_enabled));
+ initCompletionCallback.onInitCompleted(bluetoothAdapter);
+ return true;
+ }
+
+ /**
+ * Need to be called from Activity onActivityResult
+ */
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_ENABLE_BT:
+ if (resultCode == Activity.RESULT_OK) {
+ start();
+ } else {
+ declined = false;
+ }
+ break;
+ }
+ }
+
+ public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
+ }
+
+ public void onStart() {
+ }
+
+ public void onStop() {
+ }
+
+ public void onResume() {
+ }
+
+
+ public interface InitCompletionCallback {
+ void onInitCompleted(BluetoothAdapter bluetoothAdapter);
+ }
+}
diff --git a/app/src/main/java/com/dataart/btle_android/helpers/ble/base/BleScanner.java b/app/src/main/java/com/dataart/btle_android/helpers/ble/base/BleScanner.java
new file mode 100644
index 0000000..a5a1963
--- /dev/null
+++ b/app/src/main/java/com/dataart/btle_android/helpers/ble/base/BleScanner.java
@@ -0,0 +1,27 @@
+package com.dataart.btle_android.helpers.ble.base;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Created by Constantine Mars on 6/13/16.
+ * Helper for BLE scanner. Can be singleton and accessible from android services
+ */
+
+@Data
+@RequiredArgsConstructor
+public abstract class BleScanner {
+ protected final ScanCallback scanCallback;
+ protected final BluetoothAdapter bluetoothAdapter;
+
+ public abstract void startScan();
+
+ public abstract void stopScan();
+
+ public interface ScanCallback {
+ void onDeviceFound(BluetoothDevice device, int rssi, byte[] scanRecord);
+ }
+}
diff --git a/app/src/main/java/com/dataart/btle_android/helpers/ble/initializers/BleInitializerJ.java b/app/src/main/java/com/dataart/btle_android/helpers/ble/initializers/BleInitializerJ.java
new file mode 100644
index 0000000..87c66a4
--- /dev/null
+++ b/app/src/main/java/com/dataart/btle_android/helpers/ble/initializers/BleInitializerJ.java
@@ -0,0 +1,30 @@
+package com.dataart.btle_android.helpers.ble.initializers;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.os.Build;
+
+import com.dataart.btle_android.helpers.ble.base.BleInitializer;
+
+import lombok.Data;
+
+/**
+ * Created by Constantine Mars on 12/13/15.
+ *
+ * Jelly Bean MR2 - specific BLE helper
+ */
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+@Data
+public class BleInitializerJ extends BleInitializer {
+
+ public BleInitializerJ(Activity activity, InitCompletionCallback initCompletionCallback) {
+ super(activity, initCompletionCallback);
+
+ init();
+ }
+
+ @Override
+ public void start() {
+ enableBluetooth();
+ }
+}
diff --git a/app/src/main/java/com/dataart/btle_android/helpers/ble/initializers/BleInitializerL.java b/app/src/main/java/com/dataart/btle_android/helpers/ble/initializers/BleInitializerL.java
new file mode 100644
index 0000000..46a2186
--- /dev/null
+++ b/app/src/main/java/com/dataart/btle_android/helpers/ble/initializers/BleInitializerL.java
@@ -0,0 +1,22 @@
+package com.dataart.btle_android.helpers.ble.initializers;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.os.Build;
+
+import lombok.Data;
+
+/**
+ * Created by Constantine Mars on 12/6/15.
+ *
+ * Lollipop-specific BLE Helper
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+@Data
+public class BleInitializerL extends BleInitializerJ {
+
+ public BleInitializerL(Activity activity, InitCompletionCallback initCompletionCallback) {
+ super(activity, initCompletionCallback);
+ }
+
+}
diff --git a/app/src/main/java/com/dataart/btle_android/helpers/ble/initializers/BleInitializerM.java b/app/src/main/java/com/dataart/btle_android/helpers/ble/initializers/BleInitializerM.java
new file mode 100644
index 0000000..62ee440
--- /dev/null
+++ b/app/src/main/java/com/dataart/btle_android/helpers/ble/initializers/BleInitializerM.java
@@ -0,0 +1,116 @@
+package com.dataart.btle_android.helpers.ble.initializers;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.os.Build;
+
+import com.dataart.btle_android.R;
+import com.dataart.btle_android.helpers.LocationHelper;
+import com.dataart.btle_android.helpers.PermissionsHelper;
+import com.dataart.btle_android.helpers.ble.base.BleInitializer;
+
+import rx.functions.Action1;
+import timber.log.Timber;
+
+/**
+ * Created by Constantine Mars on 12/13/15.
+ *
+ * Marshmallow-specific BLE helper
+ */
+public class BleInitializerM extends BleInitializerL {
+ private static String[] permissions = new String[]{
+ Manifest.permission.BLUETOOTH,
+ Manifest.permission.BLUETOOTH_ADMIN,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ };
+ private Action1 onError;
+ private Action1 onSuccess;
+ private LocationHelper locationHelper;
+
+ public BleInitializerM(Activity activity, BleInitializer.InitCompletionCallback initCompletionCallback) {
+ super(activity, initCompletionCallback);
+ locationHelper = new LocationHelper(this::callSuperStart, activity);
+
+ onError = permissions -> {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setTitle(R.string.no_permissions_title);
+ builder.setMessage(R.string.no_permissions_message);
+ builder.setPositiveButton(android.R.string.ok, null);
+ builder.setOnDismissListener(null);
+ builder.show();
+ };
+ onSuccess = s -> {
+ enableLocation();
+ Timber.i(s);
+ };
+ }
+
+ /**
+ * 1. Request permissions at runtime (Android M specific)
+ */
+ @Override
+ public void start() {
+ requestPermissions();
+ }
+
+ /**
+ * 2. Enable location (switch location on if it's still not on)
+ */
+ private void enableLocation() {
+ locationHelper.checkLocationEnabled();
+ }
+
+ /**
+ * 3. Call super.start() to complete sequence (enable bluetooth and call initCompletionCallback.onComplete())
+ */
+ private void callSuperStart() {
+ super.start();
+ }
+
+ public void requestPermissions() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (PermissionsHelper.checkPermissions(
+ activity,
+ permissions,
+ activity.getString(R.string.permissions_explanation))) {
+ enableLocation();
+ }
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
+ PermissionsHelper.onRequestPermissionsResult(
+ requestCode,
+ permissions,
+ grantResults,
+ onSuccess,
+ onError);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ locationHelper.onStart();
+ }
+
+ @Override
+ public void onStop() {
+ locationHelper.onStop();
+ super.onStop();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ locationHelper.onResume();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ locationHelper.onActivityResult(requestCode, resultCode, data);
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+}
diff --git a/app/src/main/java/com/dataart/btle_android/helpers/ble/scanners/BleScannerJ.java b/app/src/main/java/com/dataart/btle_android/helpers/ble/scanners/BleScannerJ.java
new file mode 100644
index 0000000..d775996
--- /dev/null
+++ b/app/src/main/java/com/dataart/btle_android/helpers/ble/scanners/BleScannerJ.java
@@ -0,0 +1,32 @@
+package com.dataart.btle_android.helpers.ble.scanners;
+
+import android.bluetooth.BluetoothAdapter;
+
+import com.dataart.btle_android.helpers.ble.base.BleScanner;
+
+
+/**
+ * Created by Constantine Mars on 6/13/16.
+ * Scanner for Android Jelly Bean
+ */
+
+@SuppressWarnings("deprecation")
+public class BleScannerJ extends BleScanner {
+
+ private final BluetoothAdapter.LeScanCallback callback;
+
+ public BleScannerJ(ScanCallback scanCallback, BluetoothAdapter bluetoothAdapter) {
+ super(scanCallback, bluetoothAdapter);
+ callback = scanCallback::onDeviceFound;
+ }
+
+ @Override
+ public void startScan() {
+ bluetoothAdapter.startLeScan(callback);
+ }
+
+ @Override
+ public void stopScan() {
+ bluetoothAdapter.stopLeScan(callback);
+ }
+}
diff --git a/app/src/main/java/com/dataart/btle_android/helpers/ble/scanners/BleScannerL.java b/app/src/main/java/com/dataart/btle_android/helpers/ble/scanners/BleScannerL.java
new file mode 100644
index 0000000..514a312
--- /dev/null
+++ b/app/src/main/java/com/dataart/btle_android/helpers/ble/scanners/BleScannerL.java
@@ -0,0 +1,74 @@
+package com.dataart.btle_android.helpers.ble.scanners;
+
+import android.annotation.TargetApi;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanResult;
+import android.os.Build;
+
+import java.util.List;
+
+/**
+ * Created by Constantine Mars on 6/13/16.
+ *
+ * Scanner for Android L and above
+ */
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class BleScannerL extends BleScannerJ {
+
+ private BluetoothLeScanner scanner;
+
+ private android.bluetooth.le.ScanCallback callback;
+
+ public BleScannerL(ScanCallback scanCallback, BluetoothAdapter bluetoothAdapter) {
+ super(scanCallback, bluetoothAdapter);
+
+ callback = createCallback(scanCallback);
+ }
+
+ private static android.bluetooth.le.ScanCallback createCallback(ScanCallback scanCallback) {
+ return new android.bluetooth.le.ScanCallback() {
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+
+ BluetoothDevice device = result.getDevice();
+ int rssi = result.getRssi();
+ byte[] scanRecord = (result.getScanRecord() != null ? result.getScanRecord().getBytes() : null);
+ scanCallback.onDeviceFound(device, rssi, scanRecord);
+
+ super.onScanResult(callbackType, result);
+ }
+
+ @Override
+ public void onBatchScanResults(List results) {
+ super.onBatchScanResults(results);
+ }
+
+ @Override
+ public void onScanFailed(int errorCode) {
+ super.onScanFailed(errorCode);
+ }
+ };
+ }
+
+ private BluetoothLeScanner getScanner() {
+ if (scanner == null && bluetoothAdapter != null) {
+ scanner = bluetoothAdapter.getBluetoothLeScanner();
+ }
+ return scanner;
+ }
+
+ @Override
+ public void startScan() {
+ getScanner().startScan(callback);
+ }
+
+ @Override
+ public void stopScan() {
+ getScanner().stopScan(callback);
+ }
+
+
+}
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index 7a1806b..a65cbad 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -1,106 +1,134 @@
-
+ android:layout_height="match_parent">
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+ android:layout_height="match_parent"
+ android:layout_below="@+id/toolbar">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+ android:orientation="vertical">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
index 821d7c0..8f851fd 100644
--- a/app/src/main/res/values-v21/styles.xml
+++ b/app/src/main/res/values-v21/styles.xml
@@ -1,5 +1,8 @@
-
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..e497ead
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,5 @@
+
+
+ #000
+ #444
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 074e7a0..34be602 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -6,4 +6,6 @@
240dp
+ 10dp
+ 4dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 76b733c..a942254 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -16,11 +16,12 @@
Start service
Stop service
+ http://playground.devicehive.com/api/rest
devicehive_ble_andoid
8Gff/CCqkLYkuYHwa2oXf2GCmpyaLsuOlVYkttL93LQ=
- Bluetooth LE is not supported
- Bluetooth not supported
+ Bluetooth LE is not supported.\nApplication will close itself
+ Bluetooth not supported.\nApplication will close itself
Server URL is required
Gateway ID is required
Username is required
@@ -36,13 +37,12 @@
Unknown device
Bluetooth is off. Please turn on Bluetooth to continue working with DeviceHive BLE Gateway
- BLE Service is working...
+ BLE Service is working…
success
fail
not found
- ok
fail
waiting for completion
write not permitted
@@ -66,9 +66,39 @@
unknown command
service uuid not found
characteristic uuid not found
-
invalid args. device address required
invalid args. service UUID required
invalid args. characteristic UUID required
invalid args. write operation requires value
+
+ All location settings are satisfied
+ Location settings are not satisfied. Show the user a dialog to upgrade location settings
+ PendingIntent unable to execute request
+ Location settings are inadequate, and cannot be fixed here. Dialog not created
+ User agreed to make required location settings changes
+ User chose not to make required location settings changes
+ Unable to initialize BluetoothManager
+ Unable to obtain a BluetoothAdapter
+
+ Unsupported SDK version
+ Bluetooth LE is supported starting with SDK 18 (Android Jelly Bean MR2).\nPlease, update your Android version or try on another device
+
+ Bluetooth enabled
+ Bluetooth is disabled. Enabling…
+ Bluetooth is not supported
+ Allow permissions
+ Functionality limited
+ Because location access has not been granted, application is not able to discover BLE devices
+ DeviceHive BLE Gateway needs to turn on Bluetooth and Location (since Android M) for discovering BLE devices
+ Location settings are satisfied.
+ Location settings are not satisfied. You need upgrade location settings.
+ PendingIntent unable to execute request.
+ Location settings failed due to unknown error
+ Location settings accepted
+ Location setting declined
+ %1$s requires address of device to be specified. For example: {\"device\":\"A4:F9:4C:3B:1C:F8\"}
+ %1$s requires service UUID to be specified. For example: {\"serviceUUID\":\"aa00\"}
+ %1$s requires characteristic UUID to be specified. For example: {\"characteristicUUID\":\"aa01\"}
+ %1$s requires value to be specified
+ Unsupported device
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index c83c7e1..6f4afe3 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,27 +1,31 @@
-
+
+
-