diff options
author | Trygve Laugstøl <trygvis@inamo.no> | 2015-01-02 21:38:52 +0100 |
---|---|---|
committer | Trygve Laugstøl <trygvis@inamo.no> | 2015-01-02 21:38:52 +0100 |
commit | da80f3d219c0c05568db0cb9a8910f02cc281d47 (patch) | |
tree | f62e2c251a03e2a33a7733f1e5d1829a40bdb1ba | |
parent | ed559834ccddafa955df5b528f08fba964e57699 (diff) | |
download | io.trygvis.soilmoisture-android-da80f3d219c0c05568db0cb9a8910f02cc281d47.tar.gz io.trygvis.soilmoisture-android-da80f3d219c0c05568db0cb9a8910f02cc281d47.tar.bz2 io.trygvis.soilmoisture-android-da80f3d219c0c05568db0cb9a8910f02cc281d47.tar.xz io.trygvis.soilmoisture-android-da80f3d219c0c05568db0cb9a8910f02cc281d47.zip |
o Getting closer to something that actually work.
20 files changed, 749 insertions, 424 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c04494d..85d6d39 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -41,9 +41,16 @@ android:name="io.trygvis.android.bt.DefaultBtService" android:enabled="true"/> + <service + android:name=".DefaultSmDevicesManager" + android:enabled="true" + android:exported="false"> + </service> + <meta-data android:name="com.crashlytics.ApiKey" android:value="cf760ececcb6d74c66781b3e21ae115aaae3ffd3"/> + </application> </manifest> diff --git a/app/src/main/java/io/trygvis/android/F2.java b/app/src/main/java/io/trygvis/android/F2.java new file mode 100644 index 0000000..1f362cc --- /dev/null +++ b/app/src/main/java/io/trygvis/android/F2.java @@ -0,0 +1,5 @@ +package io.trygvis.android; + +public interface F2<A, B, C> { + C apply(A a, B b); +} diff --git a/app/src/main/java/io/trygvis/android/F3.java b/app/src/main/java/io/trygvis/android/F3.java new file mode 100644 index 0000000..30d3adf --- /dev/null +++ b/app/src/main/java/io/trygvis/android/F3.java @@ -0,0 +1,5 @@ +package io.trygvis.android; + +public interface F3<A, B, C, D> { + D apply(A a, B b, C c); +} diff --git a/app/src/main/java/io/trygvis/android/F4.java b/app/src/main/java/io/trygvis/android/F4.java new file mode 100644 index 0000000..5ff50f5 --- /dev/null +++ b/app/src/main/java/io/trygvis/android/F4.java @@ -0,0 +1,5 @@ +package io.trygvis.android; + +public interface F4<A, B, C, D, E> { + E apply(A a, B b, C c, D d); +} diff --git a/app/src/main/java/io/trygvis/android/Function.java b/app/src/main/java/io/trygvis/android/Function.java new file mode 100644 index 0000000..5e3bb96 --- /dev/null +++ b/app/src/main/java/io/trygvis/android/Function.java @@ -0,0 +1,5 @@ +package io.trygvis.android; + +public interface Function<A, B> { + B apply(A a); +} diff --git a/app/src/main/java/io/trygvis/android/LocalBinder.java b/app/src/main/java/io/trygvis/android/LocalBinder.java new file mode 100644 index 0000000..62d48fd --- /dev/null +++ b/app/src/main/java/io/trygvis/android/LocalBinder.java @@ -0,0 +1,15 @@ +package io.trygvis.android; + +import android.os.Binder; + +public class LocalBinder<A> extends Binder { + private final A service; + + public LocalBinder(A service) { + this.service = service; + } + + public A getService() { + return service; + } +} diff --git a/app/src/main/java/io/trygvis/android/Supplier.java b/app/src/main/java/io/trygvis/android/Supplier.java new file mode 100644 index 0000000..222dff2 --- /dev/null +++ b/app/src/main/java/io/trygvis/android/Supplier.java @@ -0,0 +1,5 @@ +package io.trygvis.android; + +public interface Supplier<A> { + A get(); +} diff --git a/app/src/main/java/io/trygvis/android/bt/BtActionExecutor.java b/app/src/main/java/io/trygvis/android/bt/BtActionExecutor.java index 8cbf87c..984c738 100644 --- a/app/src/main/java/io/trygvis/android/bt/BtActionExecutor.java +++ b/app/src/main/java/io/trygvis/android/bt/BtActionExecutor.java @@ -12,6 +12,9 @@ import java.util.Iterator; import java.util.List; import java.util.Queue; +import io.trygvis.android.F2; +import io.trygvis.android.Function; + import static io.trygvis.android.bt.BtActionExecutor.EventType.onCharacteristicChanged; import static io.trygvis.android.bt.BtActionExecutor.EventType.onCharacteristicRead; import static io.trygvis.android.bt.BtActionExecutor.EventType.onCharacteristicWrite; @@ -19,239 +22,146 @@ import static io.trygvis.android.bt.BtActionExecutor.EventType.onConnectionState import static io.trygvis.android.bt.BtActionExecutor.EventType.onDescriptorRead; import static io.trygvis.android.bt.BtActionExecutor.EventType.onDescriptorWrite; import static io.trygvis.android.bt.BtActionExecutor.EventType.onFailure; +import static io.trygvis.android.bt.BtActionExecutor.EventType.onFinally; import static io.trygvis.android.bt.BtActionExecutor.EventType.onReliableWriteCompleted; import static io.trygvis.android.bt.BtActionExecutor.EventType.onServicesDiscovered; public class BtActionExecutor { private final static String TAG = BtActionExecutor.class.getSimpleName(); private final Queue<BtCallback> actionQ = new ArrayDeque<>(); + private final Queue<BtCallback> failureQ = new ArrayDeque<>(); private final Queue<BtCallback> finallyQ = new ArrayDeque<>(); - private final List<BluetoothGattCallback> remoteRssi = new ArrayList<>(); - - public BtActionExecutor() { - } - - public BtActionExecutor(BtCallback first) { - actionQ.add(first); - } -// public static final BtCallback waitForIt = new BtCallback("Wait for it") { -// @Override -// public boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { -// Log.w(TAG, "wait for it...!"); -// return true; -// } -// }; - -// public synchronized BtActionExecutor onSuccess(BtCallback btCallback) { -// actionQ.add(btCallback); -// return this; -// } - - public synchronized BtActionExecutor onConnectionStateChange(OnConnectionStateChange callback) { + public synchronized BtActionExecutor onConnectionStateChange(F2<BluetoothGatt, Integer, Boolean> callback) { actionQ.add(new BtCallback("onConnectionStateChange") { @Override public boolean onConnectionStateChange(BluetoothGatt gatt, int newState) { - return callback.onConnectionStateChange(gatt, newState); + return callback.apply(gatt, newState); } }); return this; } - public synchronized BtActionExecutor onServicesDiscovered(OnServicesDiscovered callback) { + public synchronized BtActionExecutor onServicesDiscovered(Function<BluetoothGatt, Boolean> callback) { actionQ.add(new BtCallback("onServicesDiscovered") { @Override public boolean onServicesDiscovered(BluetoothGatt gatt) { - return callback.onServicesDiscovered(gatt); + return callback.apply(gatt); } }); return this; } - public synchronized BtActionExecutor onCharacteristicRead(OnCharacteristicRead callback) { + public synchronized BtActionExecutor onCharacteristicRead(F2<BluetoothGatt, BluetoothGattCharacteristic, Boolean> callback) { actionQ.add(new BtCallback("onCharacteristicRead") { @Override public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - return callback.onCharacteristicRead(gatt, characteristic); + return callback.apply(gatt, characteristic); } }); return this; } - public synchronized BtActionExecutor onCharacteristicWrite(OnCharacteristicWrite callback) { + public synchronized BtActionExecutor onCharacteristicWrite(F2<BluetoothGatt, BluetoothGattCharacteristic, Boolean> callback) { actionQ.add(new BtCallback("onCharacteristicWrite") { @Override public boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - return callback.onCharacteristicWrite(gatt, characteristic); + return callback.apply(gatt, characteristic); } }); return this; } - public synchronized BtActionExecutor onCharacteristicChanged(OnCharacteristicChanged callback) { + public synchronized BtActionExecutor onCharacteristicChanged(F2<BluetoothGatt, BluetoothGattCharacteristic, Boolean> callback) { actionQ.add(new BtCallback("onCharacteristicChanged") { @Override public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - return callback.onCharacteristicChanged(gatt, characteristic); + return callback.apply(gatt, characteristic); } }); return this; } - public synchronized BtActionExecutor onDescriptorRead(OnDescriptorRead callback) { + public synchronized BtActionExecutor onDescriptorRead(F2<BluetoothGatt, BluetoothGattDescriptor, Boolean> callback) { actionQ.add(new BtCallback("onDescriptorRead") { @Override public boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) { - return callback.onDescriptorRead(gatt, descriptor); + return callback.apply(gatt, descriptor); } }); return this; } - public synchronized BtActionExecutor onDescriptorWrite(OnDescriptorWrite callback) { + public synchronized BtActionExecutor onDescriptorWrite(F2<BluetoothGatt, BluetoothGattDescriptor, Boolean> callback) { actionQ.add(new BtCallback("onDescriptorWrite") { @Override public boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) { - return callback.onDescriptorWrite(gatt, descriptor); + return callback.apply(gatt, descriptor); } }); return this; } - public synchronized BtActionExecutor onReliableWriteCompleted(OnReliableWriteCompleted callback) { + public synchronized BtActionExecutor onReliableWriteCompleted(Function<BluetoothGatt, Boolean> callback) { actionQ.add(new BtCallback("onReliableWriteCompleted") { @Override public boolean onReliableWriteCompleted(BluetoothGatt gatt) { - return callback.onReliableWriteCompleted(gatt); + return callback.apply(gatt); } }); return this; } - public synchronized BtActionExecutor onReadRemoteRssi(OnReadRemoteRssi callback) { + public synchronized BtActionExecutor onReadRemoteRssi(F2<BluetoothGatt, Integer, Boolean> callback) { actionQ.add(new BtCallback("onReadRemoteRssi") { @Override public boolean onReadRemoteRssi(BluetoothGatt gatt, int rssi) { - return callback.onReadRemoteRssi(gatt, rssi); + return callback.apply(gatt, rssi); } }); return this; } - public synchronized BtActionExecutor onFailure(OnFailure callback) { - actionQ.add(new BtCallback("onFailure") { + public synchronized BtActionExecutor onFailure(Runnable callback) { + failureQ.add(new BtCallback("onFailure") { @Override public void onFailure() { - callback.onFailure(); + callback.run(); } }); return this; } - public static interface OnConnectionStateChange { - boolean onConnectionStateChange(BluetoothGatt gatt, int newState); - } - - public static interface OnServicesDiscovered { - boolean onServicesDiscovered(BluetoothGatt gatt); - } - - public static interface OnCharacteristicRead { - boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic); - } - - public static interface OnCharacteristicWrite { - boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic); - } - - public static interface OnCharacteristicChanged { - boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic); - } - - public static interface OnDescriptorRead { - boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor); - } - - public static interface OnDescriptorWrite { - boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor); - } - - public static interface OnReliableWriteCompleted { - boolean onReliableWriteCompleted(BluetoothGatt gatt); - } - - public static interface OnReadRemoteRssi { - boolean onReadRemoteRssi(BluetoothGatt gatt, int rssi); - } - - public static interface OnFailure { - void onFailure(); - } - - public synchronized BtActionExecutor addFinally(BtCallback btCallback) { - finallyQ.add(btCallback); + public synchronized BtActionExecutor onFinally(Runnable callback) { + finallyQ.add(new BtCallback("finally") { + @Override + public void onFinally() { + callback.run(); + } + }); return this; } -// public synchronized BtActionExecutor onRemoteRssi(BluetoothGattCallback callback) { -// remoteRssi.add(callback); -// return this; -// } - - void onEvent(EventType key, String values, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, int status, int newState) { - if (status != BluetoothGatt.GATT_SUCCESS) { - Log.w(TAG, "Operation failed: " + key + ", " + values); - doFinally(); - return; - } - - Log.i(TAG, "Bt action completed successfully: callback=" + key); - - BtCallback btCallback; - synchronized (this) { - if (actionQ.isEmpty()) { - Log.d(TAG, "All Bluetooth actions are done"); + public String toString() { + StringBuilder s = new StringBuilder("Queue: "); - doFinally(); - return; - } + Iterator<BtCallback> it = actionQ.iterator(); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // ignore + int i = 0; + while (it.hasNext()) { + BtCallback c = it.next(); + if (i > 0) { + s.append(", "); } - btCallback = actionQ.remove(); - Log.i(TAG, "Executing bt action: " + btCallback.name); + s.append(c.name); + i++; } - try { - boolean ok = callCallback(key, gatt, characteristic, descriptor, newState, btCallback); - - if (!ok) { - Log.w(TAG, "The callback don't want to continue."); - doFinally(); - } - - if (actionQ.isEmpty()) { - Log.i(TAG, "The queue is empty"); - } - } catch (NotOverriddenException e) { - Log.w(TAG, "Unexpected callback by listener: " + key); - doFinally(); - } + return s.toString(); } - private void doFinally() { - actionQ.clear(); - - for (BtCallback callback = finallyQ.poll(); callback != null; callback = finallyQ.poll()) { - try { - callCallback(onFailure, null, null, null, 0, callback); - } catch (NotOverriddenException e) { - return; - } - } + public BluetoothGattCallback asCallback() { + return new MyBluetoothGattCallback(); } enum EventType { @@ -264,10 +174,13 @@ public class BtActionExecutor { onDescriptorWrite, onReliableWriteCompleted, + onReadRemoteRssi, + onFailure, + onFinally, } - private Boolean callCallback(EventType key, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, int newState, BtCallback btCallback) { + private static Boolean callCallback(EventType key, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, int newState, BtCallback btCallback) { switch (key) { case onConnectionStateChange: return btCallback.onConnectionStateChange(gatt, newState); @@ -289,78 +202,155 @@ public class BtActionExecutor { case onFailure: btCallback.onFailure(); return null; + case onFinally: + btCallback.onFinally(); + return null; default: Log.w(TAG, "Unknown callback: " + key); return null; } } - public String toString() { - StringBuilder s = new StringBuilder("Queue: "); + private class MyBluetoothGattCallback extends BluetoothGattCallback { - Iterator<BtCallback> it = actionQ.iterator(); + private Queue<BtCallback> initialActionQ = new ArrayDeque<>(actionQ); + private List<String> events = new ArrayList<>(); - int i = 0; - while (it.hasNext()) { - BtCallback c = it.next(); - if (i > 0) { - s.append(", "); - } - s.append(c.name); - i++; - } + 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); - return s.toString(); - } + Log.i(TAG, "Operation failed: " + key + ", " + values + ", success=" + success); - public BluetoothGattCallback asCallback() { - return new BluetoothGattCallback() { - @Override - public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - onEvent(onConnectionStateChange, "status=" + status + ", newState=" + newState, gatt, null, null, status, newState); + if (!success) { + doFailure(); + return; } - @Override - public void onServicesDiscovered(BluetoothGatt gatt, int status) { - onEvent(onServicesDiscovered, "status=" + status, gatt, null, null, status, 9); - } + BtCallback btCallback; + synchronized (this) { + if (actionQ.isEmpty()) { + Log.d(TAG, "All Bluetooth actions are done"); - @Override - public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { - onEvent(onCharacteristicRead, "status=" + status + ", characteristic=" + characteristic.getUuid(), gatt, characteristic, null, status, 0); - } + doFinally(); + return; + } - @Override - public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { - onEvent(onCharacteristicWrite, "status=" + status + ", characteristic=" + characteristic.getUuid(), gatt, characteristic, null, status, 0); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // ignore + } + btCallback = actionQ.remove(); + Log.i(TAG, "Executing bt action: " + btCallback.name); } - @Override - public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - onEvent(onCharacteristicChanged, "characteristic=" + characteristic.getUuid(), gatt, characteristic, null, BluetoothGatt.GATT_SUCCESS, 0); + try { + boolean ok = callCallback(key, gatt, characteristic, descriptor, newState, btCallback); + + if (!ok) { + Log.w(TAG, "The callback don't want to continue."); + doFinally(); + } + + if (actionQ.isEmpty()) { + Log.i(TAG, "The queue is empty"); + } + } catch (NotOverriddenException e) { + Log.w(TAG, "Unexpected callback by listener: " + key); + doFailure(); } + } - @Override - public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { - onEvent(onDescriptorRead, "status=" + status + ", descriptor=" + descriptor.getUuid(), gatt, null, descriptor, status, 0); + private void doFailure() { + StringBuilder msg = new StringBuilder(); + + msg.append("Expected events: \n"); + for (BtCallback cb : initialActionQ) { + msg.append(" ").append(cb.name).append("\n"); } - @Override - public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { - onEvent(onDescriptorWrite, "status=" + status + ", descriptor=" + descriptor.getUuid(), gatt, null, descriptor, status, 0); + msg.append("Actual events: \n"); + for (String event : events) { + msg.append(" ").append(event).append("\n"); } - @Override - public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { - onEvent(onReliableWriteCompleted, "status=" + status, gatt, null, null, status, 0); + Log.w(TAG, msg.toString()); + + Log.w(TAG, "Executing " + failureQ.size() + " failure handlers"); + + for (BtCallback callback = failureQ.poll(); callback != null; callback = failureQ.poll()) { + try { + callCallback(onFailure, null, null, null, 0, callback); + } catch (NotOverriddenException e) { + return; + } } - @Override - public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { - for (BluetoothGattCallback callback : remoteRssi) { - callback.onReadRemoteRssi(gatt, rssi, status); + doFinally(); + } + + private void doFinally() { + actionQ.clear(); + + Log.w(TAG, "Executing " + finallyQ.size() + " finally handlers"); + + for (BtCallback callback = finallyQ.poll(); callback != null; callback = finallyQ.poll()) { + try { + callCallback(onFinally, null, null, null, 0, 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/android/bt/BtCallback.java b/app/src/main/java/io/trygvis/android/bt/BtCallback.java index 7702962..29ea9e1 100644 --- a/app/src/main/java/io/trygvis/android/bt/BtCallback.java +++ b/app/src/main/java/io/trygvis/android/bt/BtCallback.java @@ -50,4 +50,8 @@ public class BtCallback { public void onFailure() { throw new NotOverriddenException(); } + + public void onFinally() { + throw new NotOverriddenException(); + } } diff --git a/app/src/main/java/io/trygvis/android/bt/BtDevice.java b/app/src/main/java/io/trygvis/android/bt/BtDevice.java index ba10d2d..e34e9ea 100644 --- a/app/src/main/java/io/trygvis/android/bt/BtDevice.java +++ b/app/src/main/java/io/trygvis/android/bt/BtDevice.java @@ -3,6 +3,8 @@ package io.trygvis.android.bt; import android.bluetooth.BluetoothDevice; import android.util.Log; +import io.trygvis.android.Function; + public class BtDevice<A> { private final static String TAG = BtDevice.class.getSimpleName(); @@ -14,31 +16,22 @@ public class BtDevice<A> { private boolean seenNow; - private BtDeviceListener l = null; - - private BtDeviceListener listener = new BtDeviceListener() { - }; + public static interface BtDeviceWrapper<A> { + BtDevice<A> getBtDevice(); + } - public BtDevice(DefaultBtService btService, BluetoothDevice bluetoothDevice, A tag, Integer rssi, BtScanResult scanResult) { + BtDevice(DefaultBtService btService, BluetoothDevice bluetoothDevice, Function<BtDevice<A>, A> tagConstructor, Integer rssi, BtScanResult scanResult) { this.btService = btService; this.bluetoothDevice = bluetoothDevice; - this.tag = tag; + this.tag = tagConstructor.apply(this); this.rssi = rssi; this.scanResult = scanResult; } - public void addListener(BtDeviceListener listener) { - this.l = listener; - } - public A getTag() { return tag; } - public void setTag(A tag) { - this.tag = tag; - } - public String getAddress() { return bluetoothDevice.getAddress(); } @@ -65,4 +58,23 @@ public class BtDevice<A> { public String toString() { return "BtDevice{address=" + bluetoothDevice.getAddress() + '}'; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BtDevice other = (BtDevice) o; + + return getAddress().equals(other.getAddress()); + } + + @Override + public int hashCode() { + return getAddress().hashCode(); + } } diff --git a/app/src/main/java/io/trygvis/android/bt/BtDeviceListener.java b/app/src/main/java/io/trygvis/android/bt/BtDeviceListener.java deleted file mode 100644 index 57eabc6..0000000 --- a/app/src/main/java/io/trygvis/android/bt/BtDeviceListener.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.trygvis.android.bt; - -public interface BtDeviceListener { -} 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 123be3a..968a4a8 100644 --- a/app/src/main/java/io/trygvis/android/bt/BtService.java +++ b/app/src/main/java/io/trygvis/android/bt/BtService.java @@ -1,12 +1,17 @@ package io.trygvis.android.bt; -import android.os.Binder; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; -import java.util.List; +import java.util.Collection; -public interface BtService<A> { +import io.trygvis.android.Function; - boolean initialize(BtServiceListener<A> btServiceListener, Supplier<A> dataSupplier); +public interface BtService<A extends BtDevice.BtDeviceWrapper<A>> { + + boolean initialize(Function<BtDevice<A>, A> tagConstructor); void clearCache(); @@ -16,40 +21,35 @@ public interface BtService<A> { void stopScanning(); -// BtDevice<A> getDevice(String macAddress); + BtDevice<A> getDevice(String address); - List<BtDevice<A>> getDevices(); + A getTag(String address); - interface Supplier<A> { - A get(); - } + Collection<BtDevice<A>> getDevices(); - interface BtServiceListener<A> { - void onScanStarted(); + Collection<A> getTags(); - void onNewDevice(BtDevice<A> device); + public static class BtServiceListenerBroadcastReceiver extends BroadcastReceiver { - void onScanStopped(); - } + public static final String INTENT_NAME = BtServiceListenerBroadcastReceiver.class.getName(); - public abstract class AbstractBtServiceListener<A> implements BtServiceListener<A> { + public static final IntentFilter INTENT_FILTER = new IntentFilter(INTENT_NAME); - public void onScanStarted() { - } + public void onReceive(Context context, Intent intent) { + if (!intent.getAction().equals(INTENT_NAME)) { + return; + } - public void onScanStopped() { + DefaultBtService.dispatchEvent(intent, this); } - } - public class LocalBinder<A> extends Binder { - private final BtService<A> service; + public void onScanStarted() { + } - public LocalBinder(BtService<A> service) { - this.service = service; + public void onNewDevice(String address) { } - public BtService<A> getService() { - return service; + public void onScanStopped() { } } } 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 8638544..51d84af 100644 --- a/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java +++ b/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java @@ -13,12 +13,17 @@ import android.util.Log; import android.widget.Toast; import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import io.trygvis.android.Function; +import io.trygvis.android.LocalBinder; import io.trygvis.soilmoisture.R; -public class DefaultBtService<A> extends Service implements BtService<A> { +import static java.util.Collections.unmodifiableCollection; + +public class DefaultBtService<A extends BtDevice.BtDeviceWrapper<A>> extends Service implements BtService<A> { private final static String TAG = DefaultBtService.class.getSimpleName(); private final IBinder binder = new LocalBinder<>(this); @@ -29,19 +34,13 @@ public class DefaultBtService<A> extends Service implements BtService<A> { // State // ----------------------------------------------------------------------- - private BtServiceListener<A> serviceListener = new AbstractBtServiceListener<A>() { - @Override - public void onNewDevice(BtDevice<A> device) { - } - }; - - private Supplier<A> tagConstructor; + private Function<BtDevice<A>, A> tagConstructor; private BluetoothManager bluetoothManager; private BluetoothAdapter bluetoothAdapter; - private final List<BtDevice<A>> devices = new ArrayList<>(); + private final Set<BtDevice<A>> devices = new HashSet<>(); private boolean scanning = false; @@ -50,17 +49,13 @@ public class DefaultBtService<A> extends Service implements BtService<A> { // ----------------------------------------------------------------------- @Override - public boolean initialize(BtServiceListener<A> serviceListener, Supplier<A> dataSupplier) { + public boolean initialize(Function<BtDevice<A>, A> tagConstructor) { if (bluetoothManager != null) { - Log.e(TAG, "Already initialized"); + Log.i(TAG, "Already initialized"); return false; } - this.tagConstructor = dataSupplier; - - if (serviceListener != null) { - this.serviceListener = serviceListener; - } + this.tagConstructor = tagConstructor; // Use this check to determine whether BLE is supported on the device. Then you can // selectively disable BLE-related features. @@ -85,7 +80,7 @@ public class DefaultBtService<A> extends Service implements BtService<A> { return false; } - Log.e(TAG, "Bluetooth initialized"); + Log.i(TAG, "Bluetooth initialized"); return true; } @@ -100,13 +95,15 @@ public class DefaultBtService<A> extends Service implements BtService<A> { @Override public boolean startScanning(long timeoutMs) { + Log.d(TAG, "startScanning, timeoutMs=" + timeoutMs); + if (timeoutMs > 0) { handler.postDelayed(this::stopScanning, timeoutMs); } if (bluetoothAdapter.startLeScan(leScanCallback)) { scanning = true; - serviceListener.onScanStarted(); + sendBroadcast(createScanStarted()); return true; } @@ -115,15 +112,16 @@ public class DefaultBtService<A> extends Service implements BtService<A> { @Override public void stopScanning() { + Log.d(TAG, "stopScanning"); + // This doesn't mind being called twice. bluetoothAdapter.stopLeScan(leScanCallback); scanning = false; - serviceListener.onScanStopped(); + sendBroadcast(createScanStopped()); } -// @Override public BtDevice<A> getDevice(String mac) { BtDevice<A> device = findDevice(mac); @@ -136,8 +134,22 @@ public class DefaultBtService<A> extends Service implements BtService<A> { } @Override - public List<BtDevice<A>> getDevices() { - return Collections.unmodifiableList(devices); + public A getTag(String address) { + return getDevice(address).getTag(); + } + + @Override + public Collection<BtDevice<A>> getDevices() { + return unmodifiableCollection(devices); + } + + @Override + public Collection<A> getTags() { + ArrayList<A> tags = new ArrayList<>(); + for (BtDevice<A> device : devices) { + tags.add(device.getTag()); + } + return tags; } // ----------------------------------------------------------------------- @@ -145,8 +157,6 @@ public class DefaultBtService<A> extends Service implements BtService<A> { // ----------------------------------------------------------------------- private BluetoothAdapter.LeScanCallback leScanCallback = (device, rssi, scanRecord) -> { -// Log.i(TAG, "onLeScan()"); - BtScanResult scanResult = new BtScanResult(scanRecord); register(device, rssi, scanResult); @@ -173,14 +183,48 @@ public class DefaultBtService<A> extends Service implements BtService<A> { } Log.i(TAG, "New device: " + bluetoothDevice.getAddress()); - btDevice = new BtDevice<>(this, bluetoothDevice, tagConstructor.get(), rssi, scanResult); + btDevice = new BtDevice<>(this, bluetoothDevice, tagConstructor, rssi, scanResult); devices.add(btDevice); - serviceListener.onNewDevice(btDevice); + sendBroadcast(createNewDevice(btDevice.getAddress())); return btDevice; } + private Intent createScanStarted() { + return new Intent(BtServiceListenerBroadcastReceiver.INTENT_NAME). + putExtra("event", "scanStarted"); + } + + private Intent createScanStopped() { + return new Intent(BtServiceListenerBroadcastReceiver.INTENT_NAME). + putExtra("event", "scanStopped"); + } + + private Intent createNewDevice(String address) { + return new Intent(BtServiceListenerBroadcastReceiver.INTENT_NAME). + putExtra("event", "newDevice"). + 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; + default: + break; + } + } + private BtDevice<A> findDevice(String mac) { for (BtDevice<A> d : devices) { if (d.getAddress().equals(mac)) { diff --git a/app/src/main/java/io/trygvis/soilmoisture/Constants.java b/app/src/main/java/io/trygvis/soilmoisture/Constants.java deleted file mode 100644 index 0b891b7..0000000 --- a/app/src/main/java/io/trygvis/soilmoisture/Constants.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.trygvis.soilmoisture; - -import java.util.UUID; - -public interface Constants { - String TRYGVIS_IO_BASE_UUID = "32D0xxxx-035D-59C5-70D3-BC8E4A1FD83F"; - UUID TRYGVIS_IO_FIKEN_STATUS_PANEL_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0001")); - UUID TRYGVIS_IO_GAUGE_DATA_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0002")); - UUID TRYGVIS_IO_GAUGE_CTRL_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0004")); - UUID TRYGVIS_IO_LED_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0003")); - - UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); -} diff --git a/app/src/main/java/io/trygvis/soilmoisture/DefaultSmDevicesManager.java b/app/src/main/java/io/trygvis/soilmoisture/DefaultSmDevicesManager.java new file mode 100644 index 0000000..301bfc7 --- /dev/null +++ b/app/src/main/java/io/trygvis/soilmoisture/DefaultSmDevicesManager.java @@ -0,0 +1,224 @@ +package io.trygvis.soilmoisture; + +import android.app.Service; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; +import android.content.ComponentName; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import io.trygvis.android.LocalBinder; +import io.trygvis.android.bt.BtActionExecutor; +import io.trygvis.android.bt.BtDevice; +import io.trygvis.android.bt.BtService; +import io.trygvis.android.bt.DefaultBtService; +import io.trygvis.bluetooth.TrygvisIoUuids; + +import static io.trygvis.android.bt.BtService.BtServiceListenerBroadcastReceiver; + +public class DefaultSmDevicesManager extends Service implements SmDevicesManager { + private final static String TAG = DefaultSmDevicesManager.class.getSimpleName(); + + private final IBinder binder = new LocalBinder<>(this); + + @SuppressWarnings("UnusedDeclaration") + private final DefaultSmDevicesManager context = DefaultSmDevicesManager.this; + + private ServiceConnection serviceConnection; + + private BtService<SmDevice> btService; + + + @Override + public IBinder onBind(Intent intent) { + return binder; + } + + @Override + public void onCreate() { + Log.i(TAG, "onCreate"); + + serviceConnection = new ServiceConnection() { + @SuppressWarnings("unchecked") + @Override + public void onServiceConnected(ComponentName componentName, IBinder service) { + btService = ((LocalBinder<BtService<SmDevice>>) service).getService(); + boolean ok = btService.initialize(DefaultSmDevicesManager.this::onNewDevice); + + sendBroadcast(createReady(ok)); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + btService = null; + } + }; + + bindService(new Intent(this, DefaultBtService.class), serviceConnection, BIND_AUTO_CREATE); + registerReceiver(btServiceListener, BtServiceListenerBroadcastReceiver.INTENT_FILTER); + } + + @Override + public void onDestroy() { + unregisterReceiver(btServiceListener); + Log.i(TAG, "onDestroy" + btService); + if (serviceConnection != null) { + unbindService(serviceConnection); + } + } + + private final BtServiceListenerBroadcastReceiver btServiceListener = new BtServiceListenerBroadcastReceiver() { + @Override + public void onScanStarted() { + sendBroadcast(createScanStarted()); + } + + @Override + public void onScanStopped() { + sendBroadcast(createScanStopped()); + } + + @Override + public void onNewDevice(String address) { + BtDevice<SmDevice> btDevice = btService.getDevice(address); + SmDevice smDevice = btDevice.getTag(); + + if (!smDevice.isProbed()) { + Log.i(TAG, "Probing " + address + ", name=" + btDevice.getName()); + BtActionExecutor executor = new BtActionExecutor(). + onConnectionStateChange((gatt, newState) -> { + //noinspection SimplifiableIfStatement + if (newState == BluetoothGatt.STATE_CONNECTED) { + Log.i(TAG, "Connected to " + address + ", getting services"); + return gatt.discoverServices(); + } + + Log.i(TAG, "Could not connect to to " + address); + smDevice.setIsUseful(false); + return false; + }). + onServicesDiscovered(gatt -> { + Log.i(TAG, "Services discovered"); + + BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE); + + if (service == null) { + smDevice.setIsUseful(false); + return false; + } + + BluetoothGattCharacteristic characteristic = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE); + + if (characteristic == null) { + smDevice.setIsUseful(false); + return false; + } + + smDevice.setIsUseful(true); + sendBroadcast(createNewDevice(address)); + + gatt.disconnect(); + + return true; + }); + + btDevice.connect(executor); + } else { + sendBroadcast(createNewDevice(address)); + } + } + }; + + // ----------------------------------------------------------------------- + // SmDevicesManager Implementation + // ----------------------------------------------------------------------- + + @Override + public List<SmDevice> getDevices(Comparator<SmDevice> comparator) { + Set<SmDevice> devices = new TreeSet<>(comparator); + for (BtDevice<SmDevice> btDevice : btService.getDevices()) { + devices.add(btDevice.getTag()); + } + return new ArrayList<>(devices); + } + + @Override + public SmDevice getDevice(String address) { + return btService.getTag(address); + } + + @Override + public boolean isScanning() { + return btService.isScanning(); + } + + @Override + public boolean startScanning(long scanPeriod) { + return btService.startScanning(scanPeriod); + } + + @Override + public void stopScanning() { + btService.stopScanning(); + } + + // ----------------------------------------------------------------------- + // + // ----------------------------------------------------------------------- + + private SmDevice onNewDevice(BtDevice<SmDevice> btDevice) { + return new SmDevice(btDevice); + } + + private Intent createReady(boolean success) { + return new Intent(SmDeviceListener.INTENT_NAME). + putExtra("event", "ready"). + putExtra("success", success); + } + + private Intent createScanStarted() { + return new Intent(SmDeviceListener.INTENT_NAME). + putExtra("event", "scanStarted"); + } + + private Intent createScanStopped() { + return new Intent(SmDeviceListener.INTENT_NAME). + putExtra("event", "scanStopped"); + } + + private Intent createNewDevice(String address) { + return new Intent(SmDeviceListener.INTENT_NAME). + putExtra("event", "newDevice"). + putExtra("address", address); + } + + public static void dispatchEvent(Intent intent, SmDeviceListener listener) { + String event = intent.getStringExtra("event"); + Log.i(TAG, "Dispatching event " + intent.getAction() + "/" + event); + switch (event) { + case "ready": + listener.onReady(intent.getBooleanExtra("success", false)); + break; + case "newDevice": + listener.onNewDevice(intent.getStringExtra("address")); + break; + case "scanStarted": + listener.onScanStarted(); + break; + case "scanStopped": + listener.onScanStopped(); + break; + default: + break; + } + } +} diff --git a/app/src/main/java/io/trygvis/soilmoisture/ExceptionHandler.java b/app/src/main/java/io/trygvis/soilmoisture/ExceptionHandler.java index faa95e1..ff70b0f 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/ExceptionHandler.java +++ b/app/src/main/java/io/trygvis/soilmoisture/ExceptionHandler.java @@ -11,12 +11,6 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler { public void uncaughtException(Thread thread, Throwable ex) { Log.e(TAG, "Uncaught", ex); - if (ex instanceof RuntimeException) { - throw (RuntimeException) ex; - } - if (ex instanceof Error) { - throw (Error) ex; - } - throw new RuntimeException(ex); + Thread.getDefaultUncaughtExceptionHandler().uncaughtException(thread, ex); } } diff --git a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java index 18c96b8..6adc96c 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java +++ b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java @@ -2,8 +2,7 @@ package io.trygvis.soilmoisture; import android.app.ActionBar; import android.app.ListActivity; -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattService; +import android.app.ProgressDialog; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; @@ -20,20 +19,19 @@ import android.widget.Button; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; +import android.widget.Toast; import com.crashlytics.android.Crashlytics; +import java.util.ArrayList; +import java.util.List; + import io.fabric.sdk.android.Fabric; -import io.trygvis.android.bt.BtActionExecutor; +import io.trygvis.android.LocalBinder; import io.trygvis.android.bt.BtActivitySupport; -import io.trygvis.android.bt.BtDevice; -import io.trygvis.android.bt.BtDeviceListener; -import io.trygvis.android.bt.BtService; -import io.trygvis.android.bt.DefaultBtService; -import io.trygvis.bluetooth.TrygvisIoUuids; -import static io.trygvis.android.bt.BtService.BtServiceListener; import static io.trygvis.soilmoisture.ExceptionHandler.EXCEPTION_HANDLER; +import static io.trygvis.soilmoisture.SmDevicesManager.SmDeviceListener; import static java.lang.String.valueOf; public class MainActivity extends ListActivity { @@ -44,11 +42,14 @@ public class MainActivity extends ListActivity { private static final int REQUEST_ENABLE_BT = 1; private final BtActivitySupport btActivitySupport = new BtActivitySupport(this, REQUEST_ENABLE_BT); + private final SmDeviceListener serviceListener = new MySmDeviceListener(); + private final MainActivity context = this; private DeviceListAdapter deviceList; - private BtService<SmDevice> btService; - private ServiceConnection serviceConnection; + private SmDevicesManager smDevicesManager; + private ProgressDialog initializing; + private boolean ready; @Override protected void onCreate(Bundle savedInstanceState) { @@ -71,26 +72,23 @@ public class MainActivity extends ListActivity { @SuppressWarnings("unchecked") @Override public void onServiceConnected(ComponentName componentName, IBinder service) { - btService = ((BtService.LocalBinder<SmDevice>) service).getService(); - if (!btService.initialize(serviceListener, SmDevice::new)) { - finish(); - } - - deviceList = new DeviceListAdapter(); - deviceList.notifyDataSetChanged(); - setListAdapter(deviceList); - - startScan(); + Log.i(TAG, "onServiceConnected"); + smDevicesManager = ((LocalBinder<SmDevicesManager>) service).getService(); + registerReceiver(serviceListener, SmDeviceListener.INTENT_FILTER); } @Override public void onServiceDisconnected(ComponentName componentName) { - btService = null; + Log.i(TAG, "onServiceDisconnected"); + smDevicesManager = null; stopScan(); } }; - bindService(new Intent(this, DefaultBtService.class), serviceConnection, BIND_AUTO_CREATE); + bindService(new Intent(this, DefaultSmDevicesManager.class), serviceConnection, BIND_AUTO_CREATE); + + initializing = ProgressDialog. + show(this, "Initializing", "Connecting to Bluetooth system.", true); } @Override @@ -114,7 +112,7 @@ public class MainActivity extends ListActivity { return; } - // registerReceiver(btServiceBroadcastReceiver, IntentAction.ALL_FILTER); + registerReceiver(serviceListener, SmDeviceListener.INTENT_FILTER); } @Override @@ -123,7 +121,7 @@ public class MainActivity extends ListActivity { super.onPause(); stopScan(); - // unregisterReceiver(ntServiceBroadcastReceiver); + unregisterReceiver(serviceListener); } @Override @@ -143,14 +141,20 @@ public class MainActivity extends ListActivity { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); - if (!btService.isScanning()) { + if (ready) { + if (!smDevicesManager.isScanning()) { + menu.findItem(R.id.menu_stop).setVisible(false); + menu.findItem(R.id.menu_scan).setVisible(true); + menu.findItem(R.id.menu_refresh).setActionView(null); + } else { + menu.findItem(R.id.menu_stop).setVisible(true); + menu.findItem(R.id.menu_scan).setVisible(false); + menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_indeterminate_progress); + } + } else { menu.findItem(R.id.menu_stop).setVisible(false); menu.findItem(R.id.menu_scan).setVisible(true); menu.findItem(R.id.menu_refresh).setActionView(null); - } else { - menu.findItem(R.id.menu_stop).setVisible(true); - menu.findItem(R.id.menu_scan).setVisible(false); - menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_indeterminate_progress); } return true; } @@ -171,12 +175,12 @@ public class MainActivity extends ListActivity { } private void startScan() { - btService.startScanning(SCAN_PERIOD); + smDevicesManager.startScanning(SCAN_PERIOD); } private void stopScan() { - if (btService != null) { - btService.stopScanning(); + if (smDevicesManager != null) { + smDevicesManager.stopScanning(); } } @@ -184,54 +188,9 @@ public class MainActivity extends ListActivity { protected void onListItemClick(ListView l, View v, int position, long id) { stopScan(); - BtDevice<SmDevice> state = btService.getDevices().get(position); - - BtActionExecutor executor = new BtActionExecutor(). - onConnectionStateChange((gatt, newState) -> { - if (newState == BluetoothGatt.STATE_CONNECTED) { - Intent intent = new Intent(this, SoilActivity.class); - startActivity(intent); - return true; - } - return false; - }). - onServicesDiscovered(gatt -> false); - - state.connect(executor); +// SmDevice state = smDevicesManager.getDevices(SmDevice.deviceComparator).get(position); } - BtServiceListener<SmDevice> serviceListener = new BtServiceListener<SmDevice>() { - @Override - public void onScanStarted() { - invalidateOptionsMenu(); - } - - @Override - public void onNewDevice(BtDevice<SmDevice> device) { - device.addListener(deviceListener); - - BtActionExecutor executor = new BtActionExecutor(). - onConnectionStateChange((gatt, newState) -> gatt.discoverServices()). - onServicesDiscovered(gatt -> { - BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE); - - boolean useful = service != null; - device.getTag().setIsUseful(useful); - runOnUiThread(deviceList::notifyDataSetChanged); - return useful; - }); - device.connect(executor); - } - - @Override - public void onScanStopped() { - invalidateOptionsMenu(); - } - }; - - BtDeviceListener deviceListener = new BtDeviceListener() { - }; - // ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- @@ -253,16 +212,17 @@ public class MainActivity extends ListActivity { } private class DeviceListAdapter extends BaseAdapter { + private List<SmDevice> devices = new ArrayList<>(); private LayoutInflater inflater = MainActivity.this.getLayoutInflater(); @Override public int getCount() { - return btService.getDevices().size(); + return devices.size(); } @Override - public Object getItem(int i) { - return btService.getDevices().get(i); + public SmDevice getItem(int i) { + return devices.get(i); } @Override @@ -283,18 +243,16 @@ public class MainActivity extends ListActivity { item = (DeviceListItem) view.getTag(); } - BtDevice<SmDevice> btDevice = btService.getDevices().get(i); - if (btDevice.getName() != null && btDevice.getName().length() > 0) { - item.deviceName.setText(btDevice.getName()); + SmDevice smDevice = getItem(i); + if (smDevice.getName() != null) { + item.deviceName.setText(smDevice.getName()); } else { item.deviceName.setText(R.string.unknown_device); } - item.deviceAddress.setText(btDevice.getAddress()); + item.deviceAddress.setText(smDevice.getBtDevice().getAddress()); item.rssi.setText(getText(R.string.rssi) + ": " + - (btDevice.getRssi() != 0 ? valueOf(btDevice.getRssi()) : getText(R.string.unknown))); - - SmDevice smDevice = btDevice.getTag(); + (smDevice.getBtDevice().getRssi() != 0 ? valueOf(smDevice.getBtDevice().getRssi()) : getText(R.string.unknown))); boolean useful = smDevice.isUseful(); item.spinner.setVisibility(useful ? View.GONE : View.VISIBLE); @@ -304,4 +262,46 @@ public class MainActivity extends ListActivity { return view; } } + + private class MySmDeviceListener extends SmDeviceListener { + @Override + public void onReady(boolean ok) { + if (!ok) { + Toast.makeText(context, + "Could not initialize services.", + Toast.LENGTH_SHORT). + show(); + + finish(); + } else { + ready = true; + deviceList = new DeviceListAdapter(); + setListAdapter(deviceList); + + deviceList.notifyDataSetChanged(); + startScan(); + + initializing.dismiss(); + } + } + + @Override + public void onScanStarted() { + invalidateOptionsMenu(); + } + + @Override + public void onScanStopped() { + invalidateOptionsMenu(); + } + + @Override + public void onNewDevice(String address) { + SmDevice device = smDevicesManager.getDevice(address); + deviceList.devices.add(device); + deviceList.notifyDataSetInvalidated(); + + Log.i(TAG, "deviceList.devices.size() = " + deviceList.devices.size()); + } + } } diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java index 6bc522d..1ed7ecb 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java +++ b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java @@ -2,14 +2,35 @@ package io.trygvis.soilmoisture; import android.util.Log; -class SmDevice { +import java.util.Comparator; + +import io.trygvis.android.bt.BtDevice; + +class SmDevice implements BtDevice.BtDeviceWrapper<SmDevice> { + public static final Comparator<SmDevice> deviceComparator = (a, b) -> a.getBtDevice().getAddress().compareTo(b.getBtDevice().getAddress()); + private final static String TAG = SmDevice.class.getSimpleName(); - public SmDevice() { + private final BtDevice<SmDevice> btDevice; + + private String name; + + private Boolean isUseful; + + public SmDevice(BtDevice<SmDevice> btDevice) { + this.btDevice = btDevice; Log.i(TAG, "new device"); + + name = btDevice.getName(); + + if (name != null && name.trim().length() == 0) { + name = null; + } } - private Boolean isUseful; + public BtDevice<SmDevice> getBtDevice() { + return btDevice; + } public boolean isUseful() { return isUseful != null && isUseful; @@ -19,8 +40,16 @@ class SmDevice { return isUseful; } + public boolean isProbed() { + return isUseful != null; + } + public void setIsUseful(Boolean isUseful) { Log.i(TAG, "useful=" + isUseful); this.isUseful = isUseful; } + + public String getName() { + return name; + } } diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmDevicesManager.java b/app/src/main/java/io/trygvis/soilmoisture/SmDevicesManager.java new file mode 100644 index 0000000..531061a --- /dev/null +++ b/app/src/main/java/io/trygvis/soilmoisture/SmDevicesManager.java @@ -0,0 +1,50 @@ +package io.trygvis.soilmoisture; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import java.util.Comparator; +import java.util.List; + +import io.trygvis.android.bt.BtDevice; + +public interface SmDevicesManager { + List<SmDevice> getDevices(Comparator<SmDevice> comparator); + + SmDevice getDevice(String address); + + boolean isScanning(); + + boolean startScanning(long scanPeriod); + + void stopScanning(); + + public abstract static class SmDeviceListener extends BroadcastReceiver { + + public static final String INTENT_NAME = SmDeviceListener.class.getName(); + + public static final IntentFilter INTENT_FILTER = new IntentFilter(INTENT_NAME); + + public void onReceive(Context context, Intent intent) { + if (!intent.getAction().equals(INTENT_NAME)) { + return; + } + + DefaultSmDevicesManager.dispatchEvent(intent, this); + } + + public void onReady(boolean ok) { + } + + public void onScanStarted() { + } + + public void onNewDevice(String address) { + } + + public void onScanStopped() { + } + } +} diff --git a/app/src/main/res/layout/listitem_device.xml b/app/src/main/res/layout/listitem_device.xml index 2d6ef91..0904d94 100644 --- a/app/src/main/res/layout/listitem_device.xml +++ b/app/src/main/res/layout/listitem_device.xml @@ -33,14 +33,15 @@ <LinearLayout android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:id="@+id/spacer" + android:layout_alignParentEnd="true" android:layout_alignParentTop="true" - android:layout_alignParentEnd="true"> + android:layout_centerInParent="true"> <Button android:layout_width="wrap_content" - android:layout_height="fill_parent" + android:layout_height="match_parent" android:text="@string/connect" android:id="@+id/button_connect" /> @@ -50,61 +51,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/device_spinner" - /> + android:visibility="gone"/> </LinearLayout> - <!-- - <TextView - android:id="@+id/device_name" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textSize="24sp" - android:layout_alignParentTop="true" - android:layout_marginTop="0dp" - android:layout_toStartOf="@+id/spacer"/> - - <TextView - android:id="@+id/device_address" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textSize="12sp" - android:layout_below="@+id/device_name" - android:layout_alignParentStart="true" - android:layout_toStartOf="@+id/spacer"/> - - <TextView - android:id="@+id/device_rssi" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textSize="12sp" - android:layout_below="@+id/device_address" - android:layout_alignParentStart="true" - android:layout_toStartOf="@+id/spacer"/> - - <Space - android:id="@+id/spacer" - android:layout_width="2dp" - android:layout_height="fill_parent" - android:layout_toStartOf="@id/button_connect"/> - - <Button - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/connect" - android:id="@+id/button_connect" - android:layout_alignParentTop="true" - android:layout_toStartOf="@+id/device_spinner" - /> - - <ProgressBar - style="?android:attr/progressBarStyleLarge" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/device_spinner" - android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" - /> - --> - </RelativeLayout> |