diff options
author | Trygve Laugstøl <trygvis@inamo.no> | 2015-01-17 18:15:52 +0100 |
---|---|---|
committer | Trygve Laugstøl <trygvis@inamo.no> | 2015-01-17 18:15:52 +0100 |
commit | bc09707edf110da018a68be4d16bdfcee8af600f (patch) | |
tree | 1f8a6032244f2384372f2c37e220f6946aa4e483 | |
parent | 5f880d3816526157c5d411896707b971af48212b (diff) | |
download | io.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.
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> |