summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2015-01-17 18:15:52 +0100
committerTrygve Laugstøl <trygvis@inamo.no>2015-01-17 18:15:52 +0100
commitbc09707edf110da018a68be4d16bdfcee8af600f (patch)
tree1f8a6032244f2384372f2c37e220f6946aa4e483
parent5f880d3816526157c5d411896707b971af48212b (diff)
downloadio.trygvis.soilmoisture-android-bc09707edf110da018a68be4d16bdfcee8af600f.tar.gz
io.trygvis.soilmoisture-android-bc09707edf110da018a68be4d16bdfcee8af600f.tar.bz2
io.trygvis.soilmoisture-android-bc09707edf110da018a68be4d16bdfcee8af600f.tar.xz
io.trygvis.soilmoisture-android-bc09707edf110da018a68be4d16bdfcee8af600f.zip
o Not disconnecting when the promise is done.
o All user code can assume that they're already connected to the device, simplifying the life cycle. o When starting, send a 'new device' even for all stored devices.
-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
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java137
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/MainActivity.java106
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/SmDevice.java2
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java2
-rw-r--r--app/src/main/res/values/strings.xml3
9 files changed, 347 insertions, 154 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;
}
diff --git a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
index 174c245..56f38f4 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
@@ -112,64 +112,90 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
sendBroadcast(createNewDevice(address));
+ boolean candidate = btDevice.getAddress().startsWith("FB:");
+
+ if (!candidate) {
+ Log.w(TAG, "Skipping device: " + btDevice.getAddress());
+ markDeviceAsNotUseful(smDevice);
+ return;
+ }
+
if (!smDevice.isProbed()) {
- Log.i(TAG, "Probing " + address + ", name=" + btDevice.getName());
- BtPromise executor = new BtPromise().
- onDirect(v -> {
- BluetoothGatt gatt = (BluetoothGatt) v;
- return gatt.discoverServices() ? waitForNextEvent() : stop();
- }).
- onServicesDiscovered(gatt -> {
- Log.i(TAG, "Services discovered");
-
- BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE);
-
- if (service == null) {
- return stop();
- }
-
- BluetoothGattCharacteristic soilMoisture = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE);
-
- if (soilMoisture == null) {
- return stop();
- }
-
- BluetoothGattDescriptor ccg = soilMoisture.getDescriptor(TrygvisIoUuids.CLIENT_CHARACTERISTIC_CONFIG);
- ccg.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
- gatt.setCharacteristicNotification(soilMoisture, true);
-
- return gatt.writeDescriptor(ccg) ? waitForNextEvent() : stop();
- }).
- onDescriptorWrite((gatt, descriptor) -> {
- Log.i(TAG, "Notifications enabled, getting sensor count");
- BluetoothGattCharacteristic c = descriptor.getCharacteristic();
- c.setValue(createGetSensorCountReq());
- return gatt.writeCharacteristic(c) ? waitForNextEvent() : stop();
- }).
- onCharacteristicWrite((gatt, characteristic) -> waitForNextEvent()).
- onCharacteristicChanged((gatt, characteristic) -> {
- GetSensorCountRes getSensorCountRes = parseResponse(characteristic.getValue(),
- GET_SENSOR_COUNT, GetSensorCountRes.class);
-
- Log.i(TAG, "The device has " + getSensorCountRes.count + " sensors.");
-
- markDeviceAsUseful(smDevice, getSensorCountRes.count);
-
- return stop();
- }).
- onFinally(() -> {
-// btDevice.disconnect();
-
- if (smDevice.getIsUseful() == null) {
- smDevice.setIsUseful(false);
- }
- });
-
- btDevice.withConnection(executor);
+ probe(smDevice.getBtDevice().getAddress());
}
}
};
+ public void probe(String address) {
+ BtDevice<SmDevice> btDevice = btService.getDevice(address);
+ SmDevice smDevice = btDevice.getTag();
+
+ Log.i(TAG, "Probing " + address + ", name=" + btDevice.getName());
+ BtPromise executor = new BtPromise().
+ onDirect(v -> {
+ Log.i(TAG, "Discovering services");
+ BluetoothGatt gatt = (BluetoothGatt) v;
+ return gatt.discoverServices() ? waitForNextEvent() : stop();
+ }).
+ onServicesDiscovered(gatt -> {
+ Log.i(TAG, "Services discovered");
+
+ BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE);
+
+ if (service == null) {
+ return stop();
+ }
+
+ BluetoothGattCharacteristic soilMoisture = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE);
+
+ if (soilMoisture == null) {
+ return stop();
+ }
+
+ BluetoothGattDescriptor ccg = soilMoisture.getDescriptor(TrygvisIoUuids.CLIENT_CHARACTERISTIC_CONFIG);
+ ccg.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
+ gatt.setCharacteristicNotification(soilMoisture, true);
+
+ return gatt.writeDescriptor(ccg) ? waitForNextEvent() : stop();
+ }).
+ onDescriptorWrite((gatt, descriptor) -> {
+ Log.i(TAG, "Notifications enabled, getting sensor count");
+ BluetoothGattCharacteristic c = descriptor.getCharacteristic();
+ c.setValue(createGetSensorCountReq());
+ return gatt.writeCharacteristic(c) ? waitForNextEvent() : stop();
+ }).
+ onCharacteristicWrite((gatt, characteristic) -> waitForNextEvent()).
+ onCharacteristicChanged((gatt, characteristic) -> {
+ GetSensorCountRes getSensorCountRes = parseResponse(characteristic.getValue(),
+ GET_SENSOR_COUNT, GetSensorCountRes.class);
+
+ Log.i(TAG, "The device has " + getSensorCountRes.count + " sensors.");
+
+ markDeviceAsUseful(smDevice, getSensorCountRes.count);
+
+ return stop();
+ }).
+ onFinally(() -> {
+ if (smDevice.getIsUseful() == null) {
+ smDevice.setIsUseful(false);
+ }
+ });
+
+ btDevice.withConnection(executor);
+ }
+
+ private void markDeviceAsNotUseful(SmDevice device) {
+ btService.runTx(db -> {
+ device.setIsUseful(false);
+
+ ContentValues values = new ContentValues();
+ values.put(Tables.C_USEFUL, false);
+ db.update(Tables.T_SM_DEVICE, values, "id=?", new String[]{valueOf(device.id)});
+
+ return null;
+ });
+ }
+
private void markDeviceAsUseful(SmDevice device, int sensorCount) {
btService.runTx(db -> {
device.setIsUseful(true);
@@ -274,9 +300,6 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
BtPromise promise = new BtPromise().
onDirect(v -> {
BluetoothGatt gatt = (BluetoothGatt) v;
- return gatt.discoverServices() ? waitForNextEvent() : stop();
- }).
- onServicesDiscovered(gatt -> {
BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE);
BluetoothGattCharacteristic soilMoisture = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE);
@@ -298,8 +321,6 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
handleNewSensorValueReady(sensor, getSensorCountRes.value);
-// gatt.disconnect();
-
return stop();
}).
onFinally(() -> {
diff --git a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java
index 3a88f6c..385a566 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java
@@ -1,6 +1,10 @@
package io.trygvis.soilmoisture;
import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.ComponentName;
@@ -29,6 +33,7 @@ import java.util.List;
import io.fabric.sdk.android.Fabric;
import io.trygvis.android.LocalBinder;
import io.trygvis.android.bt.BtActivitySupport;
+import io.trygvis.android.bt.BtDevice;
import static io.trygvis.soilmoisture.ExceptionHandler.EXCEPTION_HANDLER;
import static io.trygvis.soilmoisture.SoilMoistureService.SoilMoistureListener;
@@ -211,8 +216,52 @@ public class MainActivity extends ListActivity {
//
// -----------------------------------------------------------------------
- private void onDeviceClick(SmDevice device) {
- Log.i(TAG, "onDeviceClick, device=" + device.getBtDevice().getId());
+ public static class BtDeviceDialogFragment extends DialogFragment {
+
+ private MainActivity mainActivity;
+// private final BtDevice<SmDevice> device;
+//
+// public BtDeviceDialogFragment(MainActivity mainActivity, BtDevice<SmDevice> device) {
+// this.mainActivity = mainActivity;
+// this.device = device;
+// }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+
+ String address = getArguments().getString("address");
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(R.string.dialog_probe_device).
+ setPositiveButton(R.string.yes, (dialog, id) -> mainActivity.probe(address));
+ return builder.create();
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ mainActivity = (MainActivity) activity;
+ }
+ }
+
+ private void probe(String address) {
+ soilMoistureService.probe(address);
+ }
+
+ private boolean onBtDeviceLongClick(BtDevice<SmDevice> device) {
+ Log.i(TAG, "onBtDeviceClick, device=" + device.getId());
+
+ BtDeviceDialogFragment dialog = new BtDeviceDialogFragment();
+ Bundle arguments = new Bundle();
+ arguments.putString("address", device.getAddress());
+ dialog.setArguments(arguments);
+ dialog.show(getFragmentManager(), "BtDeviceDialogFragment");
+
+ return true;
+ }
+
+ private void onSmDeviceClick(SmDevice device) {
+ Log.i(TAG, "onSmDeviceClick, device=" + device.getBtDevice().getId());
}
private void onSensorClick(SmSensor sensor) {
@@ -266,9 +315,13 @@ public class MainActivity extends ListActivity {
current = new ArrayList<>();
List<SmDevice> usefulDevices = new ArrayList<>(devices.size());
- List<SmDevice> unusefulDevices = new ArrayList<>(devices.size());
+ List<BtDevice> unusefulDevices = new ArrayList<>(devices.size());
for (SmDevice d : devices) {
- (d.isUseful() ? usefulDevices : unusefulDevices).add(d);
+ if (d.isUseful()) {
+ usefulDevices.add(d);
+ } else {
+ unusefulDevices.add(d.getBtDevice());
+ }
}
List<SmSensor> sensors = new ArrayList<>();
for (SmDevice d : devices) {
@@ -355,10 +408,12 @@ public class MainActivity extends ListActivity {
public int getItemViewType(int position) {
Object o = current.get(position);
- if (o instanceof SmDevice) {
+ if (o instanceof BtDevice) {
return 0;
- } else if (o instanceof SmSensor) {
+ } else if (o instanceof SmDevice) {
return 1;
+ } else if (o instanceof SmSensor) {
+ return 2;
}
throw new RuntimeException("Unknown kind: " + o.getClass());
@@ -366,7 +421,7 @@ public class MainActivity extends ListActivity {
@Override
public int getViewTypeCount() {
- return 2;
+ return 3;
}
@Override
@@ -393,7 +448,10 @@ public class MainActivity extends ListActivity {
public View getView(int position, View view, ViewGroup viewGroup) {
Object o = current.get(position);
- if (o instanceof SmDevice) {
+ if (o instanceof BtDevice) {
+ //noinspection unchecked
+ return getBtDeviceView((BtDevice<SmDevice>) o, view);
+ } else if (o instanceof SmDevice) {
return getSmDeviceView((SmDevice) o, view);
} else if (o instanceof SmSensor) {
return getSoilSensorView((SmSensor) o, view);
@@ -402,12 +460,38 @@ public class MainActivity extends ListActivity {
throw new RuntimeException("Not implemented");
}
+ private View getBtDeviceView(BtDevice<SmDevice> device, View view) {
+ if (view == null) {
+ view = inflater.inflate(R.layout.fragment_device, null);
+ view.setTag(new DeviceItem(view));
+ view.setClickable(false);
+ view.setLongClickable(true);
+ view.setOnLongClickListener(v -> MainActivity.this.onBtDeviceLongClick(device));
+ }
+
+ DeviceItem item = (DeviceItem) view.getTag();
+
+ if (device.getName() != null) {
+ item.deviceName.setText(device.getName());
+ } else {
+ item.deviceName.setText(R.string.unknown_device);
+ }
+ String address = device.getAddress() + ", not useful";
+
+ item.deviceAddress.setText(address);
+
+ item.rssi.setText(getText(R.string.rssi) + ": " +
+ (device.getRssi() != null ? valueOf(device.getRssi()) : getText(R.string.unknown)));
+
+ return view;
+ }
+
private View getSmDeviceView(SmDevice smDevice, View view) {
if (view == null) {
view = inflater.inflate(R.layout.fragment_device, null);
view.setTag(new DeviceItem(view));
view.setClickable(true);
- view.setOnClickListener(v -> onDeviceClick(smDevice));
+ view.setOnClickListener(v -> onSmDeviceClick(smDevice));
}
DeviceItem item = (DeviceItem) view.getTag();
@@ -430,7 +514,7 @@ public class MainActivity extends ListActivity {
item.deviceAddress.setText(address);
item.rssi.setText(getText(R.string.rssi) + ": " +
- (smDevice.getBtDevice().getRssi() != 0 ? valueOf(smDevice.getBtDevice().getRssi()) : getText(R.string.unknown)));
+ (smDevice.getBtDevice().getRssi() != null ? valueOf(smDevice.getBtDevice().getRssi()) : getText(R.string.unknown)));
boolean useful = smDevice.isUseful();
@@ -440,8 +524,6 @@ public class MainActivity extends ListActivity {
item.info.setText("");
}
-// view.setClickable(useful);
-
return view;
}
diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java
index 4cb25f3..5d439bc 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java
@@ -15,7 +15,7 @@ import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.GET_WARNING_VALUE;
import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.SET_SENSOR_NAME;
import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.SET_WARNING_VALUE;
-class SmDevice implements BtDevice.BtDeviceWrapper<SmDevice> {
+class SmDevice {
private final static String TAG = SmDevice.class.getSimpleName();
final DefaultSoilMoistureService smService;
diff --git a/app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java b/app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java
index 643fa08..a287c7b 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java
@@ -13,6 +13,8 @@ public interface SoilMoistureService {
SmDevice getDevice(String address);
+ void probe(String address);
+
boolean isScanning();
boolean startScanning(long scanPeriod);
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0764a26..2dc3820 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -33,5 +33,8 @@
<string name="current_value">Current value</string>
<string name="show_all">Show all</string>
<string name="group_by_device">Group by device</string>
+ <string name="dialog_probe_device">Probe device?</string>
+ <string name="yes">Yes</string>
+ <string name="no">No</string>
</resources>