diff options
Diffstat (limited to 'app/src/main/java/io/trygvis/android')
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 { +} |