summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2015-01-02 21:38:52 +0100
committerTrygve Laugstøl <trygvis@inamo.no>2015-01-02 21:38:52 +0100
commitda80f3d219c0c05568db0cb9a8910f02cc281d47 (patch)
treef62e2c251a03e2a33a7733f1e5d1829a40bdb1ba
parented559834ccddafa955df5b528f08fba964e57699 (diff)
downloadio.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.
-rw-r--r--app/src/main/AndroidManifest.xml7
-rw-r--r--app/src/main/java/io/trygvis/android/F2.java5
-rw-r--r--app/src/main/java/io/trygvis/android/F3.java5
-rw-r--r--app/src/main/java/io/trygvis/android/F4.java5
-rw-r--r--app/src/main/java/io/trygvis/android/Function.java5
-rw-r--r--app/src/main/java/io/trygvis/android/LocalBinder.java15
-rw-r--r--app/src/main/java/io/trygvis/android/Supplier.java5
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtActionExecutor.java358
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtCallback.java4
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtDevice.java40
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtDeviceListener.java4
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtService.java50
-rw-r--r--app/src/main/java/io/trygvis/android/bt/DefaultBtService.java100
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/Constants.java13
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/DefaultSmDevicesManager.java224
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/ExceptionHandler.java8
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/MainActivity.java178
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/SmDevice.java35
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/SmDevicesManager.java50
-rw-r--r--app/src/main/res/layout/listitem_device.xml62
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>