diff options
author | Trygve Laugstøl <trygvis@inamo.no> | 2015-01-27 21:23:50 +0100 |
---|---|---|
committer | Trygve Laugstøl <trygvis@inamo.no> | 2015-01-27 21:23:50 +0100 |
commit | c4685214d8db34166213ffa373a16af1a99401a5 (patch) | |
tree | 56e24e25119f28ff8c0a730c4d5313bc523a5c66 | |
parent | 138b49247a72890df66af9a01e1179dab72a4b71 (diff) | |
download | io.trygvis.soilmoisture-android-c4685214d8db34166213ffa373a16af1a99401a5.tar.gz io.trygvis.soilmoisture-android-c4685214d8db34166213ffa373a16af1a99401a5.tar.bz2 io.trygvis.soilmoisture-android-c4685214d8db34166213ffa373a16af1a99401a5.tar.xz io.trygvis.soilmoisture-android-c4685214d8db34166213ffa373a16af1a99401a5.zip |
o Adding 'recently seen' on BtDevice. Updated when scanning.
o Removing BtScanResult, it was never used.
o Getting MainActivity to listen on device property changed so the UI is properly updated.
o Adding a status bar with color to indicate if the device is available, connected or not seen.
18 files changed, 455 insertions, 152 deletions
@@ -6,3 +6,5 @@ local.properties .idea /build *.iml +*.tmp +*.tmp.* 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> |