Skip to content

Commit 070b7de

Browse files
committed
Add an "Auto-connect on startup" setting for Bluetooth devices, see #1339
1 parent b2ea12d commit 070b7de

5 files changed

Lines changed: 54 additions & 3 deletions

File tree

android_app/app/src/main/java/com/health/openscale/core/facade/SettingsFacade.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ object SettingsPreferenceKeys {
8989
val SAVED_BLUETOOTH_SMART_ASSIGNMENT_ENABLED = booleanPreferencesKey("saved_bluetooth_smart_assignment_enabled")
9090
val SAVED_BLUETOOTH_TOLERANCE_PERCENT = intPreferencesKey("saved_bluetooth_tolerance_percent")
9191
val SAVED_BLUETOOTH_IGNORE_OUTSIDE_TOLERANCE = booleanPreferencesKey("saved_bluetooth_ignore_outside_tolerance")
92+
val SAVED_BLUETOOTH_AUTO_CONNECT = booleanPreferencesKey("saved_bluetooth_auto_connect")
9293

9394
// Settings for chart
9495
val CHART_SHOW_DATA_POINTS = booleanPreferencesKey("chart_show_data_points")
@@ -274,6 +275,9 @@ interface SettingsFacade {
274275
val selectedLbmFormula: Flow<LbmFormulaOption>
275276
suspend fun setSelectedLbmFormula(option: LbmFormulaOption)
276277

278+
val autoConnectOnStartup: Flow<Boolean>
279+
suspend fun setAutoConnectOnStartup(enabled: Boolean)
280+
277281
// Generic Settings Accessors
278282
/**
279283
* Observes a setting with the given key name and default value.
@@ -535,6 +539,7 @@ class SettingsFacadeImpl @Inject constructor(
535539
prefs.remove(SettingsPreferenceKeys.SAVED_BLUETOOTH_DEVICE_SERVICE_UUIDS)
536540
prefs.remove(SettingsPreferenceKeys.SAVED_BLUETOOTH_DEVICE_HANDLER_HINT)
537541
prefs.remove(SettingsPreferenceKeys.SAVED_BLUETOOTH_DEVICE_MANUFACTURER_DATA)
542+
prefs.remove(SettingsPreferenceKeys.SAVED_BLUETOOTH_AUTO_CONNECT)
538543
}
539544
}
540545

@@ -574,6 +579,15 @@ class SettingsFacadeImpl @Inject constructor(
574579
}
575580
}
576581

582+
override val autoConnectOnStartup: Flow<Boolean> = observeSetting(
583+
SettingsPreferenceKeys.SAVED_BLUETOOTH_AUTO_CONNECT.name,
584+
false
585+
)
586+
587+
override suspend fun setAutoConnectOnStartup(enabled: Boolean) {
588+
saveSetting(SettingsPreferenceKeys.SAVED_BLUETOOTH_AUTO_CONNECT.name, enabled)
589+
}
590+
577591
override val isSmartAssignmentEnabled: Flow<Boolean> = observeSetting(
578592
SettingsPreferenceKeys.SAVED_BLUETOOTH_SMART_ASSIGNMENT_ENABLED.name,
579593
false

android_app/app/src/main/java/com/health/openscale/ui/screen/settings/BluetoothDetailScreen.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import androidx.compose.foundation.layout.width
3333
import androidx.compose.foundation.rememberScrollState
3434
import androidx.compose.foundation.verticalScroll
3535
import androidx.compose.material.icons.Icons
36+
import androidx.compose.material.icons.filled.BluetoothConnected
3637
import androidx.compose.material.icons.filled.BugReport
3738
import androidx.compose.material.icons.filled.DeleteForever
3839
import androidx.compose.material.icons.filled.People
@@ -105,6 +106,7 @@ fun BluetoothDetailScreen(
105106
var tuningDropdownExpanded by remember { mutableStateOf(false) }
106107
val availableTuningProfiles = remember { TuningProfile.entries.toList() }
107108
var showToleranceDialog by remember { mutableStateOf(false) }
109+
val autoConnectOnStartup by bluetoothViewModel.autoConnectOnStartup.collectAsStateWithLifecycle(false)
108110

109111
if (showToleranceDialog) {
110112
NumberInputDialog(
@@ -194,6 +196,17 @@ fun BluetoothDetailScreen(
194196

195197
// --- BLUETOOTH MEASUREMENT SECTION ---
196198
SettingsSectionTitle(title = stringResource(R.string.bluetooth_measurement_title))
199+
SettingsRow(
200+
modifier = Modifier.padding(horizontal = 16.dp),
201+
label = stringResource(R.string.auto_connect_on_startup_title),
202+
icon = Icons.Default.BluetoothConnected,
203+
onClick = { scope.launch { bluetoothViewModel.setAutoConnectOnStartup(!autoConnectOnStartup) } }
204+
) {
205+
Switch(
206+
checked = autoConnectOnStartup,
207+
onCheckedChange = null,
208+
)
209+
}
197210
Card(modifier = Modifier.fillMaxWidth()) {
198211
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
199212
SettingsRow(
@@ -342,14 +355,15 @@ private fun SettingsSectionTitle(
342355
*/
343356
@Composable
344357
private fun SettingsRow(
358+
modifier: Modifier = Modifier,
345359
label: String,
346360
description: String? = null,
347361
icon: ImageVector? = null,
348362
onClick: (() -> Unit)? = null,
349363
content: @Composable () -> Unit
350364
) {
351365
Box(
352-
modifier = Modifier
366+
modifier = modifier
353367
.fillMaxWidth()
354368
.then(if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier)
355369
) {

android_app/app/src/main/java/com/health/openscale/ui/screen/settings/BluetoothViewModel.kt

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel
3232
import kotlinx.coroutines.flow.MutableSharedFlow
3333
import kotlinx.coroutines.flow.SharedFlow
3434
import kotlinx.coroutines.flow.asSharedFlow
35+
import kotlinx.coroutines.flow.filter
36+
import kotlinx.coroutines.flow.first
3537
import kotlinx.coroutines.launch
38+
import kotlinx.coroutines.withTimeoutOrNull
3639
import javax.inject.Inject
3740

3841
/**
@@ -69,6 +72,7 @@ class BluetoothViewModel @Inject constructor(
6972
val isSmartAssignmentEnabled = settingsFacade.isSmartAssignmentEnabled
7073
val smartAssignmentTolerancePercent = settingsFacade.smartAssignmentTolerancePercent
7174
val smartAssignmentIgnoreOutsideTolerance = settingsFacade.smartAssignmentIgnoreOutsideTolerance
75+
val autoConnectOnStartup = settingsFacade.autoConnectOnStartup
7276

7377
fun setSmartAssignmentEnabled(enabled: Boolean) = viewModelScope.launch {
7478
settingsFacade.setSmartAssignmentEnabled(enabled)
@@ -82,6 +86,9 @@ class BluetoothViewModel @Inject constructor(
8286
settingsFacade.setSmartAssignmentIgnoreOutsideTolerance(ignore)
8387
}
8488

89+
fun setAutoConnectOnStartup(enabled: Boolean) = viewModelScope.launch {
90+
settingsFacade.setAutoConnectOnStartup(enabled)
91+
}
8592
// --- Snackbar events for UI ---
8693
private val _snackbarEvents = MutableSharedFlow<SnackbarEvent>(replay = 0, extraBufferCapacity = 1)
8794
val snackbarEvents: SharedFlow<SnackbarEvent> = _snackbarEvents.asSharedFlow()
@@ -92,6 +99,22 @@ class BluetoothViewModel @Inject constructor(
9299
_snackbarEvents.emit(evt)
93100
}
94101
}
102+
103+
// Auto-connect on startup
104+
viewModelScope.launch {
105+
val enabled = settingsFacade.autoConnectOnStartup.first()
106+
if (!enabled) return@launch
107+
108+
val device = withTimeoutOrNull(1_500) {
109+
bt.savedDevice
110+
.filter { it != null }
111+
.first()
112+
}
113+
114+
if (device != null) {
115+
bt.attemptAutoConnectToSavedDevice()
116+
}
117+
}
95118
}
96119

97120
// --- Delegated actions ---
@@ -122,8 +145,6 @@ class BluetoothViewModel @Inject constructor(
122145

123146
fun clearAllErrors() = bt.clearErrors()
124147

125-
fun attemptAutoConnectToSavedScale() = bt.attemptAutoConnectToSavedDevice()
126-
127148
fun provideUserInteractionFeedback(type: BluetoothEvent.UserInteractionType, feedbackData: Any) =
128149
bt.provideUserInteractionFeedback(type, feedbackData)
129150

android_app/app/src/main/res/values-de/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@
359359
<string name="tolerance_desc">Max. Gewichtsabweichung, um eine Messung automatisch einem Benutzer zuzuweisen.</string>
360360
<string name="developer_section_title">Entwickler</string>
361361
<string name="danger_zone_title">Gefahrenzone</string>
362+
<string name="auto_connect_on_startup_title">Automatisch verbinden beim Start</string>
362363

363364
<!-- Bluetooth Connector -->
364365
<string name="bluetooth_connector_connected_to">Verbunden mit %1$s</string>

android_app/app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@
364364
<string name="tolerance_desc">Max. weight deviation to automatically assign to a user.</string>
365365
<string name="developer_section_title">Developer</string>
366366
<string name="danger_zone_title">Danger Zone</string>
367+
<string name="auto_connect_on_startup_title">Auto-connect on startup</string>
367368

368369
<!-- Scale Configuration (generic, shown when a handler declares config fields) -->
369370
<string name="scale_configuration_title">Scale Configuration</string>

0 commit comments

Comments
 (0)