aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/io/trygvis/android/bt
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/io/trygvis/android/bt')
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtDevice.java81
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtPromise.java89
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtService.java9
-rw-r--r--app/src/main/java/io/trygvis/android/bt/DefaultBtService.java72
4 files changed, 168 insertions, 83 deletions
diff --git a/app/src/main/java/io/trygvis/android/bt/BtDevice.java b/app/src/main/java/io/trygvis/android/bt/BtDevice.java
index 2a4a5d8..6a4d92e 100644
--- a/app/src/main/java/io/trygvis/android/bt/BtDevice.java
+++ b/app/src/main/java/io/trygvis/android/bt/BtDevice.java
@@ -10,10 +10,11 @@ import android.util.Log;
import java.util.Date;
+import static io.trygvis.android.bt.BtPromise.BtBluetoothGattCallback;
import static io.trygvis.android.bt.BtPromise.PromiseResult.detour;
import static io.trygvis.android.bt.BtPromise.PromiseResult.stop;
-public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {
+public class BtDevice<A> {
private final static String TAG = BtDevice.class.getSimpleName();
private final DefaultBtService btService;
@@ -22,6 +23,7 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {
private Integer rssi;
private BtScanResult scanResult;
private A tag;
+ private final String address;
private final long id;
private final boolean seenBefore;
@@ -30,9 +32,7 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {
private boolean connected;
private final WrappingBluetoothGattCallback wrappingCallback = new WrappingBluetoothGattCallback();
- public static interface BtDeviceWrapper<A extends BtDevice.BtDeviceWrapper<A>> {
- BtDevice<A> getBtDevice();
- }
+ private BtBluetoothGattCallback callback;
BtDevice(DefaultBtService btService, BluetoothDevice bluetoothDevice, SQLiteDatabase db,
BtService.BtDbIntegration<A> btDbIntegration, long id, Integer rssi,
@@ -47,6 +47,7 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {
this.lastSeen = lastSeen;
this.tag = btDbIntegration.createTag(db, this);
+ this.address = bluetoothDevice.getAddress();
}
public long getId() {
@@ -65,7 +66,7 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {
return bluetoothDevice.getName();
}
- public int getRssi() {
+ public Integer getRssi() {
return rssi;
}
@@ -103,8 +104,9 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {
// return true;
// }
- private BluetoothGattCallback callback;
-
+ /**
+ * The first handler must handle a onDirect().
+ */
public synchronized void withConnection(BtPromise promise) {
if (callback != null) {
throw new RuntimeException("The current callback is not done.");
@@ -112,13 +114,15 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {
Log.i(TAG, "withConnection(), address=" + bluetoothDevice.getAddress() + ", connected: " + (gatt != null));
+ BtPromise newPromise;
if (gatt == null) {
- callback = new BtPromise().
+ newPromise = new BtPromise().
ignoreFailureForNext().
onConnectionStateChange((gatt, status, newState) -> {
Log.i(TAG, "defaultConnectCallback: status=" + status + ", newState=" + newState);
- String address = gatt.getDevice().getAddress();
- //noinspection SimplifiableIfStatement
+
+ btService.sendBroadcast(btService.createDeviceConnection(address));
+
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) {
Log.i(TAG, "Connected to " + address);
return detour(promise);
@@ -136,21 +140,26 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {
return stop();
}));
}
- }).
- asCallback(bluetoothDevice.getAddress());
+ });
} else {
- callback = promise.asCallback(bluetoothDevice.getAddress());
+ newPromise = promise;
}
- gatt = bluetoothDevice.connectGatt(btService, false, wrappingCallback);
- }
+ callback = newPromise.
+ onFinally(() -> {
+ Log.i(TAG, "Promise done, device is available again: address=" + address);
+ callback = null;
+ }).
+ asCallback(bluetoothDevice.getAddress());
-// public synchronized void disconnect() {
-// if (gatt != null) {
-// gatt.disconnect();
-// gatt = null;
-// }
-// }
+ if (gatt == null) {
+ gatt = bluetoothDevice.connectGatt(btService, false, wrappingCallback);
+ } else {
+ callback.onEvent(BtPromise.EventType.onDirect, "", gatt, null, null,
+ BluetoothGatt.GATT_SUCCESS,
+ BluetoothGatt.STATE_CONNECTED, gatt);
+ }
+ }
public boolean connected() {
return connected;
@@ -184,10 +193,32 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
- BtDevice.this.connected = newState == BluetoothGatt.STATE_CONNECTED;
-
- if (callback != null) {
- callback.onConnectionStateChange(gatt, status, newState);
+ Log.i(TAG, "Wrapping: onConnectionStateChange: " +
+ "address=" + gatt.getDevice().getAddress() + ", " +
+ "status=" + status + ", " +
+ "newState=" + newState);
+
+ boolean oldConnected = connected;
+
+ BtDevice.this.connected = status == BluetoothGatt.GATT_SUCCESS &&
+ newState == BluetoothGatt.STATE_CONNECTED;
+
+ try {
+ if (callback != null) {
+ callback.onConnectionStateChange(gatt, status, newState);
+ }
+ } finally {
+ if (!BtDevice.this.connected) {
+ if (oldConnected) {
+ Log.i(TAG, "Wrapper: Lost connection, removing gatt. gatt=" + gatt);
+ } else {
+ Log.i(TAG, "Wrapper: Lost connection, was not connected. gatt=" + gatt);
+ }
+
+ BtDevice.this.gatt = null;
+ } else {
+ Log.i(TAG, "Wrapper: connected");
+ }
}
}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtPromise.java b/app/src/main/java/io/trygvis/android/bt/BtPromise.java
index d4b3b67..c569ec2 100644
--- a/app/src/main/java/io/trygvis/android/bt/BtPromise.java
+++ b/app/src/main/java/io/trygvis/android/bt/BtPromise.java
@@ -21,7 +21,6 @@ import static io.trygvis.android.bt.BtPromise.EventType.onConnectionStateChange;
import static io.trygvis.android.bt.BtPromise.EventType.onDescriptorRead;
import static io.trygvis.android.bt.BtPromise.EventType.onDescriptorWrite;
import static io.trygvis.android.bt.BtPromise.EventType.onDirect;
-import static io.trygvis.android.bt.BtPromise.EventType.onFailure;
import static io.trygvis.android.bt.BtPromise.EventType.onFinally;
import static io.trygvis.android.bt.BtPromise.EventType.onReliableWriteCompleted;
import static io.trygvis.android.bt.BtPromise.EventType.onServicesDiscovered;
@@ -29,7 +28,7 @@ import static io.trygvis.android.bt.BtPromise.EventType.onServicesDiscovered;
public class BtPromise {
private final static String TAG = BtPromise.class.getSimpleName();
private final List<BtCallback> actionQ = new ArrayList<>();
- private final List<BtCallback> failureQ = new ArrayList<>();
+ // private final List<BtCallback> failureQ = new ArrayList<>();
private final List<BtCallback> finallyQ = new ArrayList<>();
private static final PromiseResult waitForNextEvent = new WaitForNextEvent();
@@ -194,15 +193,15 @@ public class BtPromise {
return this;
}
- public synchronized BtPromise onFailure(Runnable callback) {
- failureQ.add(new BtCallback(stopOnFailure(), "onFailure") {
- @Override
- public void onFailure() {
- callback.run();
- }
- });
- return this;
- }
+// public synchronized BtPromise onFailure(Runnable callback) {
+// failureQ.add(new BtCallback(stopOnFailure(), "onFailure") {
+// @Override
+// public void onFailure() {
+// callback.run();
+// }
+// });
+// return this;
+// }
public synchronized BtPromise onFinally(Runnable callback) {
finallyQ.add(new BtCallback(stopOnFailure(), "finally") {
@@ -233,7 +232,7 @@ public class BtPromise {
}
BtBluetoothGattCallback asCallback(String address) {
- return new BtBluetoothGattCallback(address, actionQ, failureQ, finallyQ);
+ return new BtBluetoothGattCallback(address, actionQ, /*failureQ, */finallyQ);
}
enum EventType {
@@ -294,15 +293,15 @@ public class BtPromise {
private final String address;
private List<BtCallback> actionQ;
private int currentAction = 0;
- private List<BtCallback> failureQ;
+ // private List<BtCallback> failureQ;
private List<BtCallback> finallyQ;
private List<String> events = new ArrayList<>();
private BtBluetoothGattCallback(String address, List<BtCallback> actionQ,
- List<BtCallback> failureQ, List<BtCallback> finallyQ) {
+ /*List<BtCallback> failureQ, */List<BtCallback> finallyQ) {
this.address = address;
this.actionQ = new ArrayList<>(actionQ);
- this.failureQ = new ArrayList<>(failureQ);
+// this.failureQ = new ArrayList<>(failureQ);
this.finallyQ = new ArrayList<>(finallyQ);
}
@@ -318,7 +317,6 @@ public class BtPromise {
if (!hasNext()) {
Log.d(TAG, "All Bluetooth actions are done, no handler for last event.");
- showEvents();
doFinally();
return;
}
@@ -327,7 +325,8 @@ public class BtPromise {
if (!success) {
if (btCallback.stopOnFailure) {
- doFailure();
+// doFailure();
+ doFinally();
return;
} else {
Log.i(TAG, "Last status was a failure, but the callback still want it.");
@@ -350,11 +349,15 @@ public class BtPromise {
if (result instanceof Stop) {
Log.i(TAG, "The chain want to stop.");
doFinally();
- } else if (result instanceof Detour) {
+ return;
+ }
+
+ if (result instanceof Detour) {
BtPromise detour = ((Detour) result).promise;
Log.i(TAG, "Adding detour with " + detour.actionQ.size() + " actions.");
- events.add("detour, action size=" + detour.actionQ.size() + ", failure size=" +
- detour.failureQ.size() + ", finally size=" + detour.finallyQ.size());
+ events.add("detour, action size=" + detour.actionQ.size() + ", " +
+// "failure size=" + detour.failureQ.size() + ", " +
+ "finally size=" + detour.finallyQ.size());
Log.i(TAG, "hasNext(): " + hasNext());
Log.i(TAG, "currentAction: " + currentAction);
@@ -363,13 +366,17 @@ public class BtPromise {
}
actionQ.addAll(currentAction, detour.actionQ);
- failureQ.addAll(detour.failureQ);
+// failureQ.addAll(detour.failureQ);
finallyQ.addAll(detour.finallyQ);
Log.i(TAG, "hasNext(): " + hasNext());
if (hasNext()) {
Log.i(TAG, "next action: " + actionQ.get(currentAction).name);
}
- } else if (result instanceof ContinueDirectly) {
+
+ result = PromiseResult.continueDirectly(gatt);
+ }
+
+ if (result instanceof ContinueDirectly) {
value = ((ContinueDirectly) result).value;
onEvent(onDirect, "value=" + value, null, null, null, BluetoothGatt.GATT_SUCCESS, 0,
value);
@@ -382,7 +389,8 @@ public class BtPromise {
}
} catch (NotOverriddenException e) {
Log.w(TAG, "Unexpected callback by listener: " + key);
- doFailure();
+// doFailure();
+ doFinally();
}
}
@@ -390,27 +398,38 @@ public class BtPromise {
return currentAction < actionQ.size();
}
- private void doFailure() {
- showEvents();
-
- Log.w(TAG, "Executing " + failureQ.size() + " failure handlers");
-
- for (BtCallback callback : failureQ) {
- callCallback(onFailure, null, null, null, 0, 0, callback, null);
- }
-
- doFinally();
- }
+// private void doFailure() {
+// showEvents();
+//
+// Log.w(TAG, "Executing " + failureQ.size() + " failure handlers");
+//
+// for (BtCallback callback : failureQ) {
+// callCallback(onFailure, null, null, null, 0, 0, callback, null);
+// }
+//
+// doFinally();
+// }
private void showEvents() {
StringBuilder msg = new StringBuilder();
msg.append("Address: ").append(address).append("\n");
+
msg.append("Event handlers: \n");
for (BtCallback cb : actionQ) {
msg.append("- ").append(cb.name).append("\n");
}
+// msg.append("Failure handlers: \n");
+// for (BtCallback cb : failureQ) {
+// msg.append("- ").append(cb.name).append("\n");
+// }
+
+ msg.append("Finally handlers: \n");
+ for (BtCallback cb : finallyQ) {
+ msg.append("- ").append(cb.name).append("\n");
+ }
+
msg.append("Events received: \n");
for (String event : events) {
msg.append("- ").append(event).append("\n");
@@ -420,6 +439,8 @@ public class BtPromise {
}
private void doFinally() {
+ showEvents();
+
actionQ.clear();
Log.w(TAG, "Executing " + finallyQ.size() + " finally handlers");
diff --git a/app/src/main/java/io/trygvis/android/bt/BtService.java b/app/src/main/java/io/trygvis/android/bt/BtService.java
index ff2c68c..06857ee 100644
--- a/app/src/main/java/io/trygvis/android/bt/BtService.java
+++ b/app/src/main/java/io/trygvis/android/bt/BtService.java
@@ -10,7 +10,7 @@ import java.util.Collection;
import io.trygvis.android.Function;
-public interface BtService<A extends BtDevice.BtDeviceWrapper<A>> {
+public interface BtService<A> {
boolean initialize(BtDbIntegration<A> btDbIntegration);
@@ -32,7 +32,7 @@ public interface BtService<A extends BtDevice.BtDeviceWrapper<A>> {
<T> T runTx(Function<SQLiteDatabase, T> action);
- public static interface BtDbIntegration<A extends BtDevice.BtDeviceWrapper<A>> {
+ public static interface BtDbIntegration<A> {
A createTag(SQLiteDatabase db, BtDevice<A> a);
}
@@ -53,10 +53,13 @@ public interface BtService<A extends BtDevice.BtDeviceWrapper<A>> {
public void onScanStarted() {
}
+ public void onScanStopped() {
+ }
+
public void onNewDevice(String address) {
}
- public void onScanStopped() {
+ public void onDeviceConnection(String address) {
}
}
}
diff --git a/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java b/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java
index 718a1cd..e42e685 100644
--- a/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java
+++ b/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import io.trygvis.android.Function;
@@ -32,7 +33,7 @@ import io.trygvis.soilmoisture.R;
import static java.util.Collections.unmodifiableCollection;
-public class DefaultBtService<A extends BtDevice.BtDeviceWrapper<A>> extends Service implements BtService<A> {
+public class DefaultBtService<A> extends Service implements BtService<A> {
private final static String TAG = DefaultBtService.class.getSimpleName();
private final IBinder binder = new LocalBinder<>(this);
@@ -90,6 +91,29 @@ public class DefaultBtService<A extends BtDevice.BtDeviceWrapper<A>> extends Ser
}
Log.i(TAG, "Bluetooth initialized");
+
+ List<String> addresses = runTx(db -> {
+ String[] columns = {Tables.C_ADDRESS};
+
+ List<String> as = new ArrayList<String>();
+ Cursor cursor = db.query(Tables.T_BT_DEVICE, columns, null, new String[0], null, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ String address = cursor.getString(0);
+ as.add(address);
+ }
+ } finally {
+ cursor.close();
+ }
+
+ return as;
+ });
+
+ for (String address : addresses) {
+ BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);
+ register(bluetoothDevice, null, null);
+ }
+
return true;
}
@@ -173,7 +197,9 @@ public class DefaultBtService<A extends BtDevice.BtDeviceWrapper<A>> extends Ser
return value;
} finally {
- db.endTransaction();
+ if (db.inTransaction()) {
+ db.endTransaction();
+ }
db.close();
}
}
@@ -185,11 +211,6 @@ public class DefaultBtService<A extends BtDevice.BtDeviceWrapper<A>> extends Ser
private BluetoothAdapter.LeScanCallback leScanCallback = (device, rssi, scanRecord) -> {
BtScanResult scanResult = new BtScanResult(scanRecord);
- if (!device.getAddress().startsWith("FB:")) {
- Log.w(TAG, "filtering out device: " + device.getAddress());
- return;
- }
-
register(device, rssi, scanResult);
};
@@ -268,8 +289,8 @@ public class DefaultBtService<A extends BtDevice.BtDeviceWrapper<A>> extends Ser
long now = System.currentTimeMillis();
btDevice = runTx(db -> {
- Cursor cursor = db.query("bt_device", new String[]{"id", "first_seen"}, "address=?",
- new String[]{address}, null, null, null);
+ Cursor cursor = db.query(Tables.T_BT_DEVICE, new String[]{Tables.C_ID, Tables.C_FIRST_SEEN},
+ Tables.C_ADDRESS + "=?", new String[]{address}, null, null, null);
long id;
Date firstSeen, lastSeen;
@@ -281,14 +302,14 @@ public class DefaultBtService<A extends BtDevice.BtDeviceWrapper<A>> extends Ser
lastSeen = new Date(now);
ContentValues values = new ContentValues();
- values.put("last_seen", now);
- db.update("bt_device", values, "address=?", new String[]{address});
+ values.put(Tables.C_LAST_SEEN, now);
+ db.update(Tables.T_BT_DEVICE, values, "address=?", new String[]{address});
} else {
ContentValues values = new ContentValues();
- values.put("address", address);
- values.put("first_seen", now);
- values.put("last_seen", now);
- id = db.insert("bt_device", null, values);
+ values.put(Tables.C_ADDRESS, address);
+ values.put(Tables.C_FIRST_SEEN, now);
+ values.put(Tables.C_LAST_SEEN, now);
+ id = db.insert(Tables.T_BT_DEVICE, null, values);
firstSeen = lastSeen = new Date(now);
}
@@ -305,35 +326,44 @@ public class DefaultBtService<A extends BtDevice.BtDeviceWrapper<A>> extends Ser
return btDevice;
}
- private Intent createScanStarted() {
+ Intent createScanStarted() {
return new Intent(BtServiceListenerBroadcastReceiver.INTENT_NAME).
putExtra("event", "scanStarted");
}
- private Intent createScanStopped() {
+ Intent createScanStopped() {
return new Intent(BtServiceListenerBroadcastReceiver.INTENT_NAME).
putExtra("event", "scanStopped");
}
- private Intent createNewDevice(String address) {
+ Intent createNewDevice(String address) {
return new Intent(BtServiceListenerBroadcastReceiver.INTENT_NAME).
putExtra("event", "newDevice").
putExtra("address", address);
}
+ Intent createDeviceConnection(String address) {
+ return new Intent(BtServiceListenerBroadcastReceiver.INTENT_NAME).
+ putExtra("event", "deviceConnection").
+ putExtra("address", address);
+ }
+
public static void dispatchEvent(Intent intent, BtServiceListenerBroadcastReceiver listener) {
String event = intent.getStringExtra("event");
Log.i(TAG, "Dispatching event " + intent.getAction() + "/" + event);
switch (event) {
- case "newDevice":
- listener.onNewDevice(intent.getStringExtra("address"));
- break;
case "scanStarted":
listener.onScanStarted();
break;
case "scanStopped":
listener.onScanStopped();
break;
+ case "newDevice":
+ listener.onNewDevice(intent.getStringExtra("address"));
+ break;
+ case "deviceConnection":
+ listener.onDeviceConnection(intent.getStringExtra("address"));
+ break;
default:
break;
}