diff options
| author | Trygve Laugstøl <trygvis@inamo.no> | 2015-01-11 12:28:55 +0100 | 
|---|---|---|
| committer | Trygve Laugstøl <trygvis@inamo.no> | 2015-01-11 12:28:55 +0100 | 
| commit | 17a1f7227c8c3872fce7bbcc2f5cd46540f9ac52 (patch) | |
| tree | 20c2c99e710fd1db438a49029177552204a63591 /app/src/main/java/io | |
| parent | 4a2ca2d94c827566f8682e8dbd6fbdf17d70b4dd (diff) | |
| download | io.trygvis.soilmoisture-android-17a1f7227c8c3872fce7bbcc2f5cd46540f9ac52.tar.gz io.trygvis.soilmoisture-android-17a1f7227c8c3872fce7bbcc2f5cd46540f9ac52.tar.bz2 io.trygvis.soilmoisture-android-17a1f7227c8c3872fce7bbcc2f5cd46540f9ac52.tar.xz io.trygvis.soilmoisture-android-17a1f7227c8c3872fce7bbcc2f5cd46540f9ac52.zip | |
o Reading values from the soil sensor.
o Rewrote the database schema to match the new device+sensors model.
o Storing samples in the database.
o To be able to reuse BT callbacks, added a way to always to directly to the next step instead of waiting for an event.
Diffstat (limited to 'app/src/main/java/io')
13 files changed, 563 insertions, 170 deletions
| diff --git a/app/src/main/java/io/trygvis/android/Consumer.java b/app/src/main/java/io/trygvis/android/Consumer.java new file mode 100644 index 0000000..bf8c5e5 --- /dev/null +++ b/app/src/main/java/io/trygvis/android/Consumer.java @@ -0,0 +1,5 @@ +package io.trygvis.android; + +public interface Consumer<T> { +    void accept(T t); +} 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 8dab149..71f7d56 100644 --- a/app/src/main/java/io/trygvis/android/bt/BtCallback.java +++ b/app/src/main/java/io/trygvis/android/bt/BtCallback.java @@ -4,16 +4,18 @@ import android.bluetooth.BluetoothGatt;  import android.bluetooth.BluetoothGattCharacteristic;  import android.bluetooth.BluetoothGattDescriptor; -import static io.trygvis.android.bt.BtPromise.*; +import static io.trygvis.android.bt.BtPromise.PromiseResult;  public class BtCallback { +    public final boolean stopOnFailure;      public final String name; -    public BtCallback(String name) { +    public BtCallback(boolean stopOnFailure, String name) { +        this.stopOnFailure = stopOnFailure;          this.name = name;      } -    public PromiseResult onConnectionStateChange(BluetoothGatt gatt, int newState) { +    public PromiseResult onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {          throw new NotOverriddenException();      } @@ -49,6 +51,10 @@ public class BtCallback {          throw new NotOverriddenException();      } +    public PromiseResult onDirect(Object value) { +        throw new NotOverriddenException(); +    } +      public void onFailure() {          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 1c7666e..d8dfefc 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,7 @@ package io.trygvis.android.bt;  import android.bluetooth.BluetoothDevice;  import android.bluetooth.BluetoothGatt;  import android.bluetooth.BluetoothGattCallback; +import android.database.sqlite.SQLiteDatabase;  import android.util.Log;  import java.util.Date; @@ -21,23 +22,25 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {      private final boolean seenBefore;      private final Date firstSeen;      private Date lastSeen; +    private boolean connected;      public static interface BtDeviceWrapper<A extends BtDevice.BtDeviceWrapper<A>> {          BtDevice<A> getBtDevice();      } -    BtDevice(DefaultBtService btService, BluetoothDevice bluetoothDevice, +    BtDevice(DefaultBtService btService, BluetoothDevice bluetoothDevice, SQLiteDatabase db,               BtService.BtDbIntegration<A> btDbIntegration, long id, Integer rssi,               BtScanResult scanResult, boolean seenBefore, Date firstSeen, Date lastSeen) {          this.btService = btService;          this.bluetoothDevice = bluetoothDevice; -        this.tag = btDbIntegration.createTag(this);          this.id = id;          this.rssi = rssi;          this.scanResult = scanResult;          this.seenBefore = seenBefore;          this.firstSeen = firstSeen;          this.lastSeen = lastSeen; + +        this.tag = btDbIntegration.createTag(db, this);      }      public long getId() { @@ -60,6 +63,10 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {          return rssi;      } +    public BtScanResult getScanResult() { +        return scanResult; +    } +      public boolean isSeenBefore() {          return seenBefore;      } @@ -78,8 +85,15 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {      public synchronized boolean connect(BtPromise executor) {          Log.i(TAG, "connect(), address=" + bluetoothDevice.getAddress()); -        BluetoothGattCallback callback = executor.asCallback(); -        gatt = bluetoothDevice.connectGatt(btService, false, callback); +        BluetoothGattCallback callback = executor.asCallback(bluetoothDevice.getAddress()); +        gatt = bluetoothDevice.connectGatt(btService, false, new WrappingBluetoothGattCallback(callback) { +            @Override +            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { +                BtDevice.this.connected = newState == BluetoothGatt.STATE_CONNECTED; + +                super.onConnectionStateChange(gatt, status, newState); +            } +        });          return true;      } @@ -90,8 +104,8 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {          }      } -    public BtScanResult getScanResult() { -        return scanResult; +    public boolean connected() { +        return connected;      }      @Override diff --git a/app/src/main/java/io/trygvis/android/bt/BtPromise.java b/app/src/main/java/io/trygvis/android/bt/BtPromise.java index 55b6315..6af62c0 100644 --- a/app/src/main/java/io/trygvis/android/bt/BtPromise.java +++ b/app/src/main/java/io/trygvis/android/bt/BtPromise.java @@ -10,9 +10,11 @@ import java.util.ArrayDeque;  import java.util.ArrayList;  import java.util.Iterator;  import java.util.List; +import java.util.ListIterator;  import java.util.Queue;  import io.trygvis.android.F2; +import io.trygvis.android.F3;  import io.trygvis.android.Function;  import static io.trygvis.android.bt.BtPromise.EventType.onCharacteristicChanged; @@ -21,6 +23,7 @@ import static io.trygvis.android.bt.BtPromise.EventType.onCharacteristicWrite;  import static io.trygvis.android.bt.BtPromise.EventType.onConnectionStateChange;  import static io.trygvis.android.bt.BtPromise.EventType.onDescriptorRead;  import static io.trygvis.android.bt.BtPromise.EventType.onDescriptorWrite; +import static io.trygvis.android.bt.BtPromise.EventType.onDirect;  import static io.trygvis.android.bt.BtPromise.EventType.onFailure;  import static io.trygvis.android.bt.BtPromise.EventType.onFinally;  import static io.trygvis.android.bt.BtPromise.EventType.onReliableWriteCompleted; @@ -28,20 +31,48 @@ import static io.trygvis.android.bt.BtPromise.EventType.onServicesDiscovered;  public class BtPromise {      private final static String TAG = BtPromise.class.getSimpleName(); -    private final Queue<BtCallback> actionQ = new ArrayDeque<>(); +    private final List<BtCallback> actionQ = new ArrayList<>();      private final Queue<BtCallback> failureQ = new ArrayDeque<>();      private final Queue<BtCallback> finallyQ = new ArrayDeque<>(); -    public static final PromiseResult continueDownChain = new Continue(); -    public static final PromiseResult doneWithChain = new Done(); +    private static final PromiseResult waitForNextEvent = new WaitForNextEvent(); +    private static final PromiseResult stop = new Stop(); -    public abstract static class PromiseResult { +    private Boolean stopOnFailure; + +    public static class PromiseResult { +        private PromiseResult() { +        } + +        public static PromiseResult waitForNextEvent() { +            return waitForNextEvent; +        } + +        public static PromiseResult continueDirectly(Object value) { +            return new ContinueDirectly(value); +        } + +        public static PromiseResult stop() { +            return stop; +        } + +        public static PromiseResult detour(BtPromise promise) { +            return new Detour(promise); +        }      } -    private static class Continue extends PromiseResult { +    private static class WaitForNextEvent extends PromiseResult {      } -    private static class Done extends PromiseResult { +    private static class ContinueDirectly extends PromiseResult { +        private final Object value; + +        private ContinueDirectly(Object value) { +            this.value = value; +        } +    } + +    private static class Stop extends PromiseResult {      }      private static class Detour extends PromiseResult { @@ -52,18 +83,32 @@ public class BtPromise {          }      } -    public synchronized BtPromise onConnectionStateChange(F2<BluetoothGatt, Integer, PromiseResult> callback) { -        actionQ.add(new BtCallback("onConnectionStateChange") { +    private boolean stopOnFailure() { +        if(stopOnFailure != null) { +            boolean b = stopOnFailure; +            stopOnFailure = null; +            return b; +        } +        return false; +    } + +    public BtPromise ignoreFailureForNext() { +        stopOnFailure = true; +        return this; +    } +     +    public synchronized BtPromise onConnectionStateChange(F3<BluetoothGatt, Integer, Integer, PromiseResult> callback) { +        actionQ.add(new BtCallback(stopOnFailure(), "onConnectionStateChange") {              @Override -            public PromiseResult onConnectionStateChange(BluetoothGatt gatt, int newState) { -                return callback.apply(gatt, newState); +            public PromiseResult onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { +                return callback.apply(gatt, status,newState);              }          });          return this;      }      public synchronized BtPromise onServicesDiscovered(Function<BluetoothGatt, PromiseResult> callback) { -        actionQ.add(new BtCallback("onServicesDiscovered") { +        actionQ.add(new BtCallback(stopOnFailure(), "onServicesDiscovered") {              @Override              public PromiseResult onServicesDiscovered(BluetoothGatt gatt) {                  return callback.apply(gatt); @@ -73,7 +118,7 @@ public class BtPromise {      }      public synchronized BtPromise onCharacteristicRead(F2<BluetoothGatt, BluetoothGattCharacteristic, PromiseResult> callback) { -        actionQ.add(new BtCallback("onCharacteristicRead") { +        actionQ.add(new BtCallback(stopOnFailure(), "onCharacteristicRead") {              @Override              public PromiseResult onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {                  return callback.apply(gatt, characteristic); @@ -83,7 +128,7 @@ public class BtPromise {      }      public synchronized BtPromise onCharacteristicWrite(F2<BluetoothGatt, BluetoothGattCharacteristic, PromiseResult> callback) { -        actionQ.add(new BtCallback("onCharacteristicWrite") { +        actionQ.add(new BtCallback(stopOnFailure(), "onCharacteristicWrite") {              @Override              public PromiseResult onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {                  return callback.apply(gatt, characteristic); @@ -93,7 +138,7 @@ public class BtPromise {      }      public synchronized BtPromise onCharacteristicChanged(F2<BluetoothGatt, BluetoothGattCharacteristic, PromiseResult> callback) { -        actionQ.add(new BtCallback("onCharacteristicChanged") { +        actionQ.add(new BtCallback(stopOnFailure(), "onCharacteristicChanged") {              @Override              public PromiseResult onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {                  return callback.apply(gatt, characteristic); @@ -103,7 +148,7 @@ public class BtPromise {      }      public synchronized BtPromise onDescriptorRead(F2<BluetoothGatt, BluetoothGattDescriptor, PromiseResult> callback) { -        actionQ.add(new BtCallback("onDescriptorRead") { +        actionQ.add(new BtCallback(stopOnFailure(), "onDescriptorRead") {              @Override              public PromiseResult onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {                  return callback.apply(gatt, descriptor); @@ -113,7 +158,7 @@ public class BtPromise {      }      public synchronized BtPromise onDescriptorWrite(F2<BluetoothGatt, BluetoothGattDescriptor, PromiseResult> callback) { -        actionQ.add(new BtCallback("onDescriptorWrite") { +        actionQ.add(new BtCallback(stopOnFailure(), "onDescriptorWrite") {              @Override              public PromiseResult onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {                  return callback.apply(gatt, descriptor); @@ -123,7 +168,7 @@ public class BtPromise {      }      public synchronized BtPromise onReliableWriteCompleted(Function<BluetoothGatt, PromiseResult> callback) { -        actionQ.add(new BtCallback("onReliableWriteCompleted") { +        actionQ.add(new BtCallback(stopOnFailure(), "onReliableWriteCompleted") {              @Override              public PromiseResult onReliableWriteCompleted(BluetoothGatt gatt) {                  return callback.apply(gatt); @@ -133,7 +178,7 @@ public class BtPromise {      }      public synchronized BtPromise onReadRemoteRssi(F2<BluetoothGatt, Integer, PromiseResult> callback) { -        actionQ.add(new BtCallback("onReadRemoteRssi") { +        actionQ.add(new BtCallback(stopOnFailure(), "onReadRemoteRssi") {              @Override              public PromiseResult onReadRemoteRssi(BluetoothGatt gatt, int rssi) {                  return callback.apply(gatt, rssi); @@ -142,8 +187,18 @@ public class BtPromise {          return this;      } +    public synchronized BtPromise onDirect(Function<Object, PromiseResult> callback) { +        actionQ.add(new BtCallback(stopOnFailure(), "onDirect") { +            @Override +            public PromiseResult onDirect(Object value) { +                return callback.apply(value); +            } +        }); +        return this; +    } +      public synchronized BtPromise onFailure(Runnable callback) { -        failureQ.add(new BtCallback("onFailure") { +        failureQ.add(new BtCallback(stopOnFailure(), "onFailure") {              @Override              public void onFailure() {                  callback.run(); @@ -153,7 +208,7 @@ public class BtPromise {      }      public synchronized BtPromise onFinally(Runnable callback) { -        finallyQ.add(new BtCallback("finally") { +        finallyQ.add(new BtCallback(stopOnFailure(), "finally") {              @Override              public void onFinally() {                  callback.run(); @@ -180,12 +235,8 @@ public class BtPromise {          return s.toString();      } -    BtBluetoothGattCallback asCallback() { -        return new BtBluetoothGattCallback(actionQ, failureQ, finallyQ); -    } - -    public PromiseResult toDetour() { -        return new Detour(this); +    BtBluetoothGattCallback asCallback(String address) { +        return new BtBluetoothGattCallback(address, actionQ, failureQ, finallyQ);      }      enum EventType { @@ -200,16 +251,18 @@ public class BtPromise {          onReadRemoteRssi, +        onDirect,          onFailure,          onFinally,      }      private static PromiseResult callCallback(EventType key, BluetoothGatt gatt,                                                BluetoothGattCharacteristic characteristic, -                                              BluetoothGattDescriptor descriptor, int newState, BtCallback btCallback) { +                                              BluetoothGattDescriptor descriptor, int status, int newState, +                                              BtCallback btCallback, Object value) {          switch (key) {              case onConnectionStateChange: -                return btCallback.onConnectionStateChange(gatt, newState); +                return btCallback.onConnectionStateChange(gatt, status, newState);              case onServicesDiscovered:                  return btCallback.onServicesDiscovered(gatt);              case onCharacteristicRead: @@ -225,6 +278,8 @@ public class BtPromise {              case onReliableWriteCompleted:                  return btCallback.onReliableWriteCompleted(gatt); +            case onDirect: +                return btCallback.onDirect(value);              case onFailure:                  btCallback.onFailure();                  return null; @@ -239,23 +294,27 @@ public class BtPromise {      static class BtBluetoothGattCallback extends BluetoothGattCallback { -        private ArrayDeque<BtCallback> actionQ; +        private final String address; +        private List<BtCallback> actionQ; +        private ListIterator<BtCallback> it;          private ArrayDeque<BtCallback> failureQ;          ArrayDeque<BtCallback> finallyQ; -//        private ArrayDeque<BtCallback> initialActionQ;          private List<String> events = new ArrayList<>(); -        private BtBluetoothGattCallback(Queue<BtCallback> actionQ, Queue<BtCallback> failureQ, Queue<BtCallback> finallyQ) { -            this.actionQ = new ArrayDeque<>(actionQ); +        private BtBluetoothGattCallback(String address, List<BtCallback> actionQ, Queue<BtCallback> failureQ, Queue<BtCallback> finallyQ) { +            this.address = address; +            this.actionQ = new ArrayList<>(actionQ);              this.failureQ = new ArrayDeque<>(failureQ);              this.finallyQ = new ArrayDeque<>(finallyQ); +            this.it = actionQ.listIterator();          } -        void onEvent(EventType key, String values, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, int status, int newState) { +        void onEvent(EventType key, String values, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, +                     BluetoothGattDescriptor descriptor, int status, int newState, Object value) {              boolean success = status == BluetoothGatt.GATT_SUCCESS;              events.add(key + "(" + values + "), success=" + success); -            Log.i(TAG, key + "(" + values + "), success=" + success); +            Log.i(TAG, "event: " + key + "(" + values + "), success=" + success);              if (!success) {                  doFailure(); @@ -264,31 +323,33 @@ public class BtPromise {              BtCallback btCallback;              synchronized (this) { -                if (actionQ.isEmpty()) { +                if (!it.hasNext()) {                      Log.d(TAG, "All Bluetooth actions are done");                      doFinally();                      return;                  } +                btCallback = it.next(); +                Log.i(TAG, "Executing bt action: " + btCallback.name);                  try {                      Thread.sleep(1000);                  } catch (InterruptedException e) {                      // ignore                  } -                btCallback = actionQ.remove(); -                Log.i(TAG, "Executing bt action: " + btCallback.name);              }              try { -                PromiseResult result = callCallback(key, gatt, characteristic, descriptor, newState, btCallback); +                PromiseResult result = callCallback(key, gatt, characteristic, descriptor, status, newState, btCallback, +                        value); -                if (result instanceof Done) { -                    Log.i(TAG, "The chain is done."); +                if (result instanceof Stop) { +                    Log.i(TAG, "The chain want to stop.");                      doFinally();                  } else if (result instanceof Detour) {                      Log.i(TAG, "Adding detour");                      BtPromise promise = ((Detour) result).promise; +                    events.add("detour " + promise.actionQ.size());                      if (!promise.failureQ.isEmpty()) {                          Log.i(TAG, "Ignoring " + promise.failureQ.size() + " items from the failure Q");                      } @@ -296,12 +357,19 @@ public class BtPromise {                          Log.i(TAG, "Ignoring " + promise.finallyQ.size() + " items from the finally Q");                      } -                    for (BtCallback callback : promise.actionQ) { -                        actionQ.addFirst(callback); +                    synchronized (this) { +                        for (BtCallback cb : promise.actionQ) { +                            it.add(cb); +                        }                      } +                } else if (result instanceof ContinueDirectly) { +                    value = ((ContinueDirectly) result).value; +                    onEvent(onDirect, "value=" + value, null, null, null, BluetoothGatt.GATT_SUCCESS, 0, +                            value); +                    return;                  } -                if (actionQ.isEmpty()) { +                if (!it.hasNext()) {                      Log.i(TAG, "The queue is empty");                  }              } catch (NotOverriddenException e) { @@ -313,14 +381,15 @@ public class BtPromise {          private void doFailure() {              StringBuilder msg = new StringBuilder(); +            msg.append("Address: ").append(address).append("\n");              msg.append("Expected events: \n");              for (BtCallback cb : actionQ) { -                msg.append("  ").append(cb.name).append("\n"); +                msg.append("-  ").append(cb.name).append("\n");              }              msg.append("Actual events: \n");              for (String event : events) { -                msg.append("  ").append(event).append("\n"); +                msg.append("-  ").append(event).append("\n");              }              Log.w(TAG, msg.toString()); @@ -329,7 +398,7 @@ public class BtPromise {              for (BtCallback callback = failureQ.poll(); callback != null; callback = failureQ.poll()) {                  try { -                    callCallback(onFailure, null, null, null, 0, callback); +                    callCallback(onFailure, null, null, null, 0, 0, callback, null);                  } catch (NotOverriddenException e) {                      return;                  } @@ -345,7 +414,7 @@ public class BtPromise {              for (BtCallback callback = finallyQ.poll(); callback != null; callback = finallyQ.poll()) {                  try { -                    callCallback(onFinally, null, null, null, 0, callback); +                    callCallback(onFinally, null, null, null, 0, 0, callback, null);                  } catch (NotOverriddenException e) {                      return;                  } @@ -358,42 +427,42 @@ public class BtPromise {          @Override          public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { -            onEvent(onConnectionStateChange, "status=" + status + ", newState=" + newState, gatt, null, null, status, newState); +            onEvent(onConnectionStateChange, "status=" + status + ", newState=" + newState, gatt, null, null, status, newState, null);          }          @Override          public void onServicesDiscovered(BluetoothGatt gatt, int status) { -            onEvent(onServicesDiscovered, "status=" + status, gatt, null, null, status, 9); +            onEvent(onServicesDiscovered, "status=" + status, gatt, null, null, status, 9, null);          }          @Override          public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { -            onEvent(onCharacteristicRead, "status=" + status + ", characteristic=" + characteristic.getUuid(), gatt, characteristic, null, status, 0); +            onEvent(onCharacteristicRead, "status=" + status + ", characteristic=" + characteristic.getUuid(), gatt, characteristic, null, status, 0, null);          }          @Override          public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { -            onEvent(onCharacteristicWrite, "status=" + status + ", characteristic=" + characteristic.getUuid(), gatt, characteristic, null, status, 0); +            onEvent(onCharacteristicWrite, "status=" + status + ", characteristic=" + characteristic.getUuid(), gatt, characteristic, null, status, 0, null);          }          @Override          public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { -            onEvent(onCharacteristicChanged, "characteristic=" + characteristic.getUuid(), gatt, characteristic, null, BluetoothGatt.GATT_SUCCESS, 0); +            onEvent(onCharacteristicChanged, "characteristic=" + characteristic.getUuid(), gatt, characteristic, null, BluetoothGatt.GATT_SUCCESS, 0, null);          }          @Override          public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { -            onEvent(onDescriptorRead, "status=" + status + ", descriptor=" + descriptor.getUuid(), gatt, null, descriptor, status, 0); +            onEvent(onDescriptorRead, "status=" + status + ", descriptor=" + descriptor.getUuid(), gatt, null, descriptor, status, 0, null);          }          @Override          public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { -            onEvent(onDescriptorWrite, "status=" + status + ", descriptor=" + descriptor.getUuid(), gatt, null, descriptor, status, 0); +            onEvent(onDescriptorWrite, "status=" + status + ", descriptor=" + descriptor.getUuid(), gatt, null, descriptor, status, 0, null);          }          @Override          public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { -            onEvent(onReliableWriteCompleted, "status=" + status, gatt, null, null, status, 0); +            onEvent(onReliableWriteCompleted, "status=" + status, gatt, null, null, status, 0, null);          }          @Override 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 fb11027..ff2c68c 100644 --- a/app/src/main/java/io/trygvis/android/bt/BtService.java +++ b/app/src/main/java/io/trygvis/android/bt/BtService.java @@ -33,7 +33,7 @@ public interface BtService<A extends BtDevice.BtDeviceWrapper<A>> {      <T> T runTx(Function<SQLiteDatabase, T> action);      public static interface BtDbIntegration<A extends BtDevice.BtDeviceWrapper<A>> { -        A createTag(BtDevice<A> a); +        A createTag(SQLiteDatabase db, BtDevice<A> a);      }      public static class BtServiceListenerBroadcastReceiver extends BroadcastReceiver { 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 80a195a..cc7d484 100644 --- a/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java +++ b/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java @@ -289,7 +289,7 @@ public class DefaultBtService<A extends BtDevice.BtDeviceWrapper<A>> extends Ser              Log.i(TAG, "New device: " + address + ", seenBefore=" + seenBefore);              cursor.close(); -            return new BtDevice<>(this, bluetoothDevice, btDbIntegration, id, rssi, scanResult, +            return new BtDevice<>(this, bluetoothDevice, db, btDbIntegration, id, rssi, scanResult,                      seenBefore, firstSeen, lastSeen);          }); diff --git a/app/src/main/java/io/trygvis/android/bt/WrappingBluetoothGattCallback.java b/app/src/main/java/io/trygvis/android/bt/WrappingBluetoothGattCallback.java new file mode 100644 index 0000000..8d790d1 --- /dev/null +++ b/app/src/main/java/io/trygvis/android/bt/WrappingBluetoothGattCallback.java @@ -0,0 +1,60 @@ +package io.trygvis.android.bt; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; + +class WrappingBluetoothGattCallback extends BluetoothGattCallback { + +    private final BluetoothGattCallback wrapped; + +    public WrappingBluetoothGattCallback(BluetoothGattCallback wrapped) { +        this.wrapped = wrapped; +    } + +    @Override +    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { +        wrapped.onConnectionStateChange(gatt, status, newState); +    } + +    @Override +    public void onServicesDiscovered(BluetoothGatt gatt, int status) { +        wrapped.onServicesDiscovered(gatt, status); +    } + +    @Override +    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { +        wrapped.onCharacteristicRead(gatt, characteristic, status); +    } + +    @Override +    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { +        wrapped.onCharacteristicWrite(gatt, characteristic, status); +    } + +    @Override +    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { +        wrapped.onCharacteristicChanged(gatt, characteristic); +    } + +    @Override +    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { +        wrapped.onDescriptorRead(gatt, descriptor, status); +    } + +    @Override +    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { +        wrapped.onDescriptorWrite(gatt, descriptor, status); +    } + +    @Override +    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { +        wrapped.onReliableWriteCompleted(gatt, status); +    } + +    @Override +    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { +        wrapped.onReadRemoteRssi(gatt, rssi, status); +    } +} diff --git a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java index a2723d5..3ff6e66 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java +++ b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java @@ -9,12 +9,17 @@ import android.content.ComponentName;  import android.content.ContentValues;  import android.content.Intent;  import android.content.ServiceConnection; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase;  import android.os.IBinder;  import android.util.Log;  import java.util.ArrayList;  import java.util.Comparator; +import java.util.Date; +import java.util.HashMap;  import java.util.List; +import java.util.Map;  import java.util.Set;  import java.util.TreeSet; @@ -25,15 +30,24 @@ import io.trygvis.android.bt.BtService;  import io.trygvis.android.bt.DefaultBtService;  import io.trygvis.bluetooth.TrygvisIoUuids; -import static io.trygvis.android.bt.BtPromise.continueDownChain; -import static io.trygvis.android.bt.BtPromise.doneWithChain; +import static io.trygvis.android.bt.BtPromise.PromiseResult.continueDirectly; +import static io.trygvis.android.bt.BtPromise.PromiseResult.detour; +import static io.trygvis.android.bt.BtPromise.PromiseResult.stop; +import static io.trygvis.android.bt.BtPromise.PromiseResult.waitForNextEvent;  import static io.trygvis.android.bt.BtService.BtServiceListenerBroadcastReceiver; +import static io.trygvis.bluetooth.TrygvisIoUuids.CLIENT_CHARACTERISTIC_CONFIG;  import static io.trygvis.soilmoisture.SmDevice.GetSensorCountRes; +import static io.trygvis.soilmoisture.SmDevice.GetValueRes;  import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.GET_SENSOR_COUNT; +import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.GET_VALUE; +import static io.trygvis.soilmoisture.SmDevice.createGetSensorCountReq; +import static io.trygvis.soilmoisture.SmDevice.createGetValueReq; +import static io.trygvis.soilmoisture.SmDevice.parseResponse; +import static java.lang.String.valueOf; +import static java.lang.System.currentTimeMillis;  public class DefaultSoilMoistureService extends Service implements SoilMoistureService {      private final static String TAG = DefaultSoilMoistureService.class.getSimpleName(); -    private final static int DEFAULT_WARNING_LEVEL = 750;      private final IBinder binder = new LocalBinder<>(this); @@ -58,7 +72,7 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS              @Override              public void onServiceConnected(ComponentName componentName, IBinder service) {                  btService = ((LocalBinder<BtService<SmDevice>>) service).getService(); -                boolean ok = btService.initialize(DefaultSoilMoistureService.this::onNewDevice); +                boolean ok = btService.initialize(DefaultSoilMoistureService.this::createTag);                  sendBroadcast(createReady(ok));              } @@ -98,28 +112,16 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS              BtDevice<SmDevice> btDevice = btService.getDevice(address);              SmDevice smDevice = btDevice.getTag(); +            sendBroadcast(createNewDevice(address)); +              if (!smDevice.isProbed()) {                  Log.i(TAG, "Probing " + address + ", name=" + btDevice.getName());                  BtPromise executor = new BtPromise(). -                        onConnectionStateChange((gatt, newState) -> { -                            //noinspection SimplifiableIfStatement -                            if (newState == BluetoothGatt.STATE_CONNECTED) { -                                Log.i(TAG, "Connected to " + address + ", getting services"); -                                return gatt.discoverServices() ? continueDownChain : doneWithChain; -                            } else { -                                Log.i(TAG, "Disconnected from " + address + ", trying again"); - -                                return new BtPromise().onConnectionStateChange((gatt2, newState2) -> { -                                    if (newState2 == BluetoothGatt.STATE_CONNECTED) { -                                        Log.i(TAG, "Connected to " + address + ", getting services"); -                                        return continueDownChain; -                                    } - -                                    Log.i(TAG, "Could still not connect to " + address + ", failing."); - -                                    return doneWithChain; -                                }).toDetour(); -                            } +                        ignoreFailureForNext(). +                        onConnectionStateChange(DefaultSoilMoistureService::defaultConnectCallback). +                        onDirect(v -> { +                            BluetoothGatt gatt = (BluetoothGatt) v; +                            return gatt.discoverServices() ? waitForNextEvent() : stop();                          }).                          onServicesDiscovered(gatt -> {                              Log.i(TAG, "Services discovered"); @@ -127,38 +129,37 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS                              BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE);                              if (service == null) { -                                return doneWithChain; +                                return stop();                              }                              BluetoothGattCharacteristic soilMoisture = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE);                              if (soilMoisture == null) { -                                return doneWithChain; +                                return stop();                              }                              BluetoothGattDescriptor ccg = soilMoisture.getDescriptor(TrygvisIoUuids.CLIENT_CHARACTERISTIC_CONFIG);                              ccg.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);                              gatt.setCharacteristicNotification(soilMoisture, true); -                            return gatt.writeDescriptor(ccg) ? continueDownChain : doneWithChain; +                            return gatt.writeDescriptor(ccg) ? waitForNextEvent() : stop();                          }).                          onDescriptorWrite((gatt, descriptor) -> {                              Log.i(TAG, "Notifications enabled, getting sensor count");                              BluetoothGattCharacteristic c = descriptor.getCharacteristic(); -                            c.setValue(SmDevice.createGetSensorCountReq()); -                            return gatt.writeCharacteristic(c) ? continueDownChain : doneWithChain; +                            c.setValue(createGetSensorCountReq()); +                            return gatt.writeCharacteristic(c) ? waitForNextEvent() : stop();                          }). -                        onCharacteristicWrite((gatt, characteristic) -> continueDownChain). +                        onCharacteristicWrite((gatt, characteristic) -> waitForNextEvent()).                          onCharacteristicChanged((gatt, characteristic) -> { -                            GetSensorCountRes getSensorCountRes = SmDevice.parseResponse(characteristic.getValue(), +                            GetSensorCountRes getSensorCountRes = parseResponse(characteristic.getValue(),                                      GET_SENSOR_COUNT, GetSensorCountRes.class);                              Log.i(TAG, "The device has " + getSensorCountRes.count + " sensors."); -                            smDevice.setIsUseful(true); -                            smDevice.setSensorCount(getSensorCountRes.count); +                            markDeviceAsUseful(smDevice, getSensorCountRes.count); -                            return doneWithChain; +                            return stop();                          }).                          onFinally(() -> {                              btDevice.disconnect(); @@ -167,28 +168,69 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS                                  smDevice.setIsUseful(false);                              } -                            btService.runTx(db -> { -                                if (!btDevice.isSeenBefore() && smDevice.isUseful()) { -                                    for (SmSensor soilMonitor : smDevice.getSensors()) { -                                        ContentValues values = new ContentValues(); -                                        values.put("bt_device", btDevice.getId()); -                                        values.put("warning_level", DEFAULT_WARNING_LEVEL); -                                        db.insert("soil_monitor", null, values); -                                    } -                                } - -                                return null; -                            }); -                            sendBroadcast(createNewDevice(address));                          });                  btDevice.connect(executor); -            } else { -                sendBroadcast(createNewDevice(address));              }          }      }; +    private void markDeviceAsUseful(SmDevice device, int sensorCount) { +        btService.runTx(db -> { +            device.setIsUseful(true); + +            // Find all already registered sensors and register the rest +            Map<Integer, Long> indexes = new HashMap<>(); +            Cursor cursor = db.query(Tables.T_SM_SENSOR, new String[]{Tables.C_ID, Tables.C_INDEX}, +                    Tables.C_SM_DEVICE + "=?", new String[]{valueOf(device.id)}, null, null, null); +            while (cursor.moveToNext()) { +                indexes.put(cursor.getInt(1), cursor.getLong(0)); +            } + +            ContentValues values = new ContentValues(); +            values.put(Tables.C_USEFUL, true); +            db.update(Tables.T_SM_DEVICE, values, "id=?", new String[]{valueOf(device.id)}); + +            for (int i = 0; i < sensorCount; i++) { +                Long id = indexes.get(i); + +                if (id == null) { +                    values = new ContentValues(); +                    values.put(Tables.C_SM_DEVICE, device.id); +                    values.put(Tables.C_INDEX, i); +                    id = db.insert(Tables.T_SM_SENSOR, null, values); +                } + +                device.addSensor(new SmSensor(device, id, (byte) i)); +            } + +            return null; +        }); +    } + +    public static BtPromise.PromiseResult defaultConnectCallback(BluetoothGatt gatt, int status, Integer newState) { +        Log.i(TAG, "defaultConnectCallback: status=" + status + ", newState=" + newState); +        String address = gatt.getDevice().getAddress(); +        //noinspection SimplifiableIfStatement +        if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) { +            Log.i(TAG, "Connected to " + address); +            return continueDirectly(gatt); +        } else { +            Log.i(TAG, "Disconnected from " + address + ", trying again"); + +            return detour(new BtPromise().onConnectionStateChange((gatt2, status2, newState2) -> { +                if (newState2 == BluetoothGatt.STATE_CONNECTED) { +                    Log.i(TAG, "Connected to " + address); +                    return continueDirectly(gatt); +                } + +                Log.i(TAG, "Could still not connect to " + address + ", failing."); + +                return stop(); +            })); +        } +    } +      // -----------------------------------------------------------------------      // SmDevicesManager Implementation      // ----------------------------------------------------------------------- @@ -223,13 +265,82 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS      }      // ----------------------------------------------------------------------- -    // Event creation and dispatching +    //      // ----------------------------------------------------------------------- -    private SmDevice onNewDevice(BtDevice<SmDevice> btDevice) { -        return new SmDevice(btDevice); +    private SmDevice createTag(SQLiteDatabase db, BtDevice<SmDevice> btDevice) { +        Cursor cursor = db.query(Tables.T_SM_DEVICE, new String[]{Tables.C_ID}, Tables.C_BT_DEVICE + "=?", +                new String[]{valueOf(btDevice.getId())}, null, null, null); + +        long id; +        if (cursor.moveToNext()) { +            id = cursor.getLong(0); +        } else { +            ContentValues values = new ContentValues(); +            values.put(Tables.C_BT_DEVICE, btDevice.getId()); +            id = db.insert(Tables.T_SM_DEVICE, null, values); +        } + +        return new SmDevice(this, btDevice, id); +    } + +    void handleNewSensorValueReady(SmSensor sensor, int value) { +        long timestamp = currentTimeMillis(); +        btService.runTx(db -> { +            ContentValues values = new ContentValues(); +            values.put(Tables.C_SM_SENSOR, sensor.getId()); +            values.put(Tables.C_TIMESTAMP, timestamp); +            values.put(Tables.C_VALUE, value); +            return db.insert(Tables.T_SOIL_SAMPLE, null, values); +        }); + +        sensor.updateLastValue(new Date(timestamp), value); +        sendBroadcast(createNewSample(sensor)); +    } + +    void readCurrentValue(SmSensor sensor) { +        BtPromise promise = new BtPromise(). +                onConnectionStateChange(DefaultSoilMoistureService::defaultConnectCallback). +                onDirect(v -> { +                    BluetoothGatt gatt = (BluetoothGatt) v; +                    return gatt.discoverServices() ? waitForNextEvent() : stop(); +                }). +                onServicesDiscovered(gatt -> { +                    BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE); +                    BluetoothGattCharacteristic soilMoisture = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE); + +                    BluetoothGattDescriptor ccg = soilMoisture.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG); +                    ccg.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); +                    gatt.setCharacteristicNotification(soilMoisture, true); + +                    return gatt.writeDescriptor(ccg) ? waitForNextEvent() : stop(); +                }). +                onDescriptorWrite((gatt, descriptor) -> { +                    BluetoothGattCharacteristic c = descriptor.getCharacteristic(); +                    c.setValue(createGetValueReq((byte) sensor.getIndex())); +                    return gatt.writeCharacteristic(c) ? waitForNextEvent() : stop(); +                }). +                onCharacteristicWrite((gatt, characteristic) -> waitForNextEvent()). +                onCharacteristicChanged((gatt, characteristic) -> { +                    GetValueRes getSensorCountRes = parseResponse(characteristic.getValue(), +                            GET_VALUE, GetValueRes.class); + +                    handleNewSensorValueReady(sensor, getSensorCountRes.value); + +                    gatt.disconnect(); + +                    return stop(); +                }). +                onFinally(() -> { +                }); + +        sensor.getDevice().getBtDevice().connect(promise);      } +    // ----------------------------------------------------------------------- +    // Event creation and dispatching +    // ----------------------------------------------------------------------- +      private Intent createReady(boolean success) {          return new Intent(SoilMoistureListener.INTENT_NAME).                  putExtra("event", "ready"). @@ -252,15 +363,24 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS                  putExtra("address", address);      } +    private Intent createNewSample(SmSensor sensor) { +        return new Intent(SoilMoistureListener.INTENT_NAME). +                putExtra("event", "newSample"). +                putExtra("address", sensor.getDevice().getBtDevice().getAddress()). +                putExtra("index", sensor.getIndex()); +    } +      public static void dispatchEvent(Intent intent, SoilMoistureListener listener) {          String event = intent.getStringExtra("event");          Log.i(TAG, "Dispatching event " + intent.getAction() + "/" + event);          switch (event) {              case "ready": -                listener.onReady(intent.getBooleanExtra("success", false)); +                listener.onReady( +                        intent.getBooleanExtra("success", false));                  break;              case "newDevice": -                listener.onNewDevice(intent.getStringExtra("address")); +                listener.onNewDevice( +                        intent.getStringExtra("address"));                  break;              case "scanStarted":                  listener.onScanStarted(); @@ -268,6 +388,11 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS              case "scanStopped":                  listener.onScanStopped();                  break; +            case "newSample": +                listener.onNewSample( +                        intent.getStringExtra("address"), +                        intent.getIntExtra("index", -1)); +                break;              default:                  break;          } diff --git a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java index 7aa3534..3a88f6c 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java +++ b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java @@ -16,9 +16,7 @@ import android.view.Menu;  import android.view.MenuItem;  import android.view.View;  import android.view.ViewGroup; -import android.widget.Button;  import android.widget.ListAdapter; -import android.widget.ListView;  import android.widget.ProgressBar;  import android.widget.TextView;  import android.widget.Toast; @@ -209,9 +207,18 @@ public class MainActivity extends ListActivity {          }      } -    @Override -    protected void onListItemClick(ListView l, View v, int position, long id) { -        stopScan(); +    // ----------------------------------------------------------------------- +    // +    // ----------------------------------------------------------------------- + +    private void onDeviceClick(SmDevice device) { +        Log.i(TAG, "onDeviceClick, device=" + device.getBtDevice().getId()); +    } + +    private void onSensorClick(SmSensor sensor) { +        Log.i(TAG, "onSensorClick, device=" + sensor.getDevice().getBtDevice().getId() + "/" + sensor.getIndex()); + +        sensor.readCurrentValue();      }      // ----------------------------------------------------------------------- @@ -223,16 +230,25 @@ public class MainActivity extends ListActivity {          final TextView deviceAddress;          final TextView rssi;          final TextView info; -        final ProgressBar spinner; -        final Button connect;          DeviceItem(View view) {              this.deviceName = (TextView) view.findViewById(R.id.device_name);              this.deviceAddress = (TextView) view.findViewById(R.id.device_address);              this.rssi = (TextView) view.findViewById(R.id.device_rssi);              this.info = (TextView) view.findViewById(R.id.device_info); -            this.spinner = (ProgressBar) view.findViewById(R.id.device_spinner); -            this.connect = (Button) view.findViewById(R.id.button_connect); +        } +    } + +    static class SensorItem { +        final SmSensor sensor; +        final TextView description; +        final ProgressBar sensorProgress; + +        SensorItem(SmSensor sensor, View view) { +            this.sensor = sensor; +            this.description = (TextView) view.findViewById(R.id.description); +            this.sensorProgress = (ProgressBar) view.findViewById(R.id.sensor_progress); +            sensorProgress.setMax(1024);          }      } @@ -254,18 +270,24 @@ public class MainActivity extends ListActivity {              for (SmDevice d : devices) {                  (d.isUseful() ? usefulDevices : unusefulDevices).add(d);              } -            List<SmSensor> monitors = new ArrayList<>(); +            List<SmSensor> sensors = new ArrayList<>();              for (SmDevice d : devices) { -                monitors.addAll(d.getSensors()); +                if (d.isUseful()) { +                    sensors.addAll(d.getSensors()); +                }              }              if (groupByDevice) { -                current.addAll(usefulDevices); +//                current.addAll(usefulDevices); +                for (SmDevice device : usefulDevices) { +                    current.add(device); +                    current.addAll(device.getSensors()); +                }                  if (showAll) {                      current.addAll(unusefulDevices);                  }              } else { -                current.addAll(monitors); +                current.addAll(sensors);              }              dataSetObservable.notifyChanged();          } @@ -374,50 +396,77 @@ public class MainActivity extends ListActivity {              if (o instanceof SmDevice) {                  return getSmDeviceView((SmDevice) o, view);              } else if (o instanceof SmSensor) { -                return getSoilMonitorView((SmSensor) o, view); +                return getSoilSensorView((SmSensor) o, view);              }              throw new RuntimeException("Not implemented");          }          private View getSmDeviceView(SmDevice smDevice, View view) { - -            DeviceItem item;              if (view == null) { -                view = inflater.inflate(R.layout.listitem_device, null); -                item = new DeviceItem(view); -                view.setTag(item); -                view.setClickable(false); -            } else { -                item = (DeviceItem) view.getTag(); +                view = inflater.inflate(R.layout.fragment_device, null); +                view.setTag(new DeviceItem(view)); +                view.setClickable(true); +                view.setOnClickListener(v -> onDeviceClick(smDevice));              } +            DeviceItem item = (DeviceItem) view.getTag(); +              if (smDevice.getName() != null) {                  item.deviceName.setText(smDevice.getName());              } else {                  item.deviceName.setText(R.string.unknown_device);              } -            item.deviceAddress.setText(smDevice.getBtDevice().getAddress()); +            String address = smDevice.getBtDevice().getAddress(); + +            if (!smDevice.isProbed()) { +                address += " not probed"; +            } else if (smDevice.isUseful()) { +                address += " useful"; +            } else { +                address += " not useful"; +            } + +            item.deviceAddress.setText(address);              item.rssi.setText(getText(R.string.rssi) + ": " +                      (smDevice.getBtDevice().getRssi() != 0 ? valueOf(smDevice.getBtDevice().getRssi()) : getText(R.string.unknown))); -            if (smDevice.isUseful()) { -                item.info.setText("number of sensors: " + smDevice.getSensors().size()); +            boolean useful = smDevice.isUseful(); + +            if (useful) { +                item.info.setText("Number of sensors: " + smDevice.getSensors().size());              } else {                  item.info.setText("");              } -            boolean useful = smDevice.isUseful(); -            item.spinner.setVisibility(useful ? View.GONE : View.VISIBLE); -            item.connect.setVisibility(useful ? View.VISIBLE : View.GONE); -            view.setClickable(useful); +//            view.setClickable(useful);              return view;          } -        private View getSoilMonitorView(SmSensor smSensor, View view) { -            throw new RuntimeException("Not implemented"); +        private View getSoilSensorView(SmSensor smSensor, View view) { +            if (view == null) { +                view = inflater.inflate(R.layout.fragment_sensor, null); +                view.setTag(new SensorItem(smSensor, view)); +                view.setClickable(true); +                view.setOnClickListener(v -> onSensorClick(smSensor)); +            } + +            SensorItem item = (SensorItem) view.getTag(); + +            Integer value = smSensor.getLastValue(); +            String text = "Connected: " + smSensor.getDevice().getBtDevice().connected(); +            text += ", value: " + (value == null ? "Unknown" : value); +            item.description.setText(text); + +            if (value != null) { +                item.sensorProgress.setProgress(value); +            } else { +                item.sensorProgress.setIndeterminate(true); +            } + +            return view;          }      } @@ -459,5 +508,10 @@ public class MainActivity extends ListActivity {              deviceList.devices.add(device);              deviceList.sort();          } + +        @Override +        public void onNewSample(String address, int sensor) { +            deviceList.notifyDataSetChanged(); +        }      }  } diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java index b51b3fa..4cb25f3 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java +++ b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java @@ -18,16 +18,22 @@ import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.SET_WARNING_VALUE;  class SmDevice implements BtDevice.BtDeviceWrapper<SmDevice> {      private final static String TAG = SmDevice.class.getSimpleName(); +    final DefaultSoilMoistureService smService; +      private final BtDevice<SmDevice> btDevice; +    final long id; +      private String name;      private Boolean isUseful;      private List<SmSensor> sensors = new ArrayList<>(); -    public SmDevice(BtDevice<SmDevice> btDevice) { +    public SmDevice(DefaultSoilMoistureService smService, BtDevice<SmDevice> btDevice, long id) { +        this.smService = smService;          this.btDevice = btDevice; +        this.id = id;          Log.i(TAG, "new device");          name = btDevice.getName(); @@ -63,14 +69,21 @@ class SmDevice implements BtDevice.BtDeviceWrapper<SmDevice> {      }      public List<SmSensor> getSensors() { +        if (!isUseful()) { +            throw new IllegalStateException("Not a useful device"); +        }          return sensors;      } +    void addSensor(SmSensor sensor) { +        sensors.add(sensor); +    } +      // ----------------------------------------------------------------------- -    // +    // Message parsing and handling.      // ----------------------------------------------------------------------- -    @SuppressWarnings("unchecked") +    @SuppressWarnings({"unchecked", "StatementWithEmptyBody"})      public static <T> T parseResponse(byte[] bytes, SmCmdCode code, Class<T> klass) {          byte c = bytes[0]; @@ -78,10 +91,10 @@ class SmDevice implements BtDevice.BtDeviceWrapper<SmDevice> {              throw new RuntimeException("Expected response of type " + code + ", got " + c);          } -        Object value = null;          if (c == GET_SENSOR_COUNT.code) {              return (T) new GetSensorCountRes(bytes[1]);          } else if (c == GET_VALUE.code) { +            return (T) new GetValueRes((bytes[2] & 0xff) << 8 | (bytes[1] & 0xff));          } else if (c == SET_WARNING_VALUE.code) {          } else if (c == GET_WARNING_VALUE.code) {          } else if (c == SET_SENSOR_NAME.code) { @@ -91,13 +104,6 @@ class SmDevice implements BtDevice.BtDeviceWrapper<SmDevice> {          throw new RuntimeException("Unknown code: " + c);      } -    public void setSensorCount(int count) { -        sensors = new ArrayList<>(); -        for (int index = 0; index < count; index++) { -            sensors.add(new SmSensor(this, index)); -        } -    } -      public static class GetSensorCountRes {          public final int count; @@ -106,6 +112,14 @@ class SmDevice implements BtDevice.BtDeviceWrapper<SmDevice> {          }      } +    public static class GetValueRes { +        public final int value; + +        public GetValueRes(int value) { +            this.value = value; +        } +    } +      public static byte[] createGetSensorCountReq() {          return new byte[]{                  GET_SENSOR_COUNT.code diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmSensor.java b/app/src/main/java/io/trygvis/soilmoisture/SmSensor.java index ec4b423..2a0d9cd 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/SmSensor.java +++ b/app/src/main/java/io/trygvis/soilmoisture/SmSensor.java @@ -5,38 +5,57 @@ import java.util.Date;  class SmSensor {      private final SmDevice device; -    private final int index; +    public final long id; + +    public final int index; + +    private String name;      private Date timestamp; -    private int lastValue; +    private Integer lastValue; -    SmSensor(SmDevice device, int index) { +    SmSensor(SmDevice device, long id, int index) {          this.device = device; +        this.id = id;          this.index = index; +        this.name = "Sensor #" + index;      }      public SmDevice getDevice() {          return device;      } +    public long getId() { +        return id; +    } +      public int getIndex() {          return index;      } -    public int getLastValue() { -        return lastValue; +    public String getName() { +        return name;      } -    public void setLastValue(int lastValue) { -        this.lastValue = lastValue; +    public Integer getLastValue() { +        return lastValue;      }      public Date getTimestamp() {          return timestamp;      } -    public void setTimestamp(Date timestamp) { +    // ----------------------------------------------------------------------- +    // +    // ----------------------------------------------------------------------- + +    public void readCurrentValue() { +        device.smService.readCurrentValue(this); +    } + +    void updateLastValue(Date timestamp, int lastValue) {          this.timestamp = timestamp; +        this.lastValue = lastValue;      }  } diff --git a/app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java b/app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java index 8100649..643fa08 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java +++ b/app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java @@ -25,7 +25,7 @@ public interface SoilMoistureService {          public static final IntentFilter INTENT_FILTER = new IntentFilter(INTENT_NAME); -        public void onReceive(Context context, Intent intent) { +        public final void onReceive(Context context, Intent intent) {              if (!intent.getAction().equals(INTENT_NAME)) {                  return;              } @@ -44,5 +44,8 @@ public interface SoilMoistureService {          public void onScanStopped() {          } + +        public void onNewSample(String address, int sensor) { +        }      }  } diff --git a/app/src/main/java/io/trygvis/soilmoisture/Tables.java b/app/src/main/java/io/trygvis/soilmoisture/Tables.java new file mode 100644 index 0000000..56e5041 --- /dev/null +++ b/app/src/main/java/io/trygvis/soilmoisture/Tables.java @@ -0,0 +1,24 @@ +package io.trygvis.soilmoisture; + +public class Tables { +    public static String T_SM_DEVICE = "sm_device"; +    public static String T_SM_SENSOR = "sm_sensor"; +    public static String T_SOIL_SAMPLE = "soil_sample"; + +    // sm_device +    public static String C_ID = "id"; +    public static String C_BT_DEVICE = "bt_device"; +    public static String C_USEFUL = "useful"; +//    public static String C_NAME = "name"; + +    // sm_sensor +    public static String C_SM_DEVICE = "sm_device"; +    // name +    public static String C_WARNING_VALUE = "warning_value"; + +    // soil_sample +    public static String C_SM_SENSOR = "sm_sensor"; +    public static String C_INDEX = "idx"; +    public static String C_TIMESTAMP = "timestamp"; +    public static String C_VALUE = "value"; +} | 
