From 71bcab676c81afbae94bed51f3aa3d40f20e93b2 Mon Sep 17 00:00:00 2001 From: Fredrik Liljegren Date: Mon, 23 Feb 2026 10:41:09 +0100 Subject: [PATCH] Android UX: sensor descriptions, auto-send on foreground, widget refresh - Add descriptive cards explaining what HR monitors and step sensors do - Stop BLE scanning when a device is connected - Always send pending Health Connect data when app comes to foreground (previously required background sync to be enabled) - Refresh widget data when app is brought to foreground Co-Authored-By: Claude Opus 4.6 --- .../src/main/java/net/aurboda/MainActivity.kt | 5 +- .../java/net/aurboda/ui/screens/LiveScreen.kt | 71 ++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/apps/android/app/src/main/java/net/aurboda/MainActivity.kt b/apps/android/app/src/main/java/net/aurboda/MainActivity.kt index 7a82648..b958052 100644 --- a/apps/android/app/src/main/java/net/aurboda/MainActivity.kt +++ b/apps/android/app/src/main/java/net/aurboda/MainActivity.kt @@ -761,9 +761,12 @@ fun HealthConnectScreen( // Re-check permissions in case user changed them in system settings refreshPermissions() fetchHealthData(context) - if (backgroundSyncEnabled && (healthRecords.isNotEmpty() || pendingDeletionIds.isNotEmpty())) { + // Always send pending data when app comes to foreground + if (healthRecords.isNotEmpty() || pendingDeletionIds.isNotEmpty()) { sendPendingDataToServer(context) } + // Refresh widget with latest data + net.aurboda.widget.HrZoneWidgetProvider.triggerUpdate(context) } } } diff --git a/apps/android/app/src/main/java/net/aurboda/ui/screens/LiveScreen.kt b/apps/android/app/src/main/java/net/aurboda/ui/screens/LiveScreen.kt index 95e890c..9c99705 100644 --- a/apps/android/app/src/main/java/net/aurboda/ui/screens/LiveScreen.kt +++ b/apps/android/app/src/main/java/net/aurboda/ui/screens/LiveScreen.kt @@ -357,6 +357,9 @@ fun LiveScreen( isScanning = false }, onConnectDevice = { device -> + // Stop scanning once a device is being connected + isScanning = false + // Check Health Connect write permission based on device type val hasPermission = when (device.sensorType) { SensorType.HEART_RATE -> hasHrWritePermission @@ -745,10 +748,76 @@ private fun ScannerSection( } } else if (!isScanning && discoveredDevices.isEmpty()) { Text( - text = "Tap 'Scan for Devices' to find heart rate monitors and step sensors.", + text = "Tap 'Scan for Devices' to find nearby Bluetooth sensors.", textAlign = TextAlign.Center, color = MaterialTheme.colorScheme.onSurfaceVariant ) + Spacer(modifier = Modifier.height(12.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Card( + modifier = Modifier.weight(1f), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant + ) + ) { + Column( + modifier = Modifier.padding(12.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Icon( + imageVector = Icons.Default.Favorite, + contentDescription = null, + tint = MaterialTheme.colorScheme.error + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = "Heart Rate Monitor", + style = MaterialTheme.typography.labelMedium, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + Text( + text = "Tracks heart rate and HRV in real time", + style = MaterialTheme.typography.bodySmall, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + Card( + modifier = Modifier.weight(1f), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant + ) + ) { + Column( + modifier = Modifier.padding(12.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Icon( + imageVector = Icons.Default.PlayArrow, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = "Step Sensor", + style = MaterialTheme.typography.labelMedium, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + Text( + text = "Tracks cadence and steps from a footpod", + style = MaterialTheme.typography.bodySmall, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + } } } }