From e8a43711f47431f0f81900223ac86d9c2bb33697 Mon Sep 17 00:00:00 2001 From: Fabian Christoffel Date: Mon, 26 Jun 2023 18:17:29 +0200 Subject: [PATCH] feat: show progress indicator for device ops --- .../sensortestingapp/ConnectionManager.kt | 51 ++++++++++++------- .../sensortestingapp/DeviceListAdapter.kt | 8 +++ .../example/sensortestingapp/KirbyDevice.kt | 7 +++ .../example/sensortestingapp/MainActivity.kt | 2 + app/src/main/res/layout/row_device_list.xml | 12 +++++ 5 files changed, 63 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/example/sensortestingapp/ConnectionManager.kt b/app/src/main/java/com/example/sensortestingapp/ConnectionManager.kt index 50e9de2..0ff5f37 100644 --- a/app/src/main/java/com/example/sensortestingapp/ConnectionManager.kt +++ b/app/src/main/java/com/example/sensortestingapp/ConnectionManager.kt @@ -108,6 +108,10 @@ open class BleListener(private val deviceAddress: String?) { } + open fun onQueueSizeChange(groupedOps: Map>) { + + } + } @@ -263,6 +267,14 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { listeners.filter { n -> n.isRelevantMessage(address) }.forEach(notifier) } + private fun notifyListenersOfQueueChange(address: String) { + notifyListeners(address) { listener -> + listener.onQueueSizeChange( + operationQueue.groupBy { o -> o.device.address } + ) + } + } + @Synchronized private fun enqueueOperation(operation: BleOperationType) { @@ -270,15 +282,17 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { stopScan() } operationQueue.add(operation) + notifyListenersOfQueueChange(operation.device.address) if (pendingOperation == null) { doNextOperation() } } @Synchronized - private fun signalEndOfOperation() { - Log.d("ConnectionManager", "End of $pendingOperation") + private fun signalEndOfOperation(op: BleOperationType) { + Log.d("ConnectionManager", "End of $op") pendingOperation = null + notifyListenersOfQueueChange(op.device.address) if (operationQueue.isNotEmpty()) { doNextOperation() } @@ -318,7 +332,7 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { "ConnectionManager", "Not connected to ${operation.device.address}! Aborting $operation operation." ) - signalEndOfOperation() + signalEndOfOperation(operation) return } @@ -331,7 +345,7 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { notifyListeners(gatt.device.address) { it.onDisconnect(gatt) } - signalEndOfOperation() + signalEndOfOperation(operation) } is MtuRequest -> with(operation) { @@ -348,7 +362,7 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { gatt.readCharacteristic(characteristic) } else { Log.e("ConnectionManager", "Char $charId (${serviceId}) is not readable!") - signalEndOfOperation() + signalEndOfOperation(operation) } } @@ -357,7 +371,7 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { val characteristic = gatt.getService(serviceId)?.getCharacteristic(charId); if (characteristic == null) { Log.e("ConnectionManager", "Char $charId (${serviceId}) not found!") - signalEndOfOperation() + signalEndOfOperation(operation) return } val descriptor = characteristic.getDescriptor(CCC_DESCRIPTOR_UUID) @@ -367,7 +381,7 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { "ConnectionManager", "Char ${characteristic.uuid} (service: $serviceId)" + " doesn't support notifications/indications" ) - signalEndOfOperation() + signalEndOfOperation(operation) return } @@ -379,7 +393,7 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { "ConnectionManager", "setCharacteristicNotification to true " + "failed for ${characteristic.uuid} (service: $serviceId)" ) - signalEndOfOperation() + signalEndOfOperation(operation) return } payload = @@ -393,7 +407,7 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { "ConnectionManager", "setCharacteristicNotification to false " + "failed for ${characteristic.uuid} (service: $serviceId)" ) - signalEndOfOperation() + signalEndOfOperation(operation) return } payload = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE @@ -428,8 +442,9 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { notifyListeners(gatt.device.address) { it.onConnect(gatt) } - if (pendingOperation is Connect) { - signalEndOfOperation() + val operation = pendingOperation + if (operation is Connect) { + signalEndOfOperation(operation) } } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { Log.e( @@ -468,8 +483,9 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { } } - if (pendingOperation is DiscoverServicesRequest) { - signalEndOfOperation() + val operation = pendingOperation + if (operation is DiscoverServicesRequest) { + signalEndOfOperation(operation) } } @@ -479,8 +495,9 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { "ATT MTU changed to $mtu, success: ${status == BluetoothGatt.GATT_SUCCESS}" ) - if (pendingOperation is MtuRequest) { - signalEndOfOperation() + val operation = pendingOperation + if (operation is MtuRequest) { + signalEndOfOperation(operation) } } @@ -522,7 +539,7 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { val op = pendingOperation if (op is ReadChar && op.charId == characteristic.uuid && op.serviceId == characteristic.service.uuid) { - signalEndOfOperation() + signalEndOfOperation(op) } } @@ -546,7 +563,7 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) { it.onUnsubscribe(gatt, descriptor) } } - signalEndOfOperation() + signalEndOfOperation(op) } } diff --git a/app/src/main/java/com/example/sensortestingapp/DeviceListAdapter.kt b/app/src/main/java/com/example/sensortestingapp/DeviceListAdapter.kt index 66b8df5..4aee152 100644 --- a/app/src/main/java/com/example/sensortestingapp/DeviceListAdapter.kt +++ b/app/src/main/java/com/example/sensortestingapp/DeviceListAdapter.kt @@ -4,11 +4,14 @@ import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater import android.view.View +import android.view.View.INVISIBLE +import android.view.View.VISIBLE import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.Button import android.widget.ListView import android.widget.PopupMenu +import android.widget.ProgressBar import android.widget.TextView import androidx.recyclerview.widget.RecyclerView @@ -33,6 +36,8 @@ interface DeviceListEntry { val status: String? + var hasRunningOp: Boolean + fun getActions(): List fun getMeasurements(): List @@ -59,6 +64,7 @@ class DeviceListAdapter( val statusView: TextView val measurementsListView: ListView val deviceActions: Button + val deviceProgress: ProgressBar init { @@ -68,11 +74,13 @@ class DeviceListAdapter( statusView = view.findViewById(R.id.device_status) measurementsListView = view.findViewById(R.id.measurement_fields) deviceActions = view.findViewById(R.id.device_actions) + deviceProgress = view.findViewById(R.id.device_progress) } @SuppressLint("RestrictedApi") fun bind(result: DeviceListEntry) { deviceNameView.text = result.name ?: "" + deviceProgress.visibility = if (result.hasRunningOp) VISIBLE else INVISIBLE macAddressView.text = result.address signalStrengthView.text = "${result.rssi ?: "-"} dBm" var signalStrengthIcon = R.drawable.signal_strength_weak diff --git a/app/src/main/java/com/example/sensortestingapp/KirbyDevice.kt b/app/src/main/java/com/example/sensortestingapp/KirbyDevice.kt index f1050bf..d8cb813 100644 --- a/app/src/main/java/com/example/sensortestingapp/KirbyDevice.kt +++ b/app/src/main/java/com/example/sensortestingapp/KirbyDevice.kt @@ -100,9 +100,11 @@ class KirbyDevice( private val connectionManager: ConnectionManager, private val bleDevice: BluetoothDevice, initialRssi: Int, + override var hasRunningOp: Boolean = false, private val onStateChange: (device: KirbyDevice) -> Unit ) : BleListener(bleDevice.address), DeviceListEntry { + override fun onScanResult(callbackType: Int, result: ScanResult) { statuses.add(DeviceStatus.DISCOVERED) onStateChange(this) @@ -146,6 +148,11 @@ class KirbyDevice( onStateChange(this) } + override fun onQueueSizeChange(groupedOps: Map>) { + hasRunningOp = groupedOps.getOrDefault(bleDevice.address, emptyList()).isNotEmpty() + onStateChange(this) + } + override var rssi = initialRssi set(value) { field = value diff --git a/app/src/main/java/com/example/sensortestingapp/MainActivity.kt b/app/src/main/java/com/example/sensortestingapp/MainActivity.kt index 703618f..1c5ba41 100644 --- a/app/src/main/java/com/example/sensortestingapp/MainActivity.kt +++ b/app/src/main/java/com/example/sensortestingapp/MainActivity.kt @@ -338,6 +338,8 @@ class DummyListEntry(override val address: String) : DeviceListEntry { override val status: String get() = "statusA, statusB" + override var hasRunningOp: Boolean = true + override fun getActions(): List { return listOf(object : Action { override fun getLabel(): String { diff --git a/app/src/main/res/layout/row_device_list.xml b/app/src/main/res/layout/row_device_list.xml index 5100c4a..24f6a17 100644 --- a/app/src/main/res/layout/row_device_list.xml +++ b/app/src/main/res/layout/row_device_list.xml @@ -45,6 +45,18 @@ app:layout_constraintTop_toTopOf="parent" tools:text="Device Name" /> + + +