mirror of
https://github.com/oliexdev/openScale.git
synced 2025-08-20 07:21:40 +02:00
Introduces a new "Chart Settings" screen accessible from the main settings menu.
This commit is contained in:
@@ -57,6 +57,9 @@ object UserPreferenceKeys {
|
|||||||
val SAVED_BLUETOOTH_SCALE_ADDRESS = stringPreferencesKey("saved_bluetooth_scale_address")
|
val SAVED_BLUETOOTH_SCALE_ADDRESS = stringPreferencesKey("saved_bluetooth_scale_address")
|
||||||
val SAVED_BLUETOOTH_SCALE_NAME = stringPreferencesKey("saved_bluetooth_scale_name")
|
val SAVED_BLUETOOTH_SCALE_NAME = stringPreferencesKey("saved_bluetooth_scale_name")
|
||||||
|
|
||||||
|
// Settings for chart
|
||||||
|
val SHOW_CHART_DATA_POINTS = booleanPreferencesKey("show_chart_data_points")
|
||||||
|
|
||||||
// Context strings for screen-specific settings (can be used as prefixes for dynamic keys)
|
// Context strings for screen-specific settings (can be used as prefixes for dynamic keys)
|
||||||
const val OVERVIEW_SCREEN_CONTEXT = "overview_screen"
|
const val OVERVIEW_SCREEN_CONTEXT = "overview_screen"
|
||||||
const val GRAPH_SCREEN_CONTEXT = "graph_screen"
|
const val GRAPH_SCREEN_CONTEXT = "graph_screen"
|
||||||
@@ -90,6 +93,9 @@ interface UserSettingsRepository {
|
|||||||
suspend fun saveBluetoothScale(address: String, name: String?)
|
suspend fun saveBluetoothScale(address: String, name: String?)
|
||||||
suspend fun clearSavedBluetoothScale()
|
suspend fun clearSavedBluetoothScale()
|
||||||
|
|
||||||
|
val showChartDataPoints: Flow<Boolean>
|
||||||
|
suspend fun setShowChartDataPoints(show: Boolean)
|
||||||
|
|
||||||
// Generic Settings Accessors
|
// Generic Settings Accessors
|
||||||
/**
|
/**
|
||||||
* Observes a setting with the given key name and default value.
|
* Observes a setting with the given key name and default value.
|
||||||
@@ -248,6 +254,19 @@ class UserSettingsRepositoryImpl(context: Context) : UserSettingsRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val showChartDataPoints: Flow<Boolean> = observeSetting(
|
||||||
|
UserPreferenceKeys.SHOW_CHART_DATA_POINTS.name,
|
||||||
|
true
|
||||||
|
).catch { exception ->
|
||||||
|
LogManager.e(TAG, "Error observing showChartDataPoints", exception)
|
||||||
|
emit(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setShowChartDataPoints(show: Boolean) {
|
||||||
|
LogManager.d(TAG, "Setting showChartDataPoints to: $show")
|
||||||
|
saveSetting(UserPreferenceKeys.SHOW_CHART_DATA_POINTS.name, show)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T> observeSetting(keyName: String, defaultValue: T): Flow<T> {
|
override fun <T> observeSetting(keyName: String, defaultValue: T): Flow<T> {
|
||||||
LogManager.v(TAG, "Observing setting: key='$keyName', type='${defaultValue!!::class.simpleName}'")
|
LogManager.v(TAG, "Observing setting: key='$keyName', type='${defaultValue!!::class.simpleName}'")
|
||||||
|
@@ -121,6 +121,7 @@ import com.health.openscale.ui.screen.overview.MeasurementDetailScreen
|
|||||||
import com.health.openscale.ui.screen.overview.OverviewScreen
|
import com.health.openscale.ui.screen.overview.OverviewScreen
|
||||||
import com.health.openscale.ui.screen.settings.AboutScreen
|
import com.health.openscale.ui.screen.settings.AboutScreen
|
||||||
import com.health.openscale.ui.screen.settings.BluetoothScreen
|
import com.health.openscale.ui.screen.settings.BluetoothScreen
|
||||||
|
import com.health.openscale.ui.screen.settings.ChartSettingsScreen
|
||||||
import com.health.openscale.ui.screen.settings.DataManagementSettingsScreen
|
import com.health.openscale.ui.screen.settings.DataManagementSettingsScreen
|
||||||
import com.health.openscale.ui.screen.settings.GeneralSettingsScreen
|
import com.health.openscale.ui.screen.settings.GeneralSettingsScreen
|
||||||
import com.health.openscale.ui.screen.settings.MeasurementTypeDetailScreen
|
import com.health.openscale.ui.screen.settings.MeasurementTypeDetailScreen
|
||||||
@@ -683,6 +684,13 @@ fun AppNavigation(sharedViewModel: SharedViewModel) {
|
|||||||
bluetoothViewModel = bluetoothViewModel
|
bluetoothViewModel = bluetoothViewModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
composable(Routes.CHART_SETTINGS) {
|
||||||
|
ChartSettingsScreen(
|
||||||
|
navController = navController,
|
||||||
|
sharedViewModel = sharedViewModel,
|
||||||
|
settingsViewModel = settingsViewModel
|
||||||
|
)
|
||||||
|
}
|
||||||
composable(Routes.DATA_MANAGEMENT_SETTINGS) {
|
composable(Routes.DATA_MANAGEMENT_SETTINGS) {
|
||||||
DataManagementSettingsScreen(
|
DataManagementSettingsScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
|
@@ -46,6 +46,7 @@ object Routes {
|
|||||||
const val MEASUREMENT_TYPES = "settings/types"
|
const val MEASUREMENT_TYPES = "settings/types"
|
||||||
const val MEASUREMENT_TYPE_DETAIL = "settings/typeDetail"
|
const val MEASUREMENT_TYPE_DETAIL = "settings/typeDetail"
|
||||||
const val BLUETOOTH_SETTINGS = "settings/bluetooth"
|
const val BLUETOOTH_SETTINGS = "settings/bluetooth"
|
||||||
|
const val CHART_SETTINGS = "settings/chart"
|
||||||
const val DATA_MANAGEMENT_SETTINGS = "settings/dataManagement"
|
const val DATA_MANAGEMENT_SETTINGS = "settings/dataManagement"
|
||||||
const val ABOUT_SETTINGS = "settings/about"
|
const val ABOUT_SETTINGS = "settings/about"
|
||||||
|
|
||||||
|
@@ -63,6 +63,7 @@ import com.health.openscale.core.data.MeasurementTypeKey
|
|||||||
import com.health.openscale.core.data.TimeRangeFilter
|
import com.health.openscale.core.data.TimeRangeFilter
|
||||||
import com.health.openscale.core.database.UserPreferenceKeys
|
import com.health.openscale.core.database.UserPreferenceKeys
|
||||||
import com.health.openscale.core.database.UserSettingsRepository
|
import com.health.openscale.core.database.UserSettingsRepository
|
||||||
|
import com.health.openscale.core.database.UserSettingsRepositoryImpl
|
||||||
import com.health.openscale.ui.screen.SharedViewModel
|
import com.health.openscale.ui.screen.SharedViewModel
|
||||||
import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost
|
import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost
|
||||||
import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisGuidelineComponent
|
import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisGuidelineComponent
|
||||||
@@ -142,6 +143,8 @@ fun LineChart(
|
|||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val userSettingsRepository = sharedViewModel.userSettingRepository
|
val userSettingsRepository = sharedViewModel.userSettingRepository
|
||||||
|
|
||||||
|
val showDataPointsSetting by userSettingsRepository.showChartDataPoints.collectAsState(initial = true)
|
||||||
|
|
||||||
val uiSelectedTimeRange by rememberContextualTimeRangeFilter(
|
val uiSelectedTimeRange by rememberContextualTimeRangeFilter(
|
||||||
screenContextName = screenContextName,
|
screenContextName = screenContextName,
|
||||||
userSettingsRepository = userSettingsRepository
|
userSettingsRepository = userSettingsRepository
|
||||||
@@ -503,7 +506,11 @@ fun LineChart(
|
|||||||
LineCartesianLayer.LineProvider.series(
|
LineCartesianLayer.LineProvider.series(
|
||||||
seriesEntriesForStartAxis.mapIndexedNotNull { index, _ ->
|
seriesEntriesForStartAxis.mapIndexedNotNull { index, _ ->
|
||||||
if (index < typeColorsForStartAxis.size) {
|
if (index < typeColorsForStartAxis.size) {
|
||||||
createLineSpec(typeColorsForStartAxis[index], statisticsMode = targetMeasurementTypeId != null)
|
createLineSpec(
|
||||||
|
color = typeColorsForStartAxis[index],
|
||||||
|
statisticsMode = targetMeasurementTypeId != null,
|
||||||
|
showPoints = showDataPointsSetting
|
||||||
|
)
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -522,7 +529,11 @@ fun LineChart(
|
|||||||
LineCartesianLayer.LineProvider.series(
|
LineCartesianLayer.LineProvider.series(
|
||||||
seriesEntriesForEndAxis.mapIndexedNotNull { index, _ ->
|
seriesEntriesForEndAxis.mapIndexedNotNull { index, _ ->
|
||||||
if (index < typeColorsForEndAxis.size) {
|
if (index < typeColorsForEndAxis.size) {
|
||||||
createLineSpec(typeColorsForEndAxis[index], statisticsMode = targetMeasurementTypeId != null)
|
createLineSpec(
|
||||||
|
color = typeColorsForEndAxis[index],
|
||||||
|
statisticsMode = targetMeasurementTypeId != null,
|
||||||
|
showPoints = showDataPointsSetting
|
||||||
|
)
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -702,9 +713,10 @@ private fun rememberXAxisValueFormatter(
|
|||||||
* @param color The color of the line and points.
|
* @param color The color of the line and points.
|
||||||
* @param statisticsMode If true, an area fill is added below the line, and points are hidden.
|
* @param statisticsMode If true, an area fill is added below the line, and points are hidden.
|
||||||
* This is typically used when `targetMeasurementTypeId` is set.
|
* This is typically used when `targetMeasurementTypeId` is set.
|
||||||
|
* @param showPoints If true, points are displayed on the line (unless in statisticsMode).
|
||||||
* @return A configured [LineCartesianLayer.Line].
|
* @return A configured [LineCartesianLayer.Line].
|
||||||
*/
|
*/
|
||||||
private fun createLineSpec(color: Color, statisticsMode : Boolean): LineCartesianLayer.Line {
|
private fun createLineSpec(color: Color, statisticsMode : Boolean, showPoints: Boolean): LineCartesianLayer.Line {
|
||||||
val lineStroke = LineCartesianLayer.LineStroke.Continuous(
|
val lineStroke = LineCartesianLayer.LineStroke.Continuous(
|
||||||
thicknessDp = 2f,
|
thicknessDp = 2f,
|
||||||
)
|
)
|
||||||
@@ -719,10 +731,11 @@ private fun createLineSpec(color: Color, statisticsMode : Boolean): LineCartesia
|
|||||||
// Area fill is shown in statistics mode (e.g., when a single type is focused)
|
// Area fill is shown in statistics mode (e.g., when a single type is focused)
|
||||||
areaFill = if (statisticsMode) LineCartesianLayer.AreaFill.single(Fill(color.copy(alpha = 0.2f).toArgb())) else null,
|
areaFill = if (statisticsMode) LineCartesianLayer.AreaFill.single(Fill(color.copy(alpha = 0.2f).toArgb())) else null,
|
||||||
// Points on the line are shown unless in statistics mode
|
// Points on the line are shown unless in statistics mode
|
||||||
pointProvider = if (!statisticsMode) {
|
pointProvider = if (showPoints && !statisticsMode) {
|
||||||
LineCartesianLayer.PointProvider.single(
|
LineCartesianLayer.PointProvider.single(
|
||||||
LineCartesianLayer.point(ShapeComponent(fill(color.copy(alpha = 0.7f)), CorneredShape.Pill), 6.dp)
|
LineCartesianLayer.point(ShapeComponent(fill(color.copy(alpha = 0.7f)), CorneredShape.Pill), 6.dp)
|
||||||
) } else null,
|
)
|
||||||
|
} else null,
|
||||||
// dataLabel = null, // No data labels on points
|
// dataLabel = null, // No data labels on points
|
||||||
pointConnector = LineCartesianLayer.PointConnector.cubic()
|
pointConnector = LineCartesianLayer.PointConnector.cubic()
|
||||||
)
|
)
|
||||||
|
@@ -0,0 +1,58 @@
|
|||||||
|
package com.health.openscale.ui.screen.settings
|
||||||
|
|
||||||
|
import androidx.activity.result.launch
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.health.openscale.R
|
||||||
|
import com.health.openscale.ui.screen.SharedViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChartSettingsScreen(
|
||||||
|
navController: NavController,
|
||||||
|
sharedViewModel: SharedViewModel,
|
||||||
|
settingsViewModel: SettingsViewModel
|
||||||
|
) {
|
||||||
|
val chartSettingsScreenTitle = stringResource(R.string.settings_item_chart)
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
sharedViewModel.setTopBarTitle(chartSettingsScreenTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val showDataPoints by sharedViewModel.userSettingRepository.showChartDataPoints.collectAsState(true)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.setting_show_chart_points),
|
||||||
|
style = MaterialTheme.typography.bodyLarge
|
||||||
|
)
|
||||||
|
Switch(
|
||||||
|
checked = showDataPoints,
|
||||||
|
onCheckedChange = { newValue ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
sharedViewModel.userSettingRepository.setShowChartDataPoints(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -23,10 +23,12 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ShowChart
|
||||||
import androidx.compose.material.icons.filled.Bluetooth
|
import androidx.compose.material.icons.filled.Bluetooth
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
import androidx.compose.material.icons.filled.Info
|
import androidx.compose.material.icons.filled.Info
|
||||||
import androidx.compose.material.icons.filled.Person
|
import androidx.compose.material.icons.filled.Person
|
||||||
|
import androidx.compose.material.icons.filled.ShowChart
|
||||||
import androidx.compose.material.icons.filled.Storage
|
import androidx.compose.material.icons.filled.Storage
|
||||||
import androidx.compose.material.icons.filled.Tune
|
import androidx.compose.material.icons.filled.Tune
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
@@ -79,6 +81,7 @@ fun SettingsScreen(
|
|||||||
val userSettingsLabel = stringResource(R.string.settings_item_user)
|
val userSettingsLabel = stringResource(R.string.settings_item_user)
|
||||||
val measurementTypesLabel = stringResource(R.string.settings_item_measurement_types)
|
val measurementTypesLabel = stringResource(R.string.settings_item_measurement_types)
|
||||||
val bluetoothLabel = stringResource(R.string.settings_item_bluetooth)
|
val bluetoothLabel = stringResource(R.string.settings_item_bluetooth)
|
||||||
|
val chartSettingsLabel = stringResource(R.string.settings_item_chart)
|
||||||
val dataManagementLabel = stringResource(R.string.settings_item_data_management)
|
val dataManagementLabel = stringResource(R.string.settings_item_data_management)
|
||||||
val aboutLabel = stringResource(R.string.settings_item_about)
|
val aboutLabel = stringResource(R.string.settings_item_about)
|
||||||
|
|
||||||
@@ -107,6 +110,12 @@ fun SettingsScreen(
|
|||||||
route = Routes.BLUETOOTH_SETTINGS,
|
route = Routes.BLUETOOTH_SETTINGS,
|
||||||
contentDescription = bluetoothLabel
|
contentDescription = bluetoothLabel
|
||||||
),
|
),
|
||||||
|
SettingsItem(
|
||||||
|
label = chartSettingsLabel,
|
||||||
|
icon = Icons.AutoMirrored.Filled.ShowChart,
|
||||||
|
route = Routes.CHART_SETTINGS,
|
||||||
|
contentDescription = chartSettingsLabel
|
||||||
|
),
|
||||||
SettingsItem(
|
SettingsItem(
|
||||||
label = dataManagementLabel,
|
label = dataManagementLabel,
|
||||||
icon = Icons.Filled.Storage,
|
icon = Icons.Filled.Storage,
|
||||||
|
@@ -281,6 +281,7 @@
|
|||||||
<string name="settings_item_user">Benutzer</string>
|
<string name="settings_item_user">Benutzer</string>
|
||||||
<string name="settings_item_measurement_types">Messarten</string>
|
<string name="settings_item_measurement_types">Messarten</string>
|
||||||
<string name="settings_item_bluetooth">Bluetooth</string>
|
<string name="settings_item_bluetooth">Bluetooth</string>
|
||||||
|
<string name="settings_item_chart">Diagramm</string>
|
||||||
<string name="settings_item_data_management">Datenverwaltung</string>
|
<string name="settings_item_data_management">Datenverwaltung</string>
|
||||||
<string name="settings_item_about">Über</string>
|
<string name="settings_item_about">Über</string>
|
||||||
|
|
||||||
@@ -288,6 +289,9 @@
|
|||||||
<string name="settings_general_title">Allgemeine Einstellungen</string>
|
<string name="settings_general_title">Allgemeine Einstellungen</string>
|
||||||
<string name="settings_language_label">Sprache</string>
|
<string name="settings_language_label">Sprache</string>
|
||||||
|
|
||||||
|
<!-- Diagramm Einstellungen -->
|
||||||
|
<string name="setting_show_chart_points">Datenpunkte anzeigen</string>
|
||||||
|
|
||||||
<!-- Über-Bildschirm & Diagnose -->
|
<!-- Über-Bildschirm & Diagnose -->
|
||||||
<string name="version_info">Version: %1$s (%2$s)</string>
|
<string name="version_info">Version: %1$s (%2$s)</string>
|
||||||
<string name="project_information_title">Projektinformationen</string>
|
<string name="project_information_title">Projektinformationen</string>
|
||||||
|
@@ -283,6 +283,7 @@
|
|||||||
<string name="settings_item_user">User</string>
|
<string name="settings_item_user">User</string>
|
||||||
<string name="settings_item_measurement_types">Measurement Types</string>
|
<string name="settings_item_measurement_types">Measurement Types</string>
|
||||||
<string name="settings_item_bluetooth">Bluetooth</string>
|
<string name="settings_item_bluetooth">Bluetooth</string>
|
||||||
|
<string name="settings_item_chart">Chart</string>
|
||||||
<string name="settings_item_data_management">Data Management</string>
|
<string name="settings_item_data_management">Data Management</string>
|
||||||
<string name="settings_item_about">About</string>
|
<string name="settings_item_about">About</string>
|
||||||
|
|
||||||
@@ -290,6 +291,9 @@
|
|||||||
<string name="settings_general_title">General Settings</string>
|
<string name="settings_general_title">General Settings</string>
|
||||||
<string name="settings_language_label">Language</string>
|
<string name="settings_language_label">Language</string>
|
||||||
|
|
||||||
|
<!-- Chart Settings Screen -->
|
||||||
|
<string name="setting_show_chart_points">Show data points</string>
|
||||||
|
|
||||||
<!-- About Screen & Diagnostics -->
|
<!-- About Screen & Diagnostics -->
|
||||||
<string name="version_info">Version: %1$s (%2$s)</string>
|
<string name="version_info">Version: %1$s (%2$s)</string>
|
||||||
<string name="project_information_title">Project Information</string>
|
<string name="project_information_title">Project Information</string>
|
||||||
|
Reference in New Issue
Block a user