aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/src/main/AndroidManifest.xml16
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtDevice.java49
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtPromise.java12
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtScanResult.java9
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtService.java3
-rw-r--r--app/src/main/java/io/trygvis/android/bt/DefaultBtService.java77
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java15
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/MainActivity.java191
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/SensorActivity.java134
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/SmDevice.java4
-rw-r--r--app/src/main/res/layout/activity_sensor.xml7
-rw-r--r--app/src/main/res/layout/fragment_device.xml11
-rw-r--r--app/src/main/res/layout/fragment_main_sensor.xml28
-rw-r--r--app/src/main/res/layout/fragment_sensor.xml34
-rw-r--r--app/src/main/res/menu/menu_sensor.xml8
-rw-r--r--app/src/main/res/values/colors.xml6
-rw-r--r--app/src/main/res/values/strings.xml1
17 files changed, 453 insertions, 152 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 500bd8d..3edd865 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="io.trygvis.soilmoisture">
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="io.trygvis.soilmoisture">
<!--
Declare this required feature if you want to make the app available to BLE-capable
@@ -21,7 +22,6 @@
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
-
<activity
android:name=".MainActivity"
android:label="@string/app_name">
@@ -31,7 +31,6 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
-
<activity
android:name=".SoilActivity"
android:label="@string/title_activity_soil">
@@ -45,7 +44,6 @@
android:name="io.trygvis.android.bt.DefaultBtService.migration"
android:value="db/migration/sm"/>
</service>
-
<service
android:name=".DefaultSoilMoistureService"
android:enabled="true"
@@ -56,6 +54,14 @@
android:name="com.crashlytics.ApiKey"
android:value="cf760ececcb6d74c66781b3e21ae115aaae3ffd3"/>
+ <activity
+ android:name=".SensorActivity"
+ android:label="@string/title_activity_sensor"
+ android:parentActivityName=".MainActivity">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="io.trygvis.soilmoisture.MainActivity"/>
+ </activity>
</application>
</manifest>
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 fd9e2b9..b90ac4f 100644
--- a/app/src/main/java/io/trygvis/android/bt/BtDevice.java
+++ b/app/src/main/java/io/trygvis/android/bt/BtDevice.java
@@ -15,14 +15,13 @@ import static io.trygvis.android.bt.BtPromise.PromiseResult.detour;
import static io.trygvis.android.bt.BtPromise.PromiseResult.fail;
import static io.trygvis.android.bt.BtPromise.PromiseResult.waitForNextEvent;
-public class BtDevice<A> {
+public class BtDevice<A> implements Comparable<BtDevice> {
private final static String TAG = BtDevice.class.getSimpleName();
private final DefaultBtService btService;
private final BluetoothDevice bluetoothDevice;
private BluetoothGatt gatt;
private Integer rssi;
- private BtScanResult scanResult;
private A tag;
private final String address;
@@ -30,6 +29,10 @@ public class BtDevice<A> {
private final boolean seenBefore;
private final Date firstSeen;
private Date lastSeen;
+ /**
+ * If seen in last scan.
+ */
+ private boolean recentlySeen;
private boolean connected;
private final WrappingBluetoothGattCallback wrappingCallback = new WrappingBluetoothGattCallback();
@@ -37,15 +40,15 @@ public class BtDevice<A> {
BtDevice(DefaultBtService btService, BluetoothDevice bluetoothDevice, SQLiteDatabase db,
BtService.BtDbIntegration<A> btDbIntegration, long id, Integer rssi,
- BtScanResult scanResult, boolean seenBefore, Date firstSeen, Date lastSeen) {
+ boolean seenBefore, Date firstSeen, Date lastSeen, boolean recentlySeen) {
this.btService = btService;
this.bluetoothDevice = bluetoothDevice;
this.id = id;
this.rssi = rssi;
- this.scanResult = scanResult;
this.seenBefore = seenBefore;
this.firstSeen = firstSeen;
this.lastSeen = lastSeen;
+ this.recentlySeen = recentlySeen;
this.tag = btDbIntegration.createTag(db, this);
this.address = bluetoothDevice.getAddress();
@@ -60,7 +63,7 @@ public class BtDevice<A> {
}
public String getAddress() {
- return bluetoothDevice.getAddress();
+ return address;
}
public String getName() {
@@ -71,10 +74,6 @@ public class BtDevice<A> {
return rssi;
}
- public BtScanResult getScanResult() {
- return scanResult;
- }
-
public boolean isSeenBefore() {
return seenBefore;
}
@@ -91,6 +90,14 @@ public class BtDevice<A> {
this.lastSeen = lastSeen;
}
+ public boolean isRecentlySeen() {
+ return recentlySeen;
+ }
+
+ public void setRecentlySeen(boolean recentlySeen) {
+ this.recentlySeen = recentlySeen;
+ }
+
/**
* The first handler must handle a onDirect().
* <p>
@@ -101,7 +108,7 @@ public class BtDevice<A> {
throw new RuntimeException("The current callback is not done.");
}
- Log.i(TAG, "withConnection(), address=" + bluetoothDevice.getAddress() + ", connected: " + (gatt != null));
+ Log.i(TAG, "withConnection(), address=" + address + ", connected: " + (gatt != null));
BtPromise newPromise;
if (gatt == null) {
@@ -160,7 +167,7 @@ public class BtDevice<A> {
@Override
public String toString() {
- return "BtDevice{address=" + bluetoothDevice.getAddress() + '}';
+ return "BtDevice{address=" + address + '}';
}
@Override
@@ -174,12 +181,17 @@ public class BtDevice<A> {
BtDevice other = (BtDevice) o;
- return getAddress().equals(other.getAddress());
+ return address.equals(other.getAddress());
}
@Override
public int hashCode() {
- return getAddress().hashCode();
+ return address.hashCode();
+ }
+
+ @Override
+ public int compareTo(BtDevice that) {
+ return address.compareTo(that.address);
}
private class WrappingBluetoothGattCallback extends BluetoothGattCallback {
@@ -196,6 +208,11 @@ public class BtDevice<A> {
BtDevice.this.connected = status == BluetoothGatt.GATT_SUCCESS &&
newState == BluetoothGatt.STATE_CONNECTED;
+ if (oldConnected && BtDevice.this.connected) {
+ Log.i(TAG, "Wrapping: Extra 'onConnectionStateChange' event, ignoring. gatt=" + gatt);
+ return;
+ }
+
try {
if (callback != null) {
callback.onConnectionStateChange(gatt, status, newState);
@@ -203,14 +220,14 @@ public class BtDevice<A> {
} finally {
if (!BtDevice.this.connected) {
if (oldConnected) {
- Log.i(TAG, "Wrapper: Lost connection, removing gatt. gatt=" + gatt);
+ Log.i(TAG, "Wrapping: Lost connection, removing gatt. gatt=" + gatt);
} else {
- Log.i(TAG, "Wrapper: Lost connection, was not connected. gatt=" + gatt);
+ Log.i(TAG, "Wrapping: Lost connection, was not connected. gatt=" + gatt);
}
BtDevice.this.gatt = null;
} else {
- Log.i(TAG, "Wrapper: connected");
+ Log.i(TAG, "Wrapping: connected");
}
}
}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtPromise.java b/app/src/main/java/io/trygvis/android/bt/BtPromise.java
index 50aad13..bffdd19 100644
--- a/app/src/main/java/io/trygvis/android/bt/BtPromise.java
+++ b/app/src/main/java/io/trygvis/android/bt/BtPromise.java
@@ -371,17 +371,11 @@ public class BtPromise {
if (result instanceof Detour) {
BtPromise detour = ((Detour) result).promise;
- Log.i(TAG, "Adding detour with " + detour.actionQ.size() + " actions.");
+// Log.i(TAG, "Adding detour with " + detour.actionQ.size() + " actions.");
events.add("detour, action size=" + detour.actionQ.size() + ", " +
// "failure size=" + detour.failureQ.size() + ", " +
"finally size=" + detour.finallyQ.size());
- Log.i(TAG, "hasNext(): " + hasNext());
- Log.i(TAG, "currentAction: " + currentAction);
- if (hasNext()) {
- Log.i(TAG, "next action: " + actionQ.get(currentAction).name);
- }
-
// The new promise should probably be stacked on top, so that all of its
// finally handlers are executed after the added set concludes and then the
// current stack can continue.
@@ -389,10 +383,6 @@ public class BtPromise {
actionQ.addAll(currentAction, detour.actionQ);
// failureQ.addAll(detour.failureQ);B
finallyQ.addAll(detour.finallyQ);
- Log.i(TAG, "hasNext(): " + hasNext());
- if (hasNext()) {
- Log.i(TAG, "next action: " + actionQ.get(currentAction).name);
- }
result = PromiseResult.continueDirectly();
}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtScanResult.java b/app/src/main/java/io/trygvis/android/bt/BtScanResult.java
deleted file mode 100644
index c443afa..0000000
--- a/app/src/main/java/io/trygvis/android/bt/BtScanResult.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package io.trygvis.android.bt;
-
-public class BtScanResult {
- private final byte[] scanRecord;
-
- public BtScanResult(byte[] scanRecord) {
- this.scanRecord = scanRecord;
- }
-}
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 06857ee..46f1a80 100644
--- a/app/src/main/java/io/trygvis/android/bt/BtService.java
+++ b/app/src/main/java/io/trygvis/android/bt/BtService.java
@@ -61,5 +61,8 @@ public interface BtService<A> {
public void onDeviceConnection(String address) {
}
+
+ public void onDevicePropertyUpdated(String address) {
+ }
}
}
diff --git a/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java b/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java
index e42e685..2487bd8 100644
--- a/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java
+++ b/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java
@@ -31,6 +31,7 @@ import io.trygvis.android.Function;
import io.trygvis.android.LocalBinder;
import io.trygvis.soilmoisture.R;
+import static android.bluetooth.BluetoothAdapter.LeScanCallback;
import static java.util.Collections.unmodifiableCollection;
public class DefaultBtService<A> extends Service implements BtService<A> {
@@ -54,6 +55,8 @@ public class DefaultBtService<A> extends Service implements BtService<A> {
private boolean scanning = false;
+ private Scanner scanner = new Scanner();
+
// -----------------------------------------------------------------------
// BtService Implementation
// -----------------------------------------------------------------------
@@ -111,7 +114,7 @@ public class DefaultBtService<A> extends Service implements BtService<A> {
for (String address : addresses) {
BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);
- register(bluetoothDevice, null, null);
+ register(bluetoothDevice, null, false);
}
return true;
@@ -134,8 +137,9 @@ public class DefaultBtService<A> extends Service implements BtService<A> {
handler.postDelayed(this::stopScanning, timeoutMs);
}
- if (bluetoothAdapter.startLeScan(leScanCallback)) {
+ if (bluetoothAdapter.startLeScan(scanner)) {
scanning = true;
+ scanner.found.clear();
sendBroadcast(createScanStarted());
return true;
}
@@ -148,9 +152,22 @@ public class DefaultBtService<A> extends Service implements BtService<A> {
Log.d(TAG, "stopScanning");
// This doesn't mind being called twice.
- bluetoothAdapter.stopLeScan(leScanCallback);
+ bluetoothAdapter.stopLeScan(scanner);
scanning = false;
+ for (BtDevice<A> device : devices) {
+ boolean recentlySeen = scanner.found.contains(device);
+ Log.i(TAG, "scanner.found.contains(device)=" + recentlySeen + ", " +
+ "address=" + device.getAddress());
+
+ boolean old = device.isRecentlySeen();
+ device.setRecentlySeen(recentlySeen);
+
+ // Only if it not seen and it wasn't previously seen
+ if (!recentlySeen && old) {
+ sendBroadcast(createDevicePropertyUpdated(device.getAddress()));
+ }
+ }
sendBroadcast(createScanStopped());
}
@@ -163,7 +180,7 @@ public class DefaultBtService<A> extends Service implements BtService<A> {
}
BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(mac);
- return register(bluetoothDevice, null, null);
+ return register(bluetoothDevice, null, false);
}
@Override
@@ -205,16 +222,6 @@ public class DefaultBtService<A> extends Service implements BtService<A> {
}
// -----------------------------------------------------------------------
- // Scanning
- // -----------------------------------------------------------------------
-
- private BluetoothAdapter.LeScanCallback leScanCallback = (device, rssi, scanRecord) -> {
- BtScanResult scanResult = new BtScanResult(scanRecord);
-
- register(device, rssi, scanResult);
- };
-
- // -----------------------------------------------------------------------
// Service Implementation
// -----------------------------------------------------------------------
@@ -278,17 +285,19 @@ public class DefaultBtService<A> extends Service implements BtService<A> {
return openOrCreateDatabase("bt-devices", MODE_ENABLE_WRITE_AHEAD_LOGGING, null);
}
- private BtDevice<A> register(BluetoothDevice bluetoothDevice, Integer rssi, BtScanResult scanResult) {
+ private BtDevice<A> register(BluetoothDevice bluetoothDevice, Integer rssi, boolean fromScan) {
String address = bluetoothDevice.getAddress();
- BtDevice<A> btDevice = findDevice(address);
+ BtDevice<A> device = findDevice(address);
- if (btDevice != null) {
- return btDevice;
+ if (device != null) {
+ device.setRecentlySeen(true);
+ sendBroadcast(createDevicePropertyUpdated(device.getAddress()));
+ return device;
}
long now = System.currentTimeMillis();
- btDevice = runTx(db -> {
+ device = runTx(db -> {
Cursor cursor = db.query(Tables.T_BT_DEVICE, new String[]{Tables.C_ID, Tables.C_FIRST_SEEN},
Tables.C_ADDRESS + "=?", new String[]{address}, null, null, null);
@@ -315,15 +324,15 @@ public class DefaultBtService<A> extends Service implements BtService<A> {
Log.i(TAG, "New device: " + address + ", seenBefore=" + seenBefore);
cursor.close();
- return new BtDevice<>(this, bluetoothDevice, db, btDbIntegration, id, rssi, scanResult,
- seenBefore, firstSeen, lastSeen);
+ return new BtDevice<>(this, bluetoothDevice, db, btDbIntegration, id, rssi, seenBefore,
+ firstSeen, lastSeen, fromScan);
});
- devices.add(btDevice);
+ devices.add(device);
- sendBroadcast(createNewDevice(btDevice.getAddress()));
+ sendBroadcast(createNewDevice(device.getAddress()));
- return btDevice;
+ return device;
}
Intent createScanStarted() {
@@ -348,6 +357,12 @@ public class DefaultBtService<A> extends Service implements BtService<A> {
putExtra("address", address);
}
+ Intent createDevicePropertyUpdated(String address) {
+ return new Intent(BtServiceListenerBroadcastReceiver.INTENT_NAME).
+ putExtra("event", "devicePropertyUpdated").
+ putExtra("address", address);
+ }
+
public static void dispatchEvent(Intent intent, BtServiceListenerBroadcastReceiver listener) {
String event = intent.getStringExtra("event");
Log.i(TAG, "Dispatching event " + intent.getAction() + "/" + event);
@@ -361,6 +376,9 @@ public class DefaultBtService<A> extends Service implements BtService<A> {
case "newDevice":
listener.onNewDevice(intent.getStringExtra("address"));
break;
+ case "devicePropertyUpdated":
+ listener.onDevicePropertyUpdated(intent.getStringExtra("address"));
+ break;
case "deviceConnection":
listener.onDeviceConnection(intent.getStringExtra("address"));
break;
@@ -377,4 +395,15 @@ public class DefaultBtService<A> extends Service implements BtService<A> {
}
return null;
}
+
+ private class Scanner implements LeScanCallback {
+
+ private List<BtDevice> found = new ArrayList<>();
+
+ @Override
+ public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
+ BtDevice<A> d = register(device, rssi, true);
+ found.add(d);
+ }
+ }
}
diff --git a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
index 85a6afb..3e1c93b 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
@@ -119,7 +119,8 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
sendBroadcast(createNewDevice(address));
boolean candidate = btDevice.getAddress().startsWith("FB:") ||
- btDevice.getAddress().startsWith("FD:");
+ btDevice.getAddress().startsWith("FD:") ||
+ btDevice.getAddress().startsWith("CE:");
if (!candidate) {
Log.w(TAG, "Skipping device: " + btDevice.getAddress());
@@ -131,6 +132,11 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
probe(smDevice.getBtDevice().getAddress());
}
}
+
+ @Override
+ public void onDevicePropertyUpdated(String address) {
+ sendBroadcast(createDevicePropertyUpdated(getDevice(address)));
+ }
};
private BtPromise readAttribute(String value, byte[] req, Function<byte[], BtPromise.PromiseResult> handler) {
@@ -159,7 +165,7 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
GetSensorNameRes res = parseResponse(bytes, GET_SENSOR_NAME, GetSensorNameRes.class);
String name = res.name;
- device.getSensorByIndex(index).ifPresent(sensor -> {
+ device.getSensorByNumber(index).ifPresent(sensor -> {
sensor.setName(name);
sendBroadcast(createDevicePropertyUpdated(device));
});
@@ -198,6 +204,7 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
return continueDirectly();
}).
onFinally(success -> {
+ Log.i(TAG, "finally, smDevice.getIsUseful()=" + smDevice.getIsUseful());
if (smDevice.getIsUseful() == null) {
smDevice.setIsUseful(false);
}
@@ -256,7 +263,11 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
values.put(Tables.C_INDEX, i);
id = db.insert(Tables.T_SM_SENSOR, null, values);
+ Log.i(TAG, "Created new sensor row, id=" + id);
+
device.addSensor(new SmSensor(device, id, (byte) i));
+ } else {
+ Log.i(TAG, "Using existing sensor row, id=" + id);
}
}
diff --git a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java
index e838f01..6996ef8 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java
@@ -47,7 +47,7 @@ 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 SoilMoistureListener serviceListener = new MySoilMoistureListener();
+ private final SoilMoistureListener serviceListener = new MainSoilMoistureListener();
private final MainActivity context = this;
private DeviceListAdapter deviceList;
@@ -56,7 +56,10 @@ public class MainActivity extends ListActivity {
private ProgressDialog initializing;
private boolean ready;
- @Override
+ private int red;
+ private int yellow;
+ private int green;
+
protected void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate");
Thread.setDefaultUncaughtExceptionHandler(EXCEPTION_HANDLER);
@@ -92,8 +95,12 @@ public class MainActivity extends ListActivity {
bindService(new Intent(this, DefaultSoilMoistureService.class), serviceConnection, BIND_AUTO_CREATE);
- initializing = ProgressDialog.
- show(this, "Initializing", "Connecting to Bluetooth system.", true);
+// initializing = ProgressDialog.
+// show(this, "Initializing", "Connecting to Bluetooth system.", true);
+
+ green = getResources().getColor(R.color.green);
+ yellow = getResources().getColor(R.color.yellow);
+ red = getResources().getColor(R.color.red);
setContentView(R.layout.main);
}
@@ -264,36 +271,126 @@ public class MainActivity extends ListActivity {
Log.i(TAG, "onSensorClick, device=" + sensor.getDevice().getBtDevice().getId() + "/" + sensor.getIndex());
sensor.readCurrentValue();
+
+// Intent intent = new Intent(this, SensorActivity.class);
+// intent.putExtra(SensorActivity.EXTRA_ADDRESS, sensor.getDevice().getBtDevice().getAddress());
+// intent.putExtra(SensorActivity.EXTRA_NUMBER, sensor.getIndex());
+// startActivity(intent);
}
// -----------------------------------------------------------------------
//
// -----------------------------------------------------------------------
- static class DeviceItem {
+ class DeviceItem {
+ final SmDevice device;
+ final TextView statusBar;
final TextView deviceName;
final TextView deviceAddress;
final TextView rssi;
final TextView info;
- DeviceItem(View view) {
+ DeviceItem(SmDevice device, View view) {
+ this.device = device;
+ this.statusBar = (TextView) view.findViewById(R.id.status_bar);
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);
}
+
+ public void update() {
+ statusBar.setVisibility(deviceList.isGroupByDevice() ? View.VISIBLE : View.GONE);
+ statusBar.setBackgroundColor(statusColor(device));
+
+ if (device.getName() != null) {
+ deviceName.setText(device.getName());
+ } else {
+ deviceName.setText(R.string.unknown_device);
+ }
+ String address = device.getBtDevice().getAddress();
+
+ if (!device.isProbed()) {
+ address += " not probed";
+ } else if (device.isUseful()) {
+ address += " useful";
+ } else {
+ address += " not useful";
+ }
+
+ address += ", connected=" + device.getBtDevice().connected();
+ address += ", recentlySeen=" + device.getBtDevice().isRecentlySeen();
+
+ deviceAddress.setText(address);
+
+ String rssi = getText(R.string.rssi) + ": " +
+ (device.getBtDevice().getRssi() != null ? valueOf(device.getBtDevice().getRssi()) : getText(R.string.unknown));
+ rssi += ", device: " + device.toString();
+ this.rssi.setText(rssi);
+
+// boolean useful = device.isUseful();
+//
+// if (useful) {
+// info.setText("Number of sensors: " + device.getSensors().size());
+// } else {
+// info.setText("");
+// }
+ }
}
- static class SensorItem {
+ class SensorItem {
+ final SmDevice device;
final SmSensor sensor;
+ final TextView statusBar;
final TextView description;
final ProgressBar sensorProgress;
SensorItem(SmSensor sensor, View view) {
this.sensor = sensor;
+ this.statusBar = (TextView) view.findViewById(R.id.status_bar);
this.description = (TextView) view.findViewById(R.id.description);
this.sensorProgress = (ProgressBar) view.findViewById(R.id.sensor_progress);
sensorProgress.setMax(1024);
+
+ device = sensor.getDevice();
+
+ view.setClickable(true);
+ view.setOnClickListener(v -> {
+ Log.i(TAG, "onClick, SmSensor: " +
+// "position=" + position + ", " +
+ "sensor=" + sensor /*+ ", " +
+ "tag=" + v.getTag()*/);
+ onSensorClick(sensor);
+ });
+ }
+
+ public void update() {
+ statusBar.setVisibility(deviceList.isGroupByDevice() ? View.GONE : View.VISIBLE);
+ statusBar.setBackgroundColor(statusColor(device));
+
+ if (deviceList.isGroupByDevice()) {
+ statusBar.setVisibility(View.GONE);
+ } else {
+ statusBar.setVisibility(View.VISIBLE);
+ statusBar.setBackgroundColor(statusColor(device));
+ }
+
+ Integer value = sensor.getLastValue();
+ String text = "Sensor " + sensor;
+ text += ", value: " + (value == null ? "Unknown" : value);
+ description.setText(text);
+
+ sensorProgress.setProgress(value != null ? value : 0);
+ }
+ }
+
+ private int statusColor(SmDevice device) {
+ if (device.getBtDevice().connected()) {
+ return green;
+ } else if (device.getBtDevice().isRecentlySeen()) {
+ return yellow;
+ } else {
+ return red;
}
}
@@ -349,6 +446,7 @@ public class MainActivity extends ListActivity {
}
public void notifyDataSetChanged() {
+ Log.i(TAG, "notifyDataSetChanged");
dataSetObservable.notifyChanged();
}
@@ -475,9 +573,9 @@ public class MainActivity extends ListActivity {
//noinspection unchecked
return getBtDeviceView((BtDevice<SmDevice>) o, view);
} else if (o instanceof SmDevice) {
- return getSmDeviceView(position, (SmDevice) o, view);
+ return getSmDeviceView((SmDevice) o, view);
} else if (o instanceof SmSensor) {
- return getSoilSensorView(position, (SmSensor) o, view);
+ return getSoilSensorView((SmSensor) o, view);
}
throw new RuntimeException("Not implemented");
@@ -486,7 +584,7 @@ public class MainActivity extends ListActivity {
private View getBtDeviceView(BtDevice<SmDevice> device, View view) {
if (view == null) {
view = inflater.inflate(R.layout.fragment_device, null);
- view.setTag(new DeviceItem(view));
+ view.setTag(new DeviceItem(device.getTag(), view));
view.setOnLongClickListener(v -> MainActivity.this.onBtDeviceLongClick(device));
}
@@ -507,79 +605,33 @@ public class MainActivity extends ListActivity {
return view;
}
- private View getSmDeviceView(int position, SmDevice smDevice, View view) {
+ private View getSmDeviceView(SmDevice smDevice, View view) {
if (view == null) {
view = inflater.inflate(R.layout.fragment_device, null);
- view.setTag(new DeviceItem(view));
+ view.setTag(new DeviceItem(smDevice, view));
view.setOnClickListener(v -> onSmDeviceClick(smDevice));
view.setOnLongClickListener(v -> onSmDeviceLongClick(smDevice));
}
- DeviceItem item = (DeviceItem) view.getTag();
-
- if (smDevice.getName() != null) {
- item.deviceName.setText(smDevice.getName());
- } else {
- item.deviceName.setText(R.string.unknown_device);
- }
- String address = smDevice.getBtDevice().getAddress();
-
- if (!smDevice.isProbed()) {
- address += " not probed";
- } else if (smDevice.isUseful()) {
- address += " useful";
- } else {
- address += " not useful";
- }
-
- address += ", connected=" + smDevice.getBtDevice().connected();
-
- item.deviceAddress.setText(address);
-
- String rssi = getText(R.string.rssi) + ": " +
- (smDevice.getBtDevice().getRssi() != null ? valueOf(smDevice.getBtDevice().getRssi()) : getText(R.string.unknown));
- rssi += ", device: " + smDevice.toString();
- item.rssi.setText(rssi);
-
- boolean useful = smDevice.isUseful();
-
- if (useful) {
- item.info.setText("Number of sensors: " + smDevice.getSensors().size());
- } else {
- item.info.setText("");
- }
+ ((DeviceItem) view.getTag()).update();
return view;
}
- private View getSoilSensorView(int position, SmSensor smSensor, View view) {
+ 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 -> {
- Log.i(TAG, "onClick, SmSensor: " +
-// "position=" + position + ", " +
- "sensor=" + smSensor /*+ ", " +
- "tag=" + v.getTag()*/);
- onSensorClick(smSensor);
- });
+ view = inflater.inflate(R.layout.fragment_main_sensor, null);
+ SensorItem item = new SensorItem(smSensor, view);
+ view.setTag(item);
}
- SensorItem item = (SensorItem) view.getTag();
-
- Integer value = smSensor.getLastValue();
- String text = "Sensor " + smSensor;
- text += ", value: " + (value == null ? "Unknown" : value);
- item.description.setText(text);
-
- item.sensorProgress.setProgress(value != null ? value : 0);
+ ((SensorItem) view.getTag()).update();
return view;
}
}
- private class MySoilMoistureListener extends SoilMoistureListener {
+ private class MainSoilMoistureListener extends SoilMoistureListener {
@Override
public void onToast(int id, int length) {
CharSequence text = getText(id);
@@ -605,7 +657,9 @@ public class MainActivity extends ListActivity {
deviceList.notifyDataSetChanged();
startScan();
- initializing.dismiss();
+ if (initializing != null) {
+ initializing.dismiss();
+ }
}
}
@@ -630,5 +684,10 @@ public class MainActivity extends ListActivity {
public void onNewSample(String address, int sensor) {
deviceList.notifyDataSetChanged();
}
+
+ @Override
+ public void onDevicePropertyUpdated(String address) {
+ deviceList.notifyDataSetChanged();
+ }
}
}
diff --git a/app/src/main/java/io/trygvis/soilmoisture/SensorActivity.java b/app/src/main/java/io/trygvis/soilmoisture/SensorActivity.java
new file mode 100644
index 0000000..358382c
--- /dev/null
+++ b/app/src/main/java/io/trygvis/soilmoisture/SensorActivity.java
@@ -0,0 +1,134 @@
+package io.trygvis.soilmoisture;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import io.trygvis.android.LocalBinder;
+import io.trygvis.android.Optional;
+
+import static io.trygvis.soilmoisture.SoilMoistureService.SoilMoistureListener;
+
+public class SensorActivity extends Activity {
+ private final static String TAG = SensorActivity.class.getSimpleName();
+
+ public static final String EXTRA_ADDRESS = "address";
+ public static final String EXTRA_NUMBER = "number";
+
+ private final Context context = this;
+ private String address;
+ private int number;
+
+ private ServiceConnection serviceConnection;
+ private SoilMoistureService soilMoistureService;
+ private SensorSoilMoistureListener serviceListener = new SensorSoilMoistureListener();
+ private SmDevice device;
+ private SmSensor sensor;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_sensor);
+ if (savedInstanceState == null) {
+ getFragmentManager().beginTransaction()
+ .add(R.id.container, new PlaceholderFragment())
+ .commit();
+ }
+
+ address = getIntent().getStringExtra(EXTRA_ADDRESS);
+ number = getIntent().getIntExtra(EXTRA_NUMBER, -1);
+
+ ActionBar actionBar = getActionBar();
+
+ if (actionBar != null) {
+ actionBar.setTitle(number + ": " + address);
+ }
+
+ serviceConnection = new ServiceConnection() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder service) {
+ Log.i(TAG, "onServiceConnected");
+ soilMoistureService = ((LocalBinder<SoilMoistureService>) service).getService();
+ registerReceiver(serviceListener, SoilMoistureListener.INTENT_FILTER);
+
+ device = soilMoistureService.getDevice(address);
+ Optional<SmSensor> o = device.getSensorByNumber(number);
+
+ if (!o.isPresent()) {
+ Toast.makeText(context, "Could not find sensor #" + number + " on device " + address,
+ Toast.LENGTH_LONG).show();
+ finish();
+ }
+ sensor = o.get();
+
+ runOnUiThread(SensorActivity.this::initializeView);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) {
+ Log.i(TAG, "onServiceDisconnected");
+ soilMoistureService = null;
+ }
+ };
+ }
+
+ public void initializeView() {
+ // current value, last updated
+ // sync to cloud
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_sensor, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.action_settings) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * A placeholder fragment containing a simple view.
+ */
+ public static class PlaceholderFragment extends Fragment {
+
+ public PlaceholderFragment() {
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_sensor, container, false);
+ return rootView;
+ }
+ }
+
+
+
+ private class SensorSoilMoistureListener extends SoilMoistureListener {
+ }
+}
diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java
index bf2b5f3..808cbb6 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java
@@ -80,7 +80,7 @@ class SmDevice {
return sensors;
}
- public Optional<SmSensor> getSensorByIndex(int index) {
+ public Optional<SmSensor> getSensorByNumber(int index) {
if (!isUseful()) {
throw new IllegalStateException("Not a useful device");
}
@@ -95,7 +95,7 @@ class SmDevice {
}
void addSensor(SmSensor sensor) {
- if (getSensorByIndex(sensor.index).isPresent()) {
+ if (getSensorByNumber(sensor.index).isPresent()) {
throw new IllegalStateException("This device already contains a sensor with index=" + sensor.index);
}
diff --git a/app/src/main/res/layout/activity_sensor.xml b/app/src/main/res/layout/activity_sensor.xml
new file mode 100644
index 0000000..c91e018
--- /dev/null
+++ b/app/src/main/res/layout/activity_sensor.xml
@@ -0,0 +1,7 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="io.trygvis.soilmoisture.SensorActivity"
+ tools:ignore="MergeRootFrame"/>
diff --git a/app/src/main/res/layout/fragment_device.xml b/app/src/main/res/layout/fragment_device.xml
index e6a2f25..097ba34 100644
--- a/app/src/main/res/layout/fragment_device.xml
+++ b/app/src/main/res/layout/fragment_device.xml
@@ -6,12 +6,19 @@
android:clickable="true">
<TextView
+ android:layout_width="match_parent"
+ android:layout_height="10sp"
+ android:id="@+id/status_bar"
+ android:layout_alignParentEnd="true"
+ android:layout_marginTop="0dp"
+ android:background="@color/red"/>
+
+ <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_below="@+id/status_bar"/>
<TextView
android:id="@+id/device_address"
diff --git a/app/src/main/res/layout/fragment_main_sensor.xml b/app/src/main/res/layout/fragment_main_sensor.xml
new file mode 100644
index 0000000..157dcc2
--- /dev/null
+++ b/app/src/main/res/layout/fragment_main_sensor.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="10dp"
+ android:id="@+id/status_bar"
+ android:layout_alignParentEnd="true"
+ android:layout_marginTop="0dp"
+ android:background="@color/red"/>
+
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/status_bar"/>
+
+ <ProgressBar
+ style="?android:attr/progressBarStyleHorizontal"
+ android:id="@+id/sensor_progress"
+ android:layout_width="fill_parent"
+ android:layout_height="20dp"
+ android:layout_below="@id/description"/>
+
+</RelativeLayout>
diff --git a/app/src/main/res/layout/fragment_sensor.xml b/app/src/main/res/layout/fragment_sensor.xml
index 82d0d77..4d911ca 100644
--- a/app/src/main/res/layout/fragment_sensor.xml
+++ b/app/src/main/res/layout/fragment_sensor.xml
@@ -1,21 +1,25 @@
-<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content">
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context="io.trygvis.soilmoisture.SensorActivity$PlaceholderFragment">
<TextView
- android:id="@+id/description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_marginTop="0dp"/>
+ android:text="@string/hello_world"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
- <ProgressBar
- style="?android:attr/progressBarStyleHorizontal"
- android:id="@+id/sensor_progress"
- android:layout_width="fill_parent"
- android:layout_height="20dp"
- android:layout_below="@id/description"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/refresh"
+ android:id="@+id/button"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentEnd="true"/>
</RelativeLayout>
diff --git a/app/src/main/res/menu/menu_sensor.xml b/app/src/main/res/menu/menu_sensor.xml
new file mode 100644
index 0000000..1b2de78
--- /dev/null
+++ b/app/src/main/res/menu/menu_sensor.xml
@@ -0,0 +1,8 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:context="io.trygvis.soilmoisture.SensorActivity">
+ <item android:id="@+id/action_settings"
+ android:title="@string/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never"/>
+</menu>
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..1df95ce
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="red">#ff0000</color>
+ <color name="yellow">#ffff00</color>
+ <color name="green">#00ff00</color>
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 26f42fe..d1934f0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -37,5 +37,6 @@
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="error_could_not_read_value">Could not read value</string>
+ <string name="title_activity_sensor">Sensor</string>
</resources>