aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/io/trygvis/android
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/io/trygvis/android')
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtActionExecutor.java366
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtActivitySupport.java57
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtCallback.java53
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtDevice.java68
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtDeviceListener.java4
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtScanResult.java9
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtService.java55
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtUtils.java15
-rw-r--r--app/src/main/java/io/trygvis/android/bt/DefaultBtService.java192
-rw-r--r--app/src/main/java/io/trygvis/android/bt/NotOverriddenException.java4
10 files changed, 823 insertions, 0 deletions
diff --git a/app/src/main/java/io/trygvis/android/bt/BtActionExecutor.java b/app/src/main/java/io/trygvis/android/bt/BtActionExecutor.java
new file mode 100644
index 0000000..8cbf87c
--- /dev/null
+++ b/app/src/main/java/io/trygvis/android/bt/BtActionExecutor.java
@@ -0,0 +1,366 @@
+package io.trygvis.android.bt;
+
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.util.Log;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Queue;
+
+import static io.trygvis.android.bt.BtActionExecutor.EventType.onCharacteristicChanged;
+import static io.trygvis.android.bt.BtActionExecutor.EventType.onCharacteristicRead;
+import static io.trygvis.android.bt.BtActionExecutor.EventType.onCharacteristicWrite;
+import static io.trygvis.android.bt.BtActionExecutor.EventType.onConnectionStateChange;
+import static io.trygvis.android.bt.BtActionExecutor.EventType.onDescriptorRead;
+import static io.trygvis.android.bt.BtActionExecutor.EventType.onDescriptorWrite;
+import static io.trygvis.android.bt.BtActionExecutor.EventType.onFailure;
+import static io.trygvis.android.bt.BtActionExecutor.EventType.onReliableWriteCompleted;
+import static io.trygvis.android.bt.BtActionExecutor.EventType.onServicesDiscovered;
+
+public class BtActionExecutor {
+ private final static String TAG = BtActionExecutor.class.getSimpleName();
+ private final Queue<BtCallback> actionQ = new ArrayDeque<>();
+ private final Queue<BtCallback> finallyQ = new ArrayDeque<>();
+ private final List<BluetoothGattCallback> remoteRssi = new ArrayList<>();
+
+ public BtActionExecutor() {
+ }
+
+ public BtActionExecutor(BtCallback first) {
+ actionQ.add(first);
+ }
+
+// public static final BtCallback waitForIt = new BtCallback("Wait for it") {
+// @Override
+// public boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+// Log.w(TAG, "wait for it...!");
+// return true;
+// }
+// };
+
+// public synchronized BtActionExecutor onSuccess(BtCallback btCallback) {
+// actionQ.add(btCallback);
+// return this;
+// }
+
+ public synchronized BtActionExecutor onConnectionStateChange(OnConnectionStateChange callback) {
+ actionQ.add(new BtCallback("onConnectionStateChange") {
+ @Override
+ public boolean onConnectionStateChange(BluetoothGatt gatt, int newState) {
+ return callback.onConnectionStateChange(gatt, newState);
+ }
+ });
+ return this;
+ }
+
+ public synchronized BtActionExecutor onServicesDiscovered(OnServicesDiscovered callback) {
+ actionQ.add(new BtCallback("onServicesDiscovered") {
+ @Override
+ public boolean onServicesDiscovered(BluetoothGatt gatt) {
+ return callback.onServicesDiscovered(gatt);
+ }
+ });
+ return this;
+ }
+
+ public synchronized BtActionExecutor onCharacteristicRead(OnCharacteristicRead callback) {
+ actionQ.add(new BtCallback("onCharacteristicRead") {
+ @Override
+ public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ return callback.onCharacteristicRead(gatt, characteristic);
+ }
+ });
+ return this;
+ }
+
+ public synchronized BtActionExecutor onCharacteristicWrite(OnCharacteristicWrite callback) {
+ actionQ.add(new BtCallback("onCharacteristicWrite") {
+ @Override
+ public boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ return callback.onCharacteristicWrite(gatt, characteristic);
+ }
+ });
+ return this;
+ }
+
+ public synchronized BtActionExecutor onCharacteristicChanged(OnCharacteristicChanged callback) {
+ actionQ.add(new BtCallback("onCharacteristicChanged") {
+ @Override
+ public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ return callback.onCharacteristicChanged(gatt, characteristic);
+ }
+ });
+ return this;
+ }
+
+ public synchronized BtActionExecutor onDescriptorRead(OnDescriptorRead callback) {
+ actionQ.add(new BtCallback("onDescriptorRead") {
+ @Override
+ public boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
+ return callback.onDescriptorRead(gatt, descriptor);
+ }
+ });
+ return this;
+ }
+
+ public synchronized BtActionExecutor onDescriptorWrite(OnDescriptorWrite callback) {
+ actionQ.add(new BtCallback("onDescriptorWrite") {
+ @Override
+ public boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
+ return callback.onDescriptorWrite(gatt, descriptor);
+ }
+ });
+ return this;
+ }
+
+ public synchronized BtActionExecutor onReliableWriteCompleted(OnReliableWriteCompleted callback) {
+ actionQ.add(new BtCallback("onReliableWriteCompleted") {
+ @Override
+ public boolean onReliableWriteCompleted(BluetoothGatt gatt) {
+ return callback.onReliableWriteCompleted(gatt);
+ }
+ });
+ return this;
+ }
+
+ public synchronized BtActionExecutor onReadRemoteRssi(OnReadRemoteRssi callback) {
+ actionQ.add(new BtCallback("onReadRemoteRssi") {
+ @Override
+ public boolean onReadRemoteRssi(BluetoothGatt gatt, int rssi) {
+ return callback.onReadRemoteRssi(gatt, rssi);
+ }
+ });
+ return this;
+ }
+
+ public synchronized BtActionExecutor onFailure(OnFailure callback) {
+ actionQ.add(new BtCallback("onFailure") {
+ @Override
+ public void onFailure() {
+ callback.onFailure();
+ }
+ });
+ return this;
+ }
+
+ public static interface OnConnectionStateChange {
+ boolean onConnectionStateChange(BluetoothGatt gatt, int newState);
+ }
+
+ public static interface OnServicesDiscovered {
+ boolean onServicesDiscovered(BluetoothGatt gatt);
+ }
+
+ public static interface OnCharacteristicRead {
+ boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
+ }
+
+ public static interface OnCharacteristicWrite {
+ boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
+ }
+
+ public static interface OnCharacteristicChanged {
+ boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
+ }
+
+ public static interface OnDescriptorRead {
+ boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor);
+ }
+
+ public static interface OnDescriptorWrite {
+ boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor);
+ }
+
+ public static interface OnReliableWriteCompleted {
+ boolean onReliableWriteCompleted(BluetoothGatt gatt);
+ }
+
+ public static interface OnReadRemoteRssi {
+ boolean onReadRemoteRssi(BluetoothGatt gatt, int rssi);
+ }
+
+ public static interface OnFailure {
+ void onFailure();
+ }
+
+ public synchronized BtActionExecutor addFinally(BtCallback btCallback) {
+ finallyQ.add(btCallback);
+ return this;
+ }
+
+// public synchronized BtActionExecutor onRemoteRssi(BluetoothGattCallback callback) {
+// remoteRssi.add(callback);
+// return this;
+// }
+
+ void onEvent(EventType key, String values, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, int status, int newState) {
+ if (status != BluetoothGatt.GATT_SUCCESS) {
+ Log.w(TAG, "Operation failed: " + key + ", " + values);
+ doFinally();
+ return;
+ }
+
+ Log.i(TAG, "Bt action completed successfully: callback=" + key);
+
+ BtCallback btCallback;
+ synchronized (this) {
+ if (actionQ.isEmpty()) {
+ Log.d(TAG, "All Bluetooth actions are done");
+
+ doFinally();
+ return;
+ }
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ btCallback = actionQ.remove();
+ Log.i(TAG, "Executing bt action: " + btCallback.name);
+ }
+
+ try {
+ boolean ok = callCallback(key, gatt, characteristic, descriptor, newState, btCallback);
+
+ if (!ok) {
+ Log.w(TAG, "The callback don't want to continue.");
+ doFinally();
+ }
+
+ if (actionQ.isEmpty()) {
+ Log.i(TAG, "The queue is empty");
+ }
+ } catch (NotOverriddenException e) {
+ Log.w(TAG, "Unexpected callback by listener: " + key);
+ doFinally();
+ }
+ }
+
+ private void doFinally() {
+ actionQ.clear();
+
+ for (BtCallback callback = finallyQ.poll(); callback != null; callback = finallyQ.poll()) {
+ try {
+ callCallback(onFailure, null, null, null, 0, callback);
+ } catch (NotOverriddenException e) {
+ return;
+ }
+ }
+ }
+
+ enum EventType {
+ onConnectionStateChange,
+ onServicesDiscovered,
+ onCharacteristicRead,
+ onCharacteristicWrite,
+ onCharacteristicChanged,
+ onDescriptorRead,
+ onDescriptorWrite,
+ onReliableWriteCompleted,
+
+ onFailure,
+ }
+
+ private Boolean callCallback(EventType key, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, int newState, BtCallback btCallback) {
+ switch (key) {
+ case onConnectionStateChange:
+ return btCallback.onConnectionStateChange(gatt, newState);
+ case onServicesDiscovered:
+ return btCallback.onServicesDiscovered(gatt);
+ case onCharacteristicRead:
+ return btCallback.onCharacteristicRead(gatt, characteristic);
+ case onCharacteristicWrite:
+ return btCallback.onCharacteristicWrite(gatt, characteristic);
+ case onCharacteristicChanged:
+ return btCallback.onCharacteristicChanged(gatt, characteristic);
+ case onDescriptorRead:
+ return btCallback.onDescriptorRead(gatt, descriptor);
+ case onDescriptorWrite:
+ return btCallback.onDescriptorWrite(gatt, descriptor);
+ case onReliableWriteCompleted:
+ return btCallback.onReliableWriteCompleted(gatt);
+
+ case onFailure:
+ btCallback.onFailure();
+ return null;
+ default:
+ Log.w(TAG, "Unknown callback: " + key);
+ return null;
+ }
+ }
+
+ public String toString() {
+ StringBuilder s = new StringBuilder("Queue: ");
+
+ Iterator<BtCallback> it = actionQ.iterator();
+
+ int i = 0;
+ while (it.hasNext()) {
+ BtCallback c = it.next();
+ if (i > 0) {
+ s.append(", ");
+ }
+ s.append(c.name);
+ i++;
+ }
+
+ return s.toString();
+ }
+
+ public BluetoothGattCallback asCallback() {
+ return new BluetoothGattCallback() {
+ @Override
+ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+ onEvent(onConnectionStateChange, "status=" + status + ", newState=" + newState, gatt, null, null, status, newState);
+ }
+
+ @Override
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ onEvent(onServicesDiscovered, "status=" + status, gatt, null, null, status, 9);
+ }
+
+ @Override
+ public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ onEvent(onCharacteristicRead, "status=" + status + ", characteristic=" + characteristic.getUuid(), gatt, characteristic, null, status, 0);
+ }
+
+ @Override
+ public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ onEvent(onCharacteristicWrite, "status=" + status + ", characteristic=" + characteristic.getUuid(), gatt, characteristic, null, status, 0);
+ }
+
+ @Override
+ public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ onEvent(onCharacteristicChanged, "characteristic=" + characteristic.getUuid(), gatt, characteristic, null, BluetoothGatt.GATT_SUCCESS, 0);
+ }
+
+ @Override
+ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ onEvent(onDescriptorRead, "status=" + status + ", descriptor=" + descriptor.getUuid(), gatt, null, descriptor, status, 0);
+ }
+
+ @Override
+ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ onEvent(onDescriptorWrite, "status=" + status + ", descriptor=" + descriptor.getUuid(), gatt, null, descriptor, status, 0);
+ }
+
+ @Override
+ public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
+ onEvent(onReliableWriteCompleted, "status=" + status, gatt, null, null, status, 0);
+ }
+
+ @Override
+ public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+ for (BluetoothGattCallback callback : remoteRssi) {
+ callback.onReadRemoteRssi(gatt, rssi, status);
+ }
+ }
+ };
+ }
+}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtActivitySupport.java b/app/src/main/java/io/trygvis/android/bt/BtActivitySupport.java
new file mode 100644
index 0000000..03a36fb
--- /dev/null
+++ b/app/src/main/java/io/trygvis/android/bt/BtActivitySupport.java
@@ -0,0 +1,57 @@
+package io.trygvis.android.bt;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.Toast;
+
+import io.trygvis.soilmoisture.R;
+
+public class BtActivitySupport {
+
+ private final Activity activity;
+ private final int requestCode;
+ private BluetoothAdapter mBluetoothAdapter;
+
+ public BtActivitySupport(Activity activity, int requestCode) {
+ this.activity = activity;
+ this.requestCode = requestCode;
+ }
+
+ public boolean onCreate() {
+ final BluetoothManager bluetoothManager = (BluetoothManager) activity.getSystemService(Context.BLUETOOTH_SERVICE);
+ mBluetoothAdapter = bluetoothManager.getAdapter();
+
+ // Checks if Bluetooth is supported on the device.
+ if (mBluetoothAdapter == null) {
+ Toast.makeText(activity, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).
+ show();
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean enableBt() {
+ // Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
+ // fire an intent to display a dialog asking the user to grant permission to enable it.
+ if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
+ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+ activity.startActivityForResult(enableBtIntent, requestCode);
+ }
+
+ return true;
+ }
+
+ @SuppressWarnings({"RedundantIfStatement", "UnusedParameters"})
+ public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+ // User chose not to enable Bluetooth.
+ if (requestCode == this.requestCode && resultCode == Activity.RESULT_CANCELED) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtCallback.java b/app/src/main/java/io/trygvis/android/bt/BtCallback.java
new file mode 100644
index 0000000..7702962
--- /dev/null
+++ b/app/src/main/java/io/trygvis/android/bt/BtCallback.java
@@ -0,0 +1,53 @@
+package io.trygvis.android.bt;
+
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+
+public class BtCallback {
+ public final String name;
+
+ public BtCallback(String name) {
+ this.name = name;
+ }
+
+ public boolean onConnectionStateChange(BluetoothGatt gatt, int newState) {
+ throw new NotOverriddenException();
+ }
+
+ public boolean onServicesDiscovered(BluetoothGatt gatt) {
+ throw new NotOverriddenException();
+ }
+
+ public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ throw new NotOverriddenException();
+ }
+
+ public boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ throw new NotOverriddenException();
+ }
+
+ public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ throw new NotOverriddenException();
+ }
+
+ public boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
+ throw new NotOverriddenException();
+ }
+
+ public boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
+ throw new NotOverriddenException();
+ }
+
+ public boolean onReliableWriteCompleted(BluetoothGatt gatt) {
+ throw new NotOverriddenException();
+ }
+
+ public boolean onReadRemoteRssi(BluetoothGatt gatt, int rssi) {
+ throw new NotOverriddenException();
+ }
+
+ public void onFailure() {
+ throw new NotOverriddenException();
+ }
+}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtDevice.java b/app/src/main/java/io/trygvis/android/bt/BtDevice.java
new file mode 100644
index 0000000..ba10d2d
--- /dev/null
+++ b/app/src/main/java/io/trygvis/android/bt/BtDevice.java
@@ -0,0 +1,68 @@
+package io.trygvis.android.bt;
+
+import android.bluetooth.BluetoothDevice;
+import android.util.Log;
+
+public class BtDevice<A> {
+ private final static String TAG = BtDevice.class.getSimpleName();
+
+ private final DefaultBtService btService;
+ private final BluetoothDevice bluetoothDevice;
+ private Integer rssi;
+ private BtScanResult scanResult;
+ private A tag;
+
+ private boolean seenNow;
+
+ private BtDeviceListener l = null;
+
+ private BtDeviceListener listener = new BtDeviceListener() {
+ };
+
+ public BtDevice(DefaultBtService btService, BluetoothDevice bluetoothDevice, A tag, Integer rssi, BtScanResult scanResult) {
+ this.btService = btService;
+ this.bluetoothDevice = bluetoothDevice;
+ this.tag = tag;
+ this.rssi = rssi;
+ this.scanResult = scanResult;
+ }
+
+ public void addListener(BtDeviceListener listener) {
+ this.l = listener;
+ }
+
+ public A getTag() {
+ return tag;
+ }
+
+ public void setTag(A tag) {
+ this.tag = tag;
+ }
+
+ public String getAddress() {
+ return bluetoothDevice.getAddress();
+ }
+
+ public String getName() {
+ return bluetoothDevice.getName();
+ }
+
+ public int getRssi() {
+ return rssi;
+ }
+
+ public boolean connect(BtActionExecutor executor) {
+ Log.i(TAG, "connect(), address=" + bluetoothDevice.getAddress() + ", queue=" + executor);
+ bluetoothDevice.connectGatt(btService, false, executor.asCallback());
+ return true;
+ }
+
+ public BtScanResult getScanResult() {
+ return scanResult;
+ }
+
+ @Override
+ public String toString() {
+ return "BtDevice{address=" + bluetoothDevice.getAddress() + '}';
+ }
+}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtDeviceListener.java b/app/src/main/java/io/trygvis/android/bt/BtDeviceListener.java
new file mode 100644
index 0000000..57eabc6
--- /dev/null
+++ b/app/src/main/java/io/trygvis/android/bt/BtDeviceListener.java
@@ -0,0 +1,4 @@
+package io.trygvis.android.bt;
+
+public interface BtDeviceListener {
+}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtScanResult.java b/app/src/main/java/io/trygvis/android/bt/BtScanResult.java
new file mode 100644
index 0000000..c443afa
--- /dev/null
+++ b/app/src/main/java/io/trygvis/android/bt/BtScanResult.java
@@ -0,0 +1,9 @@
+package io.trygvis.android.bt;
+
+public class BtScanResult {
+ private final byte[] scanRecord;
+
+ public BtScanResult(byte[] scanRecord) {
+ this.scanRecord = scanRecord;
+ }
+}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtService.java b/app/src/main/java/io/trygvis/android/bt/BtService.java
new file mode 100644
index 0000000..123be3a
--- /dev/null
+++ b/app/src/main/java/io/trygvis/android/bt/BtService.java
@@ -0,0 +1,55 @@
+package io.trygvis.android.bt;
+
+import android.os.Binder;
+
+import java.util.List;
+
+public interface BtService<A> {
+
+ boolean initialize(BtServiceListener<A> btServiceListener, Supplier<A> dataSupplier);
+
+ void clearCache();
+
+ boolean isScanning();
+
+ boolean startScanning(long timeoutMs);
+
+ void stopScanning();
+
+// BtDevice<A> getDevice(String macAddress);
+
+ List<BtDevice<A>> getDevices();
+
+ interface Supplier<A> {
+ A get();
+ }
+
+ interface BtServiceListener<A> {
+ void onScanStarted();
+
+ void onNewDevice(BtDevice<A> device);
+
+ void onScanStopped();
+ }
+
+ public abstract class AbstractBtServiceListener<A> implements BtServiceListener<A> {
+
+ public void onScanStarted() {
+ }
+
+ public void onScanStopped() {
+ }
+ }
+
+ public class LocalBinder<A> extends Binder {
+ private final BtService<A> service;
+
+ public LocalBinder(BtService<A> service) {
+ this.service = service;
+ }
+
+ public BtService<A> getService() {
+ return service;
+ }
+ }
+}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtUtils.java b/app/src/main/java/io/trygvis/android/bt/BtUtils.java
new file mode 100644
index 0000000..66e745f
--- /dev/null
+++ b/app/src/main/java/io/trygvis/android/bt/BtUtils.java
@@ -0,0 +1,15 @@
+package io.trygvis.android.bt;
+
+public class BtUtils {
+ public static String toHexString(byte[] bytes) {
+ StringBuilder s = new StringBuilder();
+ for (byte b : bytes) {
+ if (b < 10) {
+ s.append('0');
+ }
+ s.append(Integer.toHexString(b));
+ }
+
+ return s.toString();
+ }
+}
diff --git a/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java b/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java
new file mode 100644
index 0000000..8638544
--- /dev/null
+++ b/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java
@@ -0,0 +1,192 @@
+package io.trygvis.android.bt;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import io.trygvis.soilmoisture.R;
+
+public class DefaultBtService<A> extends Service implements BtService<A> {
+ private final static String TAG = DefaultBtService.class.getSimpleName();
+
+ private final IBinder binder = new LocalBinder<>(this);
+
+ private Handler handler = new Handler();
+
+ // -----------------------------------------------------------------------
+ // State
+ // -----------------------------------------------------------------------
+
+ private BtServiceListener<A> serviceListener = new AbstractBtServiceListener<A>() {
+ @Override
+ public void onNewDevice(BtDevice<A> device) {
+ }
+ };
+
+ private Supplier<A> tagConstructor;
+
+ private BluetoothManager bluetoothManager;
+
+ private BluetoothAdapter bluetoothAdapter;
+
+ private final List<BtDevice<A>> devices = new ArrayList<>();
+
+ private boolean scanning = false;
+
+ // -----------------------------------------------------------------------
+ // BtService Implementation
+ // -----------------------------------------------------------------------
+
+ @Override
+ public boolean initialize(BtServiceListener<A> serviceListener, Supplier<A> dataSupplier) {
+ if (bluetoothManager != null) {
+ Log.e(TAG, "Already initialized");
+ return false;
+ }
+
+ this.tagConstructor = dataSupplier;
+
+ if (serviceListener != null) {
+ this.serviceListener = serviceListener;
+ }
+
+ // Use this check to determine whether BLE is supported on the device. Then you can
+ // selectively disable BLE-related features.
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
+ Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
+ // Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
+ // BluetoothAdapter through BluetoothManager.
+ bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+
+ if (bluetoothManager == null) {
+ Log.e(TAG, "Unable to initialize BluetoothManager.");
+ return false;
+ }
+
+ bluetoothAdapter = bluetoothManager.getAdapter();
+ if (bluetoothAdapter == null) {
+ Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
+ bluetoothManager = null;
+ return false;
+ }
+
+ Log.e(TAG, "Bluetooth initialized");
+ return true;
+ }
+
+ @Override
+ public void clearCache() {
+ }
+
+ @Override
+ public boolean isScanning() {
+ return scanning;
+ }
+
+ @Override
+ public boolean startScanning(long timeoutMs) {
+ if (timeoutMs > 0) {
+ handler.postDelayed(this::stopScanning, timeoutMs);
+ }
+
+ if (bluetoothAdapter.startLeScan(leScanCallback)) {
+ scanning = true;
+ serviceListener.onScanStarted();
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void stopScanning() {
+ // This doesn't mind being called twice.
+ bluetoothAdapter.stopLeScan(leScanCallback);
+
+ scanning = false;
+
+ serviceListener.onScanStopped();
+ }
+
+// @Override
+ public BtDevice<A> getDevice(String mac) {
+ BtDevice<A> device = findDevice(mac);
+
+ if (device != null) {
+ return device;
+ }
+
+ BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(mac);
+ return register(bluetoothDevice, null, null);
+ }
+
+ @Override
+ public List<BtDevice<A>> getDevices() {
+ return Collections.unmodifiableList(devices);
+ }
+
+ // -----------------------------------------------------------------------
+ // Scanning
+ // -----------------------------------------------------------------------
+
+ private BluetoothAdapter.LeScanCallback leScanCallback = (device, rssi, scanRecord) -> {
+// Log.i(TAG, "onLeScan()");
+
+ BtScanResult scanResult = new BtScanResult(scanRecord);
+
+ register(device, rssi, scanResult);
+ };
+
+ // -----------------------------------------------------------------------
+ // Service Implementation
+ // -----------------------------------------------------------------------
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return binder;
+ }
+
+ // -----------------------------------------------------------------------
+ // Stuff
+ // -----------------------------------------------------------------------
+
+ private BtDevice<A> register(BluetoothDevice bluetoothDevice, Integer rssi, BtScanResult scanResult) {
+ BtDevice<A> btDevice = findDevice(bluetoothDevice.getAddress());
+
+ if (btDevice != null) {
+ return btDevice;
+ }
+
+ Log.i(TAG, "New device: " + bluetoothDevice.getAddress());
+ btDevice = new BtDevice<>(this, bluetoothDevice, tagConstructor.get(), rssi, scanResult);
+ devices.add(btDevice);
+
+ serviceListener.onNewDevice(btDevice);
+
+ return btDevice;
+ }
+
+ private BtDevice<A> findDevice(String mac) {
+ for (BtDevice<A> d : devices) {
+ if (d.getAddress().equals(mac)) {
+ return d;
+ }
+ }
+ return null;
+ }
+}
diff --git a/app/src/main/java/io/trygvis/android/bt/NotOverriddenException.java b/app/src/main/java/io/trygvis/android/bt/NotOverriddenException.java
new file mode 100644
index 0000000..0f2bb6a
--- /dev/null
+++ b/app/src/main/java/io/trygvis/android/bt/NotOverriddenException.java
@@ -0,0 +1,4 @@
+package io.trygvis.android.bt;
+
+class NotOverriddenException extends RuntimeException {
+}