aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2015-01-28 23:45:38 +0100
committerTrygve Laugstøl <trygvis@inamo.no>2015-01-28 23:45:38 +0100
commit3e619a735e63a1222e71060d9e65b354a156b158 (patch)
tree2f31cdc65d0d773874800c114530eb620bcca536
parentc4685214d8db34166213ffa373a16af1a99401a5 (diff)
downloadio.trygvis.soilmoisture-android-3e619a735e63a1222e71060d9e65b354a156b158.tar.gz
io.trygvis.soilmoisture-android-3e619a735e63a1222e71060d9e65b354a156b158.tar.bz2
io.trygvis.soilmoisture-android-3e619a735e63a1222e71060d9e65b354a156b158.tar.xz
io.trygvis.soilmoisture-android-3e619a735e63a1222e71060d9e65b354a156b158.zip
o Major refactoring on the BtPromise, mainly internal. Renaming BtPromise to BtSequence and BtSequencer.
-rw-r--r--app/build.gradle23
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtDevice.java29
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtPromise.java519
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java28
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/SensorActivity.java1
-rw-r--r--bt/build.gradle46
-rw-r--r--bt/src/main/java/io/trygvis/android/Consumer.java (renamed from app/src/main/java/io/trygvis/android/Consumer.java)0
-rw-r--r--bt/src/main/java/io/trygvis/android/F2.java (renamed from app/src/main/java/io/trygvis/android/F2.java)0
-rw-r--r--bt/src/main/java/io/trygvis/android/F3.java (renamed from app/src/main/java/io/trygvis/android/F3.java)0
-rw-r--r--bt/src/main/java/io/trygvis/android/F4.java (renamed from app/src/main/java/io/trygvis/android/F4.java)0
-rw-r--r--bt/src/main/java/io/trygvis/android/Function.java (renamed from app/src/main/java/io/trygvis/android/Function.java)0
-rw-r--r--bt/src/main/java/io/trygvis/android/Optional.java (renamed from app/src/main/java/io/trygvis/android/Optional.java)0
-rw-r--r--bt/src/main/java/io/trygvis/android/Supplier.java (renamed from app/src/main/java/io/trygvis/android/Supplier.java)0
-rw-r--r--bt/src/main/java/io/trygvis/android/bt/BtBluetoothGattCallback.java73
-rw-r--r--bt/src/main/java/io/trygvis/android/bt/BtCallback.java (renamed from app/src/main/java/io/trygvis/android/bt/BtCallback.java)31
-rw-r--r--bt/src/main/java/io/trygvis/android/bt/BtSequence.java243
-rw-r--r--bt/src/main/java/io/trygvis/android/bt/BtSequencer.java265
-rw-r--r--bt/src/main/java/io/trygvis/android/bt/NotOverriddenException.java (renamed from app/src/main/java/io/trygvis/android/bt/NotOverriddenException.java)0
-rw-r--r--bt/src/test/java/io/trygvis/android/bt/BtSequencerTest.java96
-rw-r--r--build.gradle4
-rw-r--r--settings.gradle2
-rw-r--r--trygvis-io-bt-stubs/build.gradle5
-rw-r--r--trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGatt.java32
-rw-r--r--trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattCallback.java31
-rw-r--r--trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattCharacteristic.java13
-rw-r--r--trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattDescriptor.java13
-rw-r--r--trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattService.java4
-rw-r--r--trygvis-io-bt-stubs/src/main/java/android/util/Log.java20
28 files changed, 909 insertions, 569 deletions
diff --git a/app/build.gradle b/app/build.gradle
index bea207c..0b6ba45 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,3 +1,5 @@
+// https://github.com/JCAndKSolutions/android-unit-test
+
def getVersionCode = { ->
try {
def stdout = new ByteArrayOutputStream()
@@ -56,7 +58,7 @@ android {
versionCode getVersionCode()
versionName getVersionName()
- println("versionCode=$versionCode, versionName=$versionName");
+ return println("versionCode=$versionCode, versionName=$versionName");
}
signingConfigs {
release {
@@ -90,5 +92,22 @@ dependencies {
}
compile 'org.sqldroid:sqldroid:1.0.3'
compile 'org.flywaydb:flyway-core:3.1'
- // compile 'net.sourceforge.streamsupport:streamsupport:1.1.2'
+ compile project(':bt')
+}
+
+/*
+apply plugin: 'android-unit-test'
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ compile('com.crashlytics.sdk.android:crashlytics:2.1.0@aar') {
+ transitive = true;
+ }
+ compile 'org.sqldroid:sqldroid:1.0.3'
+ compile 'org.flywaydb:flyway-core:3.1'
+
+ testCompile 'junit:junit:4.11'
+ testCompile 'org.robolectric:robolectric:2.4'
+ testCompile 'org.easytesting:fest-assert:1.4'
}
+*/
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 b90ac4f..07e4e52 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,9 @@ 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.fail;
-import static io.trygvis.android.bt.BtPromise.PromiseResult.waitForNextEvent;
+import static io.trygvis.android.bt.BtSequence.SequenceResult.detour;
+import static io.trygvis.android.bt.BtSequence.SequenceResult.fail;
+import static io.trygvis.android.bt.BtSequence.SequenceResult.waitForNextEvent;
public class BtDevice<A> implements Comparable<BtDevice> {
private final static String TAG = BtDevice.class.getSimpleName();
@@ -103,16 +102,16 @@ public class BtDevice<A> implements Comparable<BtDevice> {
* <p>
* Services will be discovered.
*/
- public synchronized void withConnection(BtPromise promise) {
+ public synchronized void withConnection(BtSequence sequence) {
if (callback != null) {
throw new RuntimeException("The current callback is not done.");
}
Log.i(TAG, "withConnection(), address=" + address + ", connected: " + (gatt != null));
- BtPromise newPromise;
+ BtSequence newSequence;
if (gatt == null) {
- newPromise = new BtPromise().
+ newSequence = new BtSequence().
ignoreFailureForNext().
onConnectionStateChange((gatt, status, newState) -> {
Log.i(TAG, "defaultConnectCallback: status=" + status + ", newState=" + newState);
@@ -125,7 +124,7 @@ public class BtDevice<A> implements Comparable<BtDevice> {
} else {
Log.i(TAG, "Could not connect to " + address + ", trying again");
- return detour(new BtPromise().onConnectionStateChange((gatt2, status2, newState2) -> {
+ return detour(new BtSequence().onConnectionStateChange((gatt2, status2, newState2) -> {
if (status2 == BluetoothGatt.GATT_SUCCESS && newState2 == BluetoothGatt.STATE_CONNECTED) {
Log.i(TAG, "Connected to " + address + ", discovering services");
return gatt.discoverServices() ? waitForNextEvent() : fail();
@@ -139,25 +138,23 @@ public class BtDevice<A> implements Comparable<BtDevice> {
}).
onServicesDiscovered(gatt -> {
Log.i(TAG, "Services discovered, has " + gatt.getServices().size() + " services");
- return detour(promise);
+ return detour(sequence);
});
} else {
- newPromise = promise;
+ newSequence = sequence;
}
- callback = newPromise.
+ BtSequencer sequencer = new BtSequencer(bluetoothDevice.getAddress(), newSequence.
onFinally(success -> {
Log.i(TAG, "Promise done, device is available again: address=" + address + ", success=" + success);
callback = null;
- }).
- asCallback(bluetoothDevice.getAddress());
+ }));
+ callback = new BtBluetoothGattCallback(sequencer);
if (gatt == null) {
gatt = bluetoothDevice.connectGatt(btService, false, wrappingCallback);
} else {
- callback.onEvent(BtPromise.EventType.onDirect, "", gatt, null, null,
- BluetoothGatt.GATT_SUCCESS,
- BluetoothGatt.STATE_CONNECTED);
+ sequencer.onDirect();
}
}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtPromise.java b/app/src/main/java/io/trygvis/android/bt/BtPromise.java
deleted file mode 100644
index bffdd19..0000000
--- a/app/src/main/java/io/trygvis/android/bt/BtPromise.java
+++ /dev/null
@@ -1,519 +0,0 @@
-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.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import io.trygvis.android.Consumer;
-import io.trygvis.android.F2;
-import io.trygvis.android.F3;
-import io.trygvis.android.Function;
-
-import static io.trygvis.android.bt.BtPromise.EventType.onCharacteristicChanged;
-import static io.trygvis.android.bt.BtPromise.EventType.onCharacteristicRead;
-import static io.trygvis.android.bt.BtPromise.EventType.onCharacteristicWrite;
-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.onFinally;
-import static io.trygvis.android.bt.BtPromise.EventType.onReliableWriteCompleted;
-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> finallyQ = new ArrayList<>();
-
- private static final PromiseResult waitForNextEvent = new WaitForNextEvent();
- private static final PromiseResult stop = new Stop();
- private static final PromiseResult fail = new Fail();
- private static final PromiseResult continueDirectly = new ContinueDirectly();
-
- private Boolean stopOnFailure;
-
- public static class PromiseResult {
- private PromiseResult() {
- }
-
- public static PromiseResult waitForNextEvent() {
- return waitForNextEvent;
- }
-
- public static PromiseResult continueDirectly() {
- return continueDirectly;
- }
-
- public static PromiseResult stop() {
- return stop;
- }
-
- public static PromiseResult fail() {
- return fail;
- }
-
- public static PromiseResult detour(BtPromise promise) {
- return new Detour(promise);
- }
- }
-
- private static class WaitForNextEvent extends PromiseResult {
- }
-
- private static class ContinueDirectly extends PromiseResult {
- }
-
- private static class Stop extends PromiseResult {
- }
-
- private static class Fail extends PromiseResult {
- }
-
- private static class Detour extends PromiseResult {
- final BtPromise promise;
-
- private Detour(BtPromise promise) {
- this.promise = promise;
- }
- }
-
- private boolean stopOnFailure() {
- if (stopOnFailure != null) {
- boolean b = stopOnFailure;
- stopOnFailure = null;
- return b;
- }
- return false;
- }
-
- public BtPromise ignoreFailureForNext() {
- stopOnFailure = true;
- return this;
- }
-
- public synchronized BtPromise onConnectionStateChange(F3<BluetoothGatt, Integer, Integer, PromiseResult> callback) {
- actionQ.add(new BtCallback(stopOnFailure(), "onConnectionStateChange") {
- @Override
- public PromiseResult onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
- return callback.apply(gatt, status, newState);
- }
- });
- return this;
- }
-
- public synchronized BtPromise onServicesDiscovered(Function<BluetoothGatt, PromiseResult> callback) {
- actionQ.add(new BtCallback(stopOnFailure(), "onServicesDiscovered") {
- @Override
- public PromiseResult onServicesDiscovered(BluetoothGatt gatt) {
- return callback.apply(gatt);
- }
- });
- return this;
- }
-
- public synchronized BtPromise onCharacteristicRead(F2<BluetoothGatt, BluetoothGattCharacteristic, PromiseResult> callback) {
- actionQ.add(new BtCallback(stopOnFailure(), "onCharacteristicRead") {
- @Override
- public PromiseResult onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
- return callback.apply(gatt, characteristic);
- }
- });
- return this;
- }
-
- public synchronized BtPromise onCharacteristicWrite(F2<BluetoothGatt, BluetoothGattCharacteristic, PromiseResult> callback) {
- actionQ.add(new BtCallback(stopOnFailure(), "onCharacteristicWrite") {
- @Override
- public PromiseResult onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
- return callback.apply(gatt, characteristic);
- }
- });
- return this;
- }
-
- public synchronized BtPromise onCharacteristicChanged(F2<BluetoothGatt, BluetoothGattCharacteristic, PromiseResult> callback) {
- actionQ.add(new BtCallback(stopOnFailure(), "onCharacteristicChanged") {
- @Override
- public PromiseResult onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
- return callback.apply(gatt, characteristic);
- }
- });
- return this;
- }
-
- public synchronized BtPromise onDescriptorRead(F2<BluetoothGatt, BluetoothGattDescriptor, PromiseResult> callback) {
- actionQ.add(new BtCallback(stopOnFailure(), "onDescriptorRead") {
- @Override
- public PromiseResult onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
- return callback.apply(gatt, descriptor);
- }
- });
- return this;
- }
-
- public synchronized BtPromise onDescriptorWrite(F2<BluetoothGatt, BluetoothGattDescriptor, PromiseResult> callback) {
- actionQ.add(new BtCallback(stopOnFailure(), "onDescriptorWrite") {
- @Override
- public PromiseResult onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
- return callback.apply(gatt, descriptor);
- }
- });
- return this;
- }
-
- public synchronized BtPromise onReliableWriteCompleted(Function<BluetoothGatt, PromiseResult> callback) {
- actionQ.add(new BtCallback(stopOnFailure(), "onReliableWriteCompleted") {
- @Override
- public PromiseResult onReliableWriteCompleted(BluetoothGatt gatt) {
- return callback.apply(gatt);
- }
- });
- return this;
- }
-
- public synchronized BtPromise onReadRemoteRssi(F2<BluetoothGatt, Integer, PromiseResult> callback) {
- actionQ.add(new BtCallback(stopOnFailure(), "onReadRemoteRssi") {
- @Override
- public PromiseResult onReadRemoteRssi(BluetoothGatt gatt, int rssi) {
- return callback.apply(gatt, rssi);
- }
- });
- return this;
- }
-
- public synchronized BtPromise onDirect(Function<BluetoothGatt, PromiseResult> callback) {
- actionQ.add(new BtCallback(stopOnFailure(), "onDirect") {
- @Override
- public PromiseResult onDirect(BluetoothGatt value) {
- return callback.apply(value);
- }
- });
- 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(Consumer<Boolean> callback) {
- finallyQ.add(new BtCallback(stopOnFailure(), "finally") {
- @Override
- public void onFinally(boolean success) {
- callback.accept(success);
- }
- });
- return this;
- }
-
- public BtPromise andThen(BtPromise btPromise) {
- actionQ.addAll(btPromise.actionQ);
- finallyQ.addAll(btPromise.finallyQ);
- return this;
- }
-
- 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();
- }
-
- BtBluetoothGattCallback asCallback(String address) {
- return new BtBluetoothGattCallback(address, actionQ, /*failureQ, */finallyQ);
- }
-
- enum EventType {
- onConnectionStateChange,
- onServicesDiscovered,
- onCharacteristicRead,
- onCharacteristicWrite,
- onCharacteristicChanged,
- onDescriptorRead,
- onDescriptorWrite,
- onReliableWriteCompleted,
-
- onReadRemoteRssi,
-
- onDirect,
- onFailure,
- onFinally,
- }
-
- private static PromiseResult callCallback(EventType key, BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic,
- BluetoothGattDescriptor descriptor, int status, int newState,
- Boolean success, BtCallback btCallback) {
- switch (key) {
- case onConnectionStateChange:
- return btCallback.onConnectionStateChange(gatt, status, 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 onDirect:
- return btCallback.onDirect(gatt);
- case onFailure:
- btCallback.onFailure();
- return null;
- case onFinally:
- btCallback.onFinally(success);
- return null;
- default:
- Log.w(TAG, "Unknown callback: " + key);
- return null;
- }
- }
-
- static class BtBluetoothGattCallback extends BluetoothGattCallback {
-
- private final String address;
- private List<BtCallback> actionQ;
- private int currentAction = 0;
- // 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) {
- this.address = address;
- this.actionQ = new ArrayList<>(actionQ);
-// this.failureQ = new ArrayList<>(failureQ);
- this.finallyQ = new ArrayList<>(finallyQ);
- }
-
- void onEvent(EventType key, String values, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
- BluetoothGattDescriptor descriptor, int status, int newState) {
- boolean success = status == BluetoothGatt.GATT_SUCCESS;
- events.add(key + "(" + values + "), success=" + success);
-
- Log.i(TAG, "event: " + key + "(" + values + "), success=" + success);
-
- BtCallback btCallback;
- synchronized (this) {
- if (!hasNext()) {
- Log.d(TAG, "All Bluetooth actions are done, no handler for last event.");
-
- doFinally(true);
- return;
- }
-
- btCallback = actionQ.get(currentAction++);
-
- if (!success) {
- if (btCallback.stopOnFailure) {
-// doFailure();
- doFinally(false);
- return;
- } else {
- Log.i(TAG, "Last status was a failure, but the callback still want it.");
- }
- }
-
-// Log.i(TAG, "Graceful sleep" + btCallback.name);
-// try {
-// Thread.sleep(1000);
-// } catch (InterruptedException e) {
-// // ignore
-// }
- }
-
- try {
- Log.i(TAG, "Executing bt action: " + btCallback.name);
- PromiseResult result = callCallback(key, gatt, characteristic, descriptor, status, newState, null,
- btCallback);
-
- if (result instanceof Stop) {
- Log.i(TAG, "The chain want to stop.");
- doFinally(true);
- return;
- }
-
- if (result instanceof Fail) {
- Log.i(TAG, "The chain returned fail().");
- doFinally(false);
- 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());
-
- // The new promise should probably be stacked on top, so that all of its
- // finally handlers are executed after the added set concludes and then the
- // current stack can continue.
-
- actionQ.addAll(currentAction, detour.actionQ);
-// failureQ.addAll(detour.failureQ);B
- finallyQ.addAll(detour.finallyQ);
-
- result = PromiseResult.continueDirectly();
- }
-
- if (result instanceof ContinueDirectly) {
- onEvent(onDirect, "direct", gatt, null, null, BluetoothGatt.GATT_SUCCESS, 0);
- return;
- }
-
- if (!hasNext()) {
- Log.i(TAG, "The queue is empty");
- showEvents();
- }
- } catch (NotOverriddenException e) {
- Log.w(TAG, "Unexpected callback by listener: " + key);
-// doFailure();
- doFinally(false);
- } catch (Exception e) {
- Log.w(TAG, "Exception in callback", e);
-// doFailure();
- doFinally(false);
- }
- }
-
- private boolean hasNext() {
- 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 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");
- }
-
- Log.w(TAG, msg.toString());
- }
-
- private void doFinally(boolean success) {
- showEvents();
-
- actionQ.clear();
-
- Log.w(TAG, "Executing " + finallyQ.size() + " finally handlers, success=" + success);
-
- for (BtCallback callback : finallyQ) {
- try {
- callCallback(onFinally, null, null, null, 0, 0, success, callback);
- } catch (NotOverriddenException e) {
- return;
- }
- }
- }
-
- // -----------------------------------------------------------------------
- //
- // -----------------------------------------------------------------------
-
- @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) {
-// onEvent(onReadRemoteRssi, "status=" + status, gatt, null, null, status, 0);
- }
- }
-}
diff --git a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
index 3e1c93b..2cf85d9 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
@@ -26,15 +26,16 @@ import java.util.TreeSet;
import io.trygvis.android.Function;
import io.trygvis.android.LocalBinder;
import io.trygvis.android.bt.BtDevice;
-import io.trygvis.android.bt.BtPromise;
+import io.trygvis.android.bt.BtSequence;
import io.trygvis.android.bt.BtService;
import io.trygvis.android.bt.DefaultBtService;
import io.trygvis.bluetooth.TrygvisIoUuids;
-import static io.trygvis.android.bt.BtPromise.PromiseResult.continueDirectly;
-import static io.trygvis.android.bt.BtPromise.PromiseResult.detour;
-import static io.trygvis.android.bt.BtPromise.PromiseResult.stop;
-import static io.trygvis.android.bt.BtPromise.PromiseResult.waitForNextEvent;
+import static io.trygvis.android.bt.BtSequence.SequenceResult;
+import static io.trygvis.android.bt.BtSequence.SequenceResult.continueDirectly;
+import static io.trygvis.android.bt.BtSequence.SequenceResult.detour;
+import static io.trygvis.android.bt.BtSequence.SequenceResult.stop;
+import static io.trygvis.android.bt.BtSequence.SequenceResult.waitForNextEvent;
import static io.trygvis.android.bt.BtService.BtServiceListenerBroadcastReceiver;
import static io.trygvis.bluetooth.TrygvisIoUuids.CLIENT_CHARACTERISTIC_CONFIG;
import static io.trygvis.soilmoisture.SmDevice.GetSensorCountRes;
@@ -139,8 +140,8 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
}
};
- private BtPromise readAttribute(String value, byte[] req, Function<byte[], BtPromise.PromiseResult> handler) {
- return new BtPromise().
+ private BtSequence readAttribute(String value, byte[] req, Function<byte[], SequenceResult> handler) {
+ return new BtSequence().
onDirect(gatt -> {
Log.i(TAG, "Getting attribute: " + value);
@@ -158,7 +159,7 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
});
}
- private BtPromise readSensorName(SmDevice device, int index) {
+ private BtSequence readSensorName(SmDevice device, int index) {
byte[] req = createGetSensorNameReq((byte) index);
return readAttribute("sensor name, index#" + index, req, bytes -> {
@@ -179,7 +180,7 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
SmDevice smDevice = btDevice.getTag();
Log.i(TAG, "Probing " + address + ", name=" + btDevice.getName());
- BtPromise promise = new BtPromise().
+ BtSequence sequence = new BtSequence().
onDirect(gatt -> {
BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE);
@@ -210,7 +211,7 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
}
});
- BtPromise btPromise = readAttribute("sensor count", createGetSensorCountReq(), bytes -> {
+ BtSequence btSequence = readAttribute("sensor count", createGetSensorCountReq(), bytes -> {
GetSensorCountRes getSensorCountRes = parseResponse(bytes, GET_SENSOR_COUNT, GetSensorCountRes.class);
int count = getSensorCountRes.count;
@@ -221,9 +222,8 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
return detour(readSensorName(smDevice, 0));
});
- promise.andThen(btPromise);
- btDevice.withConnection(promise);
+ btDevice.withConnection(sequence.andThen(btSequence));
}
private void markDeviceAsNotUseful(SmDevice device) {
@@ -371,7 +371,7 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
}
void readCurrentValue(SmSensor sensor) {
- BtPromise promise = new BtPromise().
+ BtSequence sequence = new BtSequence().
onDirect(gatt -> {
BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE);
BluetoothGattCharacteristic soilMoisture = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE);
@@ -402,7 +402,7 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
}
});
- sensor.getDevice().getBtDevice().withConnection(promise);
+ sensor.getDevice().getBtDevice().withConnection(sequence);
}
// -----------------------------------------------------------------------
diff --git a/app/src/main/java/io/trygvis/soilmoisture/SensorActivity.java b/app/src/main/java/io/trygvis/soilmoisture/SensorActivity.java
index 358382c..a5b68d8 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/SensorActivity.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/SensorActivity.java
@@ -128,7 +128,6 @@ public class SensorActivity extends Activity {
}
-
private class SensorSoilMoistureListener extends SoilMoistureListener {
}
}
diff --git a/bt/build.gradle b/bt/build.gradle
new file mode 100644
index 0000000..5beefce
--- /dev/null
+++ b/bt/build.gradle
@@ -0,0 +1,46 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'me.tatarka:gradle-retrolambda:2.5.0'
+ }
+}
+
+apply plugin: 'java'
+apply plugin: 'idea'
+
+configurations {
+ provided
+}
+
+sourceSets {
+ main.compileClasspath += configurations.provided
+ test.compileClasspath += configurations.provided
+ test.runtimeClasspath += configurations.provided
+}
+
+//sourceCompatibility JavaVersion.VERSION_1_8
+//targetCompatibility JavaVersion.VERSION_1_8
+//sourceCompatibility "1.8"
+//targetCompatibility "1.8"
+
+//compileJava {
+// sourceCompatibility 1.8
+// targetCompatibility 1.8
+//}
+
+apply plugin: 'me.tatarka.retrolambda'
+
+dependencies {
+ provided project(':trygvis-io-bt-stubs')
+ testCompile 'junit:junit:4.11'
+ testCompile 'org.easytesting:fest-assert:1.4'
+}
+
+idea {
+ module {
+ scopes.PROVIDED.plus += [configurations.provided] // for Gradle 2.0
+ }
+}
diff --git a/app/src/main/java/io/trygvis/android/Consumer.java b/bt/src/main/java/io/trygvis/android/Consumer.java
index bf8c5e5..bf8c5e5 100644
--- a/app/src/main/java/io/trygvis/android/Consumer.java
+++ b/bt/src/main/java/io/trygvis/android/Consumer.java
diff --git a/app/src/main/java/io/trygvis/android/F2.java b/bt/src/main/java/io/trygvis/android/F2.java
index 1f362cc..1f362cc 100644
--- a/app/src/main/java/io/trygvis/android/F2.java
+++ b/bt/src/main/java/io/trygvis/android/F2.java
diff --git a/app/src/main/java/io/trygvis/android/F3.java b/bt/src/main/java/io/trygvis/android/F3.java
index 30d3adf..30d3adf 100644
--- a/app/src/main/java/io/trygvis/android/F3.java
+++ b/bt/src/main/java/io/trygvis/android/F3.java
diff --git a/app/src/main/java/io/trygvis/android/F4.java b/bt/src/main/java/io/trygvis/android/F4.java
index 5ff50f5..5ff50f5 100644
--- a/app/src/main/java/io/trygvis/android/F4.java
+++ b/bt/src/main/java/io/trygvis/android/F4.java
diff --git a/app/src/main/java/io/trygvis/android/Function.java b/bt/src/main/java/io/trygvis/android/Function.java
index 5e3bb96..5e3bb96 100644
--- a/app/src/main/java/io/trygvis/android/Function.java
+++ b/bt/src/main/java/io/trygvis/android/Function.java
diff --git a/app/src/main/java/io/trygvis/android/Optional.java b/bt/src/main/java/io/trygvis/android/Optional.java
index 616f9a7..616f9a7 100644
--- a/app/src/main/java/io/trygvis/android/Optional.java
+++ b/bt/src/main/java/io/trygvis/android/Optional.java
diff --git a/app/src/main/java/io/trygvis/android/Supplier.java b/bt/src/main/java/io/trygvis/android/Supplier.java
index 222dff2..222dff2 100644
--- a/app/src/main/java/io/trygvis/android/Supplier.java
+++ b/bt/src/main/java/io/trygvis/android/Supplier.java
diff --git a/bt/src/main/java/io/trygvis/android/bt/BtBluetoothGattCallback.java b/bt/src/main/java/io/trygvis/android/bt/BtBluetoothGattCallback.java
new file mode 100644
index 0000000..1f4921f
--- /dev/null
+++ b/bt/src/main/java/io/trygvis/android/bt/BtBluetoothGattCallback.java
@@ -0,0 +1,73 @@
+package io.trygvis.android.bt;
+
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+
+import static io.trygvis.android.bt.BtSequencer.EventType.onCharacteristicChanged;
+import static io.trygvis.android.bt.BtSequencer.EventType.onCharacteristicRead;
+import static io.trygvis.android.bt.BtSequencer.EventType.onCharacteristicWrite;
+import static io.trygvis.android.bt.BtSequencer.EventType.onConnectionStateChange;
+import static io.trygvis.android.bt.BtSequencer.EventType.onDescriptorRead;
+import static io.trygvis.android.bt.BtSequencer.EventType.onDescriptorWrite;
+import static io.trygvis.android.bt.BtSequencer.EventType.onReliableWriteCompleted;
+import static io.trygvis.android.bt.BtSequencer.EventType.onServicesDiscovered;
+
+public class BtBluetoothGattCallback extends BluetoothGattCallback {
+
+ private final BtSequencer sequencer;
+
+ BtBluetoothGattCallback(BtSequencer sequencer) {
+ this.sequencer = sequencer;
+ }
+
+ // -----------------------------------------------------------------------
+ //
+ // -----------------------------------------------------------------------
+
+ @Override
+ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+ sequencer.onEvent(onConnectionStateChange, "status=" + status + ", newState=" + newState, gatt, null, null, status, newState);
+ }
+
+ @Override
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ sequencer.onEvent(onServicesDiscovered, "status=" + status, gatt, null, null, status, 9);
+ }
+
+ @Override
+ public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ sequencer.onEvent(onCharacteristicRead, "status=" + status + ", characteristic=" + characteristic.getUuid(), gatt, characteristic, null, status, 0);
+ }
+
+ @Override
+ public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ sequencer.onEvent(onCharacteristicWrite, "status=" + status + ", characteristic=" + characteristic.getUuid(), gatt, characteristic, null, status, 0);
+ }
+
+ @Override
+ public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ sequencer.onEvent(onCharacteristicChanged, "characteristic=" + characteristic.getUuid(), gatt, characteristic, null, BluetoothGatt.GATT_SUCCESS, 0);
+ }
+
+ @Override
+ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ sequencer.onEvent(onDescriptorRead, "status=" + status + ", descriptor=" + descriptor.getUuid(), gatt, null, descriptor, status, 0);
+ }
+
+ @Override
+ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ sequencer.onEvent(onDescriptorWrite, "status=" + status + ", descriptor=" + descriptor.getUuid(), gatt, null, descriptor, status, 0);
+ }
+
+ @Override
+ public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
+ sequencer.onEvent(onReliableWriteCompleted, "status=" + status, gatt, null, null, status, 0);
+ }
+
+// @Override
+// public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+// sequencer.onEvent(onReadRemoteRssi, "status=" + status, gatt, null, null, status, 0);
+// }
+} \ No newline at end of file
diff --git a/app/src/main/java/io/trygvis/android/bt/BtCallback.java b/bt/src/main/java/io/trygvis/android/bt/BtCallback.java
index 0f7287f..4aa8529 100644
--- a/app/src/main/java/io/trygvis/android/bt/BtCallback.java
+++ b/bt/src/main/java/io/trygvis/android/bt/BtCallback.java
@@ -4,54 +4,55 @@ import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
-import static io.trygvis.android.bt.BtPromise.PromiseResult;
+import static io.trygvis.android.bt.BtSequence.SequenceResult;
+import static io.trygvis.android.bt.BtSequencer.EventType;
public class BtCallback {
public final boolean stopOnFailure;
- public final String name;
+ public final EventType type;
- public BtCallback(boolean stopOnFailure, String name) {
+ public BtCallback(boolean stopOnFailure, EventType type) {
this.stopOnFailure = stopOnFailure;
- this.name = name;
+ this.type = type;
}
- public PromiseResult onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+ public SequenceResult onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
throw new NotOverriddenException();
}
- public PromiseResult onServicesDiscovered(BluetoothGatt gatt) {
+ public SequenceResult onServicesDiscovered(BluetoothGatt gatt) {
throw new NotOverriddenException();
}
- public PromiseResult onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ public SequenceResult onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
throw new NotOverriddenException();
}
- public PromiseResult onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ public SequenceResult onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
throw new NotOverriddenException();
}
- public PromiseResult onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ public SequenceResult onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
throw new NotOverriddenException();
}
- public PromiseResult onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
+ public SequenceResult onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
throw new NotOverriddenException();
}
- public PromiseResult onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
+ public SequenceResult onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
throw new NotOverriddenException();
}
- public PromiseResult onReliableWriteCompleted(BluetoothGatt gatt) {
+ public SequenceResult onReliableWriteCompleted(BluetoothGatt gatt) {
throw new NotOverriddenException();
}
- public PromiseResult onReadRemoteRssi(BluetoothGatt gatt, int rssi) {
+ public SequenceResult onReadRemoteRssi(BluetoothGatt gatt, int rssi) {
throw new NotOverriddenException();
}
- public PromiseResult onDirect(BluetoothGatt value) {
+ public SequenceResult onDirect(BluetoothGatt value) {
throw new NotOverriddenException();
}
@@ -66,7 +67,7 @@ public class BtCallback {
@Override
public String toString() {
return "BtCallback{" +
- "name='" + name + '\'' +
+ "type='" + type + '\'' +
", stopOnFailure=" + stopOnFailure +
'}';
}
diff --git a/bt/src/main/java/io/trygvis/android/bt/BtSequence.java b/bt/src/main/java/io/trygvis/android/bt/BtSequence.java
new file mode 100644
index 0000000..be1be4c
--- /dev/null
+++ b/bt/src/main/java/io/trygvis/android/bt/BtSequence.java
@@ -0,0 +1,243 @@
+package io.trygvis.android.bt;
+
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+import io.trygvis.android.Consumer;
+import io.trygvis.android.F2;
+import io.trygvis.android.F3;
+import io.trygvis.android.Function;
+import io.trygvis.android.Optional;
+
+import static io.trygvis.android.bt.BtSequencer.EventType.onCharacteristicChanged;
+import static io.trygvis.android.bt.BtSequencer.EventType.onCharacteristicRead;
+import static io.trygvis.android.bt.BtSequencer.EventType.onCharacteristicWrite;
+import static io.trygvis.android.bt.BtSequencer.EventType.onConnectionStateChange;
+import static io.trygvis.android.bt.BtSequencer.EventType.onDescriptorRead;
+import static io.trygvis.android.bt.BtSequencer.EventType.onDescriptorWrite;
+import static io.trygvis.android.bt.BtSequencer.EventType.onDirect;
+import static io.trygvis.android.bt.BtSequencer.EventType.onFinally;
+import static io.trygvis.android.bt.BtSequencer.EventType.onReadRemoteRssi;
+import static io.trygvis.android.bt.BtSequencer.EventType.onReliableWriteCompleted;
+import static io.trygvis.android.bt.BtSequencer.EventType.onServicesDiscovered;
+import static java.util.Collections.unmodifiableList;
+
+public class BtSequence {
+ private static final SequenceResult waitForNextEvent = new WaitForNextEvent();
+ private static final SequenceResult stop = new Stop();
+ private static final SequenceResult fail = new Fail();
+ private static final SequenceResult continueDirectly = new ContinueDirectly();
+
+ public static class SequenceResult {
+ private SequenceResult() {
+ }
+
+ public static SequenceResult waitForNextEvent() {
+ return waitForNextEvent;
+ }
+
+ public static SequenceResult continueDirectly() {
+ return continueDirectly;
+ }
+
+ public static SequenceResult stop() {
+ return stop;
+ }
+
+ public static SequenceResult fail() {
+ return fail;
+ }
+
+ public static SequenceResult detour(BtSequence sequence) {
+ return new Detour(sequence);
+ }
+ }
+
+ public static class WaitForNextEvent extends SequenceResult {
+ }
+
+ public static class ContinueDirectly extends SequenceResult {
+ }
+
+ public static class Stop extends SequenceResult {
+ }
+
+ public static class Fail extends SequenceResult {
+ }
+
+ public static class Detour extends SequenceResult {
+ final BtSequence sequence;
+
+ private Detour(BtSequence sequence) {
+ this.sequence = sequence;
+ }
+ }
+
+ private final List<BtCallback> actionQ;
+ private final List<BtCallback> finallyQ;
+ private final Optional<BtSequence> next;
+
+ private Boolean stopOnFailure = true;
+
+ public BtSequence() {
+ actionQ = new ArrayList<>();
+ finallyQ = new ArrayList<>();
+ next = Optional.empty();
+ }
+
+ private BtSequence(List<BtCallback> actionQ, List<BtCallback> finallyQ, Optional<BtSequence> next) {
+ this.actionQ = actionQ;
+ this.finallyQ = finallyQ;
+ this.next = next;
+ }
+
+ public BtSequence andThen(BtSequence btSequence) {
+ return new BtSequence(actionQ, finallyQ, Optional.of(btSequence));
+ }
+
+ public ListIterator<BtCallback> actionQ() {
+ return actionQ.listIterator();
+ }
+
+ public List<BtCallback> finallyQ() {
+ return unmodifiableList(finallyQ);
+ }
+
+ public boolean firstIsOnDirect() {
+ return actionQ.size() > 0 && actionQ.get(0).type == onDirect;
+ }
+
+ public Optional<BtSequence> getNext() {
+ return next;
+ }
+
+ public String toString() {
+ return "actionQ=" + actionQ.size() + ", finallyQ=" + finallyQ.size() + ", has next=" + next.isPresent();
+ }
+
+ private BtSequence add(BtCallback callback) {
+ List<BtCallback> actions = new ArrayList<>(this.actionQ);
+ actions.add(callback);
+ return new BtSequence(actions, finallyQ, next);
+ }
+
+ private boolean stopOnFailure() {
+ if (stopOnFailure != null) {
+ boolean b = stopOnFailure;
+ stopOnFailure = null;
+ return b;
+ }
+ return false;
+ }
+
+ public BtSequence ignoreFailureForNext() {
+ stopOnFailure = true;
+ return this;
+ }
+
+ public BtSequence onConnectionStateChange(F3<BluetoothGatt, Integer, Integer, SequenceResult> callback) {
+ return add(new BtCallback(stopOnFailure(), onConnectionStateChange) {
+ @Override
+ public SequenceResult onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+ return callback.apply(gatt, status, newState);
+ }
+ });
+ }
+
+ public BtSequence onServicesDiscovered(Function<BluetoothGatt, SequenceResult> callback) {
+ return add(new BtCallback(stopOnFailure(), onServicesDiscovered) {
+ @Override
+ public SequenceResult onServicesDiscovered(BluetoothGatt gatt) {
+ return callback.apply(gatt);
+ }
+ });
+ }
+
+ public BtSequence onCharacteristicRead(F2<BluetoothGatt, BluetoothGattCharacteristic, SequenceResult> callback) {
+ return add(new BtCallback(stopOnFailure(), onCharacteristicRead) {
+ @Override
+ public SequenceResult onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ return callback.apply(gatt, characteristic);
+ }
+ });
+ }
+
+ public BtSequence onCharacteristicWrite(F2<BluetoothGatt, BluetoothGattCharacteristic, SequenceResult> callback) {
+ return add(new BtCallback(stopOnFailure(), onCharacteristicWrite) {
+ @Override
+ public SequenceResult onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ return callback.apply(gatt, characteristic);
+ }
+ });
+ }
+
+ public BtSequence onCharacteristicChanged(F2<BluetoothGatt, BluetoothGattCharacteristic, SequenceResult> callback) {
+ return add(new BtCallback(stopOnFailure(), onCharacteristicChanged) {
+ @Override
+ public SequenceResult onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ return callback.apply(gatt, characteristic);
+ }
+ });
+ }
+
+ public BtSequence onDescriptorRead(F2<BluetoothGatt, BluetoothGattDescriptor, SequenceResult> callback) {
+ return add(new BtCallback(stopOnFailure(), onDescriptorRead) {
+ @Override
+ public SequenceResult onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
+ return callback.apply(gatt, descriptor);
+ }
+ });
+ }
+
+ public BtSequence onDescriptorWrite(F2<BluetoothGatt, BluetoothGattDescriptor, SequenceResult> callback) {
+ return add(new BtCallback(stopOnFailure(), onDescriptorWrite) {
+ @Override
+ public SequenceResult onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
+ return callback.apply(gatt, descriptor);
+ }
+ });
+ }
+
+ public BtSequence onReliableWriteCompleted(Function<BluetoothGatt, SequenceResult> callback) {
+ return add(new BtCallback(stopOnFailure(), onReliableWriteCompleted) {
+ @Override
+ public SequenceResult onReliableWriteCompleted(BluetoothGatt gatt) {
+ return callback.apply(gatt);
+ }
+ });
+ }
+
+ public BtSequence onReadRemoteRssi(F2<BluetoothGatt, Integer, SequenceResult> callback) {
+ return add(new BtCallback(stopOnFailure(), onReadRemoteRssi) {
+ @Override
+ public SequenceResult onReadRemoteRssi(BluetoothGatt gatt, int rssi) {
+ return callback.apply(gatt, rssi);
+ }
+ });
+ }
+
+ public BtSequence onDirect(Function<BluetoothGatt, SequenceResult> callback) {
+ return add(new BtCallback(stopOnFailure(), onDirect) {
+ @Override
+ public SequenceResult onDirect(BluetoothGatt value) {
+ return callback.apply(value);
+ }
+ });
+ }
+
+ public BtSequence onFinally(Consumer<Boolean> callback) {
+ List<BtCallback> finallyQ = new ArrayList<>(this.finallyQ);
+ finallyQ.add(new BtCallback(stopOnFailure(), onFinally) {
+ @Override
+ public void onFinally(boolean success) {
+ callback.accept(success);
+ }
+ });
+ return new BtSequence(actionQ, finallyQ, next);
+ }
+}
diff --git a/bt/src/main/java/io/trygvis/android/bt/BtSequencer.java b/bt/src/main/java/io/trygvis/android/bt/BtSequencer.java
new file mode 100644
index 0000000..7eecbed
--- /dev/null
+++ b/bt/src/main/java/io/trygvis/android/bt/BtSequencer.java
@@ -0,0 +1,265 @@
+package io.trygvis.android.bt;
+
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.util.Log;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.List;
+
+import static io.trygvis.android.bt.BtSequence.ContinueDirectly;
+import static io.trygvis.android.bt.BtSequence.Detour;
+import static io.trygvis.android.bt.BtSequence.Fail;
+import static io.trygvis.android.bt.BtSequence.SequenceResult;
+import static io.trygvis.android.bt.BtSequence.Stop;
+import static io.trygvis.android.bt.BtSequencer.EventType.onDirect;
+import static io.trygvis.android.bt.BtSequencer.EventType.onFinally;
+
+class BtSequencer {
+ private final static String TAG = BtSequencer.class.getSimpleName();
+
+ enum EventType {
+ onConnectionStateChange,
+ onServicesDiscovered,
+ onCharacteristicRead,
+ onCharacteristicWrite,
+ onCharacteristicChanged,
+ onDescriptorRead,
+ onDescriptorWrite,
+ onReliableWriteCompleted,
+
+ onReadRemoteRssi,
+
+ onDirect,
+ onFailure,
+ onFinally,
+ }
+
+ private final String address;
+ private final Deque<BtSequence> sequences = new ArrayDeque<>();
+ private final Deque<Iterator<BtCallback>> iterators = new ArrayDeque<>();
+
+ private Iterator<BtCallback> callbacks;
+ private BtSequence sequence;
+ private List<String> events = new ArrayList<>();
+
+ private boolean finallyDone;
+
+ BtSequencer(String address, BtSequence sequence) {
+ this.address = address;
+ push(sequence);
+ }
+
+ private void pop() {
+ sequence = sequences.pop();
+ callbacks = iterators.pop();
+ }
+
+ private void push(BtSequence sequence) {
+ if (this.sequence != null) {
+ sequences.push(this.sequence);
+ iterators.push(this.callbacks);
+ }
+
+ this.sequence = sequence;
+ this.callbacks = sequence.actionQ();
+ }
+
+ public void onDirect() {
+ onEvent(onDirect, "initial onDirect", null, null, null, BluetoothGatt.GATT_SUCCESS,
+ BluetoothGatt.STATE_CONNECTED);
+ }
+
+ public synchronized void onEvent(EventType key, String values, BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor,
+ int status, int newState) {
+ if (finallyDone) {
+ Log.w(TAG, "Got event after finally has been executed: " + key + "(" + values + ").");
+ return;
+ }
+
+ boolean success = status == BluetoothGatt.GATT_SUCCESS;
+ events.add(key + "(" + values + "), success=" + success);
+
+ Log.i(TAG, "event: " + key + "(" + values + "), success=" + success);
+
+ BtCallback callback;
+ synchronized (this) {
+ if (!callbacks.hasNext() && sequence.getNext().isPresent()) {
+ Log.i(TAG, "Switching to next sequence");
+
+ doFinally(success, sequence);
+
+ sequence = sequence.getNext().get();
+ callbacks = sequence.actionQ();
+ }
+
+ if (!callbacks.hasNext()) {
+ if (!sequences.isEmpty()) {
+ Log.d(TAG, "Sequence is done, continuing on previous sequence");
+ doFinally(true, sequence);
+ pop();
+ } else {
+ Log.d(TAG, "Sequence is done, no more sequences");
+ doFinally(true);
+ return;
+ }
+ }
+
+ callback = callbacks.next();
+
+ if (!success) {
+ if (callback.stopOnFailure) {
+ doFinally(false);
+ return;
+ } else {
+ Log.i(TAG, "Last status was a failure, but the callback still want it.");
+ }
+ }
+ }
+
+ try {
+ Log.i(TAG, "Executing bt action: " + callback.type);
+ SequenceResult result = callCallback(key, gatt, characteristic, descriptor, status, newState, null,
+ callback);
+
+ if (result instanceof Stop) {
+ Log.i(TAG, "The sequence stopped.");
+ doFinally(true);
+ return;
+ }
+
+ if (result instanceof Fail) {
+ Log.i(TAG, "The sequence failed.");
+ doFinally(false);
+ return;
+ }
+
+ if (result instanceof Detour) {
+ BtSequence detour = ((Detour) result).sequence;
+ Log.i(TAG, "Adding detour: " + detour);
+
+ if (!detour.firstIsOnDirect()) {
+ throw new IllegalArgumentException("The first handler in a detour must be an onDirect.");
+ }
+
+ events.add("detour, " + detour);
+
+ push(detour);
+ onEvent(onDirect, "direct", gatt, null, null, BluetoothGatt.GATT_SUCCESS, 0);
+ return;
+ }
+
+ if (result instanceof ContinueDirectly) {
+ onEvent(onDirect, "direct", gatt, null, null, BluetoothGatt.GATT_SUCCESS, 0);
+ return;
+ }
+
+ if (!callbacks.hasNext()) {
+ if (!sequences.isEmpty()) {
+ Log.d(TAG, "Sequence is done, continuing on previous sequence");
+ doFinally(true, sequence);
+ pop();
+ } else {
+ Log.d(TAG, "Sequence is done, no more sequences");
+ doFinally(true);
+ }
+ }
+ } catch (NotOverriddenException e) {
+ Log.w(TAG, "Unexpected callback by listener: " + key);
+// doFailure();
+ doFinally(false);
+ } catch (Exception e) {
+ Log.w(TAG, "Exception in callback", e);
+// doFailure();
+ doFinally(false);
+ }
+ }
+
+ private void doFinally(boolean success) {
+ finallyDone = true;
+ showEvents();
+
+ doFinally(success, sequence);
+
+ while (!sequences.isEmpty()) {
+ doFinally(success, sequences.pop());
+ }
+ }
+
+ private void doFinally(boolean success, BtSequence s) {
+ List<BtCallback> q = s.finallyQ();
+
+ Log.w(TAG, "Executing " + q.size() + " finally handlers, success=" + success);
+ for (BtCallback callback : q) {
+ try {
+ callCallback(onFinally, null, null, null, 0, 0, success, callback);
+ } catch (NotOverriddenException e) {
+ // ignore
+ }
+ }
+ }
+
+ 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("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");
+ }
+
+ Log.w(TAG, msg.toString());
+ }
+
+ private static SequenceResult callCallback(EventType key, BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic,
+ BluetoothGattDescriptor descriptor, int status, int newState,
+ Boolean success, BtCallback btCallback) {
+ switch (key) {
+ case onConnectionStateChange:
+ return btCallback.onConnectionStateChange(gatt, status, 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 onDirect:
+ return btCallback.onDirect(gatt);
+ case onFailure:
+ btCallback.onFailure();
+ return null;
+ case onFinally:
+ btCallback.onFinally(success);
+ return null;
+ default:
+ Log.w(TAG, "Unknown callback: " + key);
+ return null;
+ }
+ }
+}
diff --git a/app/src/main/java/io/trygvis/android/bt/NotOverriddenException.java b/bt/src/main/java/io/trygvis/android/bt/NotOverriddenException.java
index 0f2bb6a..0f2bb6a 100644
--- a/app/src/main/java/io/trygvis/android/bt/NotOverriddenException.java
+++ b/bt/src/main/java/io/trygvis/android/bt/NotOverriddenException.java
diff --git a/bt/src/test/java/io/trygvis/android/bt/BtSequencerTest.java b/bt/src/test/java/io/trygvis/android/bt/BtSequencerTest.java
new file mode 100644
index 0000000..862eda5
--- /dev/null
+++ b/bt/src/test/java/io/trygvis/android/bt/BtSequencerTest.java
@@ -0,0 +1,96 @@
+package io.trygvis.android.bt;
+
+import android.bluetooth.BluetoothGatt;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static io.trygvis.android.bt.BtSequence.SequenceResult.continueDirectly;
+import static io.trygvis.android.bt.BtSequence.SequenceResult.detour;
+import static io.trygvis.android.bt.BtSequence.SequenceResult.stop;
+import static org.fest.assertions.Assertions.assertThat;
+
+@RunWith(JUnit4.class)
+public class BtSequencerTest {
+
+ public static final String ADDRESS = "AA:BB:CC:DD:EE:FF";
+
+ @Test
+ public void eventAndFinally() {
+ List<String> events = new ArrayList<>();
+
+ BtSequence seq = new BtSequence().onConnectionStateChange((gatt, newState, status) -> {
+ events.add("onConnectionStateChange");
+ return stop();
+ }).onFinally(success -> events.add("finally: " + success));
+
+ BtSequencer sequencer = new BtSequencer(ADDRESS, seq);
+
+ sequencer.onEvent(BtSequencer.EventType.onConnectionStateChange, null, null, null, null,
+ BluetoothGatt.GATT_SUCCESS, BluetoothGatt.STATE_CONNECTED);
+
+ assertThat(events).containsSequence(
+ "onConnectionStateChange",
+ "finally: true"
+ );
+ }
+
+ @Test
+ public void detours() {
+ List<String> events = new ArrayList<>();
+
+ BtSequence tour = new BtSequence().onDirect(gatt -> {
+ events.add("tour: onDirect");
+ return stop();
+ }).onFinally(success -> events.add("tour: finally: " + success));
+
+ BtSequence seq = new BtSequence().onConnectionStateChange((gatt, newState, status) -> {
+ events.add("onConnectionStateChange");
+ return detour(tour.andThen(tour));
+ }).onFinally(success -> events.add("finally: " + success));
+
+ BtSequencer sequencer = new BtSequencer(ADDRESS, seq);
+
+ sequencer.onEvent(BtSequencer.EventType.onConnectionStateChange, null, null, null, null,
+ BluetoothGatt.GATT_SUCCESS, BluetoothGatt.STATE_CONNECTED);
+
+ assertThat(events).containsSequence(
+ "onConnectionStateChange",
+ "tour: onDirect",
+ "tour: finally: true",
+ "finally: true"
+ );
+ }
+
+ @Test
+ public void andThen() {
+ List<String> events = new ArrayList<>();
+
+ BtSequence a = new BtSequence().onConnectionStateChange((gatt, newState, status) -> {
+ events.add("a");
+ return continueDirectly();
+ }).onFinally(success -> events.add("a: finally: " + success));
+ BtSequence b = new BtSequence().onDirect(gatt -> {
+ events.add("b");
+ return stop();
+ }).onFinally(success -> events.add("b: finally: " + success));
+
+ BtSequence seq = a.andThen(b);
+
+ BtSequencer sequencer = new BtSequencer(ADDRESS, seq);
+
+ sequencer.onEvent(BtSequencer.EventType.onConnectionStateChange, null, null, null, null,
+ BluetoothGatt.GATT_SUCCESS, BluetoothGatt.STATE_CONNECTED);
+
+ assertThat(events).containsSequence(
+ "a",
+ "a: finally: true",
+ "b",
+ "b: finally: true"
+ );
+ }
+}
diff --git a/build.gradle b/build.gradle
index 234dff0..fae46cd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,9 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.0.0-rc4'
+ classpath 'com.android.tools.build:gradle:1.0.1'
+// classpath 'org.robolectric:robolectric-gradle-plugin:0.14.1'
+ classpath 'com.github.jcandksolutions.gradle:android-unit-test:2.1.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/settings.gradle b/settings.gradle
index e7b4def..1d1e858 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app'
+include ':app', ':bt', ':trygvis-io-bt-stubs'
diff --git a/trygvis-io-bt-stubs/build.gradle b/trygvis-io-bt-stubs/build.gradle
new file mode 100644
index 0000000..c152b19
--- /dev/null
+++ b/trygvis-io-bt-stubs/build.gradle
@@ -0,0 +1,5 @@
+apply plugin: 'java'
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+} \ No newline at end of file
diff --git a/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGatt.java b/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGatt.java
new file mode 100644
index 0000000..13b33ed
--- /dev/null
+++ b/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGatt.java
@@ -0,0 +1,32 @@
+package android.bluetooth;
+
+import java.util.List;
+import java.util.UUID;
+
+public class BluetoothGatt {
+ public static final int GATT_SUCCESS = 1;
+ public static final int STATE_CONNECTED = 2;
+
+ public boolean discoverServices() {
+ return false;
+ }
+
+ public List getServices() {
+ return null;
+ }
+
+ public BluetoothGattService getService(UUID uuid) {
+ return null;
+ }
+
+ public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
+ return false;
+ }
+
+ public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean b) {
+ }
+
+ public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
+ return false;
+ }
+}
diff --git a/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattCallback.java b/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattCallback.java
new file mode 100644
index 0000000..a2a2943
--- /dev/null
+++ b/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattCallback.java
@@ -0,0 +1,31 @@
+package android.bluetooth;
+
+public class BluetoothGattCallback {
+
+ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+ }
+
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ }
+
+ public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ }
+
+ public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ }
+
+ public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ }
+
+ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ }
+
+ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ }
+
+ public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
+ }
+
+ public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+ }
+}
diff --git a/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattCharacteristic.java b/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattCharacteristic.java
new file mode 100644
index 0000000..8398b5a
--- /dev/null
+++ b/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -0,0 +1,13 @@
+package android.bluetooth;
+
+import java.util.UUID;
+
+public class BluetoothGattCharacteristic {
+ public UUID getUuid() {
+ return null;
+ }
+
+ public byte[] getValue() {
+ return null;
+ }
+}
diff --git a/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattDescriptor.java b/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattDescriptor.java
new file mode 100644
index 0000000..0a86c7e
--- /dev/null
+++ b/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -0,0 +1,13 @@
+package android.bluetooth;
+
+import java.util.UUID;
+
+public class BluetoothGattDescriptor {
+ public UUID getUuid() {
+ return null;
+ }
+
+ public BluetoothGattCharacteristic getCharacteristic() {
+ return null;
+ }
+}
diff --git a/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattService.java b/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattService.java
new file mode 100644
index 0000000..cd2a57f
--- /dev/null
+++ b/trygvis-io-bt-stubs/src/main/java/android/bluetooth/BluetoothGattService.java
@@ -0,0 +1,4 @@
+package android.bluetooth;
+
+public class BluetoothGattService {
+}
diff --git a/trygvis-io-bt-stubs/src/main/java/android/util/Log.java b/trygvis-io-bt-stubs/src/main/java/android/util/Log.java
new file mode 100644
index 0000000..036ce22
--- /dev/null
+++ b/trygvis-io-bt-stubs/src/main/java/android/util/Log.java
@@ -0,0 +1,20 @@
+package android.util;
+
+public class Log {
+ public static void d(String tag, String s) {
+ System.out.println("d: " + tag + ": " + s);
+ }
+
+ public static void i(String tag, String s) {
+ System.out.println("i: " + tag + ": " + s);
+ }
+
+ public static void w(String tag, String s) {
+ System.out.println("w: " + tag + ": " + s);
+ }
+
+ public static void w(String tag, String s, Exception e) {
+ System.out.println("w: " + tag + ": " + s);
+ e.printStackTrace(System.out);
+ }
+}