1
0
mirror of https://github.com/oliexdev/openScale.git synced 2025-09-03 05:12:42 +02:00

Add MeasurementType User and dialog to change user

This commit is contained in:
oliexdev
2025-08-30 10:32:27 +02:00
parent 7be6b9575c
commit bdd20422ce
10 changed files with 199 additions and 23 deletions

View File

@@ -72,7 +72,8 @@ fun getDefaultMeasurementTypes(): List<MeasurementType> {
MeasurementType(key = MeasurementTypeKey.CALORIES, unit = UnitType.KCAL, color = 0xFF4CAF50.toInt(), icon = MeasurementTypeIcon.IC_CALORIES, isEnabled = true), MeasurementType(key = MeasurementTypeKey.CALORIES, unit = UnitType.KCAL, color = 0xFF4CAF50.toInt(), icon = MeasurementTypeIcon.IC_CALORIES, isEnabled = true),
MeasurementType(key = MeasurementTypeKey.COMMENT, inputType = InputFieldType.TEXT, unit = UnitType.NONE, color = 0xFFE0E0E0.toInt(), icon = MeasurementTypeIcon.IC_COMMENT, isPinned = true, isEnabled = true), MeasurementType(key = MeasurementTypeKey.COMMENT, inputType = InputFieldType.TEXT, unit = UnitType.NONE, color = 0xFFE0E0E0.toInt(), icon = MeasurementTypeIcon.IC_COMMENT, isPinned = true, isEnabled = true),
MeasurementType(key = MeasurementTypeKey.DATE, inputType = InputFieldType.DATE, unit = UnitType.NONE, color = 0xFF9E9E9E.toInt(), icon = MeasurementTypeIcon.IC_DATE, isEnabled = true), MeasurementType(key = MeasurementTypeKey.DATE, inputType = InputFieldType.DATE, unit = UnitType.NONE, color = 0xFF9E9E9E.toInt(), icon = MeasurementTypeIcon.IC_DATE, isEnabled = true),
MeasurementType(key = MeasurementTypeKey.TIME, inputType = InputFieldType.TIME, unit = UnitType.NONE, color = 0xFF757575.toInt(), icon = MeasurementTypeIcon.IC_TIME, isEnabled = true) MeasurementType(key = MeasurementTypeKey.TIME, inputType = InputFieldType.TIME, unit = UnitType.NONE, color = 0xFF757575.toInt(), icon = MeasurementTypeIcon.IC_TIME, isEnabled = true),
MeasurementType(key = MeasurementTypeKey.USER, inputType = InputFieldType.USER, unit = UnitType.NONE, color = 0xFF90A4AE.toInt(), icon = MeasurementTypeIcon.IC_USER, isEnabled = true)
) )
} }

View File

@@ -221,6 +221,7 @@ enum class MeasurementTypeIcon(val resource: IconResource) {
IC_COMMENT(IconResource.PainterResource(R.drawable.ic_comment)), IC_COMMENT(IconResource.PainterResource(R.drawable.ic_comment)),
IC_TIME(IconResource.PainterResource(R.drawable.ic_time)), IC_TIME(IconResource.PainterResource(R.drawable.ic_time)),
IC_DATE(IconResource.PainterResource(R.drawable.ic_date)), IC_DATE(IconResource.PainterResource(R.drawable.ic_date)),
IC_USER(IconResource.PainterResource(R.drawable.ic_user)),
IC_M_HEIGHT(IconResource.VectorResource(Icons.Filled.Height)), IC_M_HEIGHT(IconResource.VectorResource(Icons.Filled.Height)),
IC_M_HEART_RATE(IconResource.VectorResource(Icons.Filled.Favorite)), IC_M_HEART_RATE(IconResource.VectorResource(Icons.Filled.Favorite)),
@@ -301,6 +302,7 @@ enum class MeasurementTypeKey(
DATE(24, R.string.measurement_type_date, listOf(UnitType.NONE), listOf(InputFieldType.DATE)), DATE(24, R.string.measurement_type_date, listOf(UnitType.NONE), listOf(InputFieldType.DATE)),
TIME(25, R.string.measurement_type_time, listOf(UnitType.NONE), listOf(InputFieldType.TIME)), TIME(25, R.string.measurement_type_time, listOf(UnitType.NONE), listOf(InputFieldType.TIME)),
COMMENT(26, R.string.measurement_type_comment, listOf(UnitType.NONE), listOf(InputFieldType.TEXT)), COMMENT(26, R.string.measurement_type_comment, listOf(UnitType.NONE), listOf(InputFieldType.TEXT)),
USER(27, R.string.measurement_type_user, listOf(UnitType.NONE), listOf(InputFieldType.USER)),
CUSTOM(99, R.string.measurement_type_custom_default_name, UnitType.entries.toList(), listOf(InputFieldType.FLOAT, InputFieldType.INT, InputFieldType.TEXT, InputFieldType.DATE, InputFieldType.TIME)); CUSTOM(99, R.string.measurement_type_custom_default_name, UnitType.entries.toList(), listOf(InputFieldType.FLOAT, InputFieldType.INT, InputFieldType.TEXT, InputFieldType.DATE, InputFieldType.TIME));
} }
@@ -325,7 +327,8 @@ enum class InputFieldType {
INT, INT,
TEXT, TEXT,
DATE, DATE,
TIME TIME,
USER
} }
enum class Trend { enum class Trend {

View File

@@ -96,7 +96,9 @@ class ImportExportUseCases @Inject constructor(
val allAppTypes: List<MeasurementType> = repository.getAllMeasurementTypes().first() val allAppTypes: List<MeasurementType> = repository.getAllMeasurementTypes().first()
val exportableValueTypes = allAppTypes.filter { val exportableValueTypes = allAppTypes.filter {
it.key != MeasurementTypeKey.DATE && it.key != MeasurementTypeKey.TIME it.key != MeasurementTypeKey.DATE &&
it.key != MeasurementTypeKey.TIME &&
it.key != MeasurementTypeKey.USER
} }
val valueColumnKeys = exportableValueTypes.map { it.key.name }.distinct() val valueColumnKeys = exportableValueTypes.map { it.key.name }.distinct()
@@ -141,6 +143,7 @@ class ImportExportUseCases @Inject constructor(
InputFieldType.TIME -> value.dateValue?.let { InputFieldType.TIME -> value.dateValue?.let {
timeFormatter.format(Instant.ofEpochMilli(it).atZone(ZoneId.systemDefault())) timeFormatter.format(Instant.ofEpochMilli(it).atZone(ZoneId.systemDefault()))
} }
InputFieldType.USER -> null
} }
row[type.key.name] = s row[type.key.name] = s
} }

View File

@@ -0,0 +1,115 @@
/*
* openScale
* Copyright (C) 2025 olie.xdev <olie.xdeveloper@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.health.openscale.ui.screen.dialog
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.health.openscale.R
import com.health.openscale.core.data.MeasurementTypeIcon
import com.health.openscale.core.data.User
import com.health.openscale.ui.components.RoundMeasurementIcon
import java.util.Locale
@Composable
fun UserInputDialog(
title: String,
users: List<User>,
initialSelectedId: Int?,
measurementIcon: MeasurementTypeIcon,
iconBackgroundColor: Color,
onDismiss: () -> Unit,
onConfirm: (Int?) -> Unit
) {
var selectedId by remember(users, initialSelectedId) { mutableStateOf(initialSelectedId) }
val usersSorted = remember(users) {
users.sortedBy { it.name.lowercase(Locale.getDefault()) }
}
AlertDialog(
onDismissRequest = onDismiss,
confirmButton = {
TextButton(onClick = { onConfirm(selectedId) }) {
Text(stringResource(R.string.dialog_ok))
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text(stringResource(R.string.cancel_button))
}
},
title = {
Row(verticalAlignment = Alignment.CenterVertically) {
RoundMeasurementIcon(
icon = measurementIcon,
backgroundTint = iconBackgroundColor
)
Spacer(Modifier.width(12.dp))
Text(text = title, style = MaterialTheme.typography.titleMedium)
}
},
text = {
Box(Modifier.heightIn(max = 360.dp)) {
LazyColumn {
items(usersSorted, key = { it.id }) { user ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable { selectedId = user.id }
.padding(vertical = 8.dp)
) {
RadioButton(
selected = (selectedId == user.id),
onClick = { selectedId = user.id }
)
Spacer(Modifier.width(12.dp))
Text(
text = user.name,
style = MaterialTheme.typography.bodyLarge
)
}
}
}
}
}
)
}

View File

@@ -74,6 +74,7 @@ import com.health.openscale.ui.screen.dialog.DateInputDialog
import com.health.openscale.ui.screen.dialog.NumberInputDialog import com.health.openscale.ui.screen.dialog.NumberInputDialog
import com.health.openscale.ui.screen.dialog.TextInputDialog import com.health.openscale.ui.screen.dialog.TextInputDialog
import com.health.openscale.ui.screen.dialog.TimeInputDialog import com.health.openscale.ui.screen.dialog.TimeInputDialog
import com.health.openscale.ui.screen.dialog.UserInputDialog
import com.health.openscale.ui.screen.dialog.decrementValue import com.health.openscale.ui.screen.dialog.decrementValue
import com.health.openscale.ui.screen.dialog.incrementValue import com.health.openscale.ui.screen.dialog.incrementValue
import com.health.openscale.ui.shared.TopBarAction import com.health.openscale.ui.shared.TopBarAction
@@ -122,6 +123,10 @@ fun MeasurementDetailScreen(
val dateFormat = remember { DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.getDefault()) } val dateFormat = remember { DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.getDefault()) }
val timeFormat = remember { DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()) } val timeFormat = remember { DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()) }
val allUsers by sharedViewModel.allUsers.collectAsState()
var pendingUserId by remember { mutableStateOf<Int?>(null) }
var showUserPicker by remember { mutableStateOf(false) }
// Show a loading indicator if navigation is pending (e.g., after saving). // Show a loading indicator if navigation is pending (e.g., after saving).
if (isPendingNavigation) { if (isPendingNavigation) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
@@ -146,6 +151,7 @@ fun MeasurementDetailScreen(
currentMeasurementDbId = data.measurement.id currentMeasurementDbId = data.measurement.id
currentUserIdState = data.measurement.userId // Use UserID from the loaded measurement currentUserIdState = data.measurement.userId // Use UserID from the loaded measurement
measurementTimestampState = data.measurement.timestamp measurementTimestampState = data.measurement.timestamp
pendingUserId = null
valuesState.clear() valuesState.clear()
data.values.forEach { mvWithType -> data.values.forEach { mvWithType ->
// Populate valuesState for non-date/time, enabled types. // Populate valuesState for non-date/time, enabled types.
@@ -166,6 +172,7 @@ fun MeasurementDetailScreen(
currentMeasurementDbId = 0 currentMeasurementDbId = 0
currentUserIdState = userId // Use the passed userId for a new measurement currentUserIdState = userId // Use the passed userId for a new measurement
measurementTimestampState = System.currentTimeMillis() // Always use current timestamp for new measurementTimestampState = System.currentTimeMillis() // Always use current timestamp for new
pendingUserId = null
valuesState.clear() valuesState.clear()
// Preload values from the user's last measurement, if available and types are loaded. // Preload values from the user's last measurement, if available and types are loaded.
@@ -206,29 +213,25 @@ fun MeasurementDetailScreen(
icon = Icons.Default.Save, icon = Icons.Default.Save,
contentDescription = context.getString(R.string.action_save_measurement), contentDescription = context.getString(R.string.action_save_measurement),
onClick = { onClick = {
if (currentUserIdState == -1) { // Ensure a user is selected. val effectiveUserIdForSave = pendingUserId ?: currentUserIdState
Toast.makeText(context, R.string.toast_no_user_selected, Toast.LENGTH_SHORT)
.show() if (effectiveUserIdForSave == -1) {
Toast.makeText(context, R.string.toast_no_user_selected, Toast.LENGTH_SHORT).show()
return@TopBarAction return@TopBarAction
} }
// Prevent saving if it's a new measurement with the exact same timestamp as the user's last one.
if (currentMeasurementDbId == 0 && if (currentMeasurementDbId == 0 &&
lastMeasurementToPreloadFrom != null && lastMeasurementToPreloadFrom != null &&
lastMeasurementToPreloadFrom!!.measurement.userId == currentUserIdState && lastMeasurementToPreloadFrom!!.measurement.userId == effectiveUserIdForSave &&
measurementTimestampState == lastMeasurementToPreloadFrom!!.measurement.timestamp measurementTimestampState == lastMeasurementToPreloadFrom!!.measurement.timestamp
) { ) {
Toast.makeText( Toast.makeText(context, R.string.toast_duplicate_timestamp, Toast.LENGTH_LONG).show()
context,
R.string.toast_duplicate_timestamp,
Toast.LENGTH_LONG
).show()
return@TopBarAction return@TopBarAction
} }
val measurementToSave = Measurement( val measurementToSave = Measurement(
id = currentMeasurementDbId, id = currentMeasurementDbId,
userId = currentUserIdState, userId = effectiveUserIdForSave,
timestamp = measurementTimestampState timestamp = measurementTimestampState
) )
@@ -310,6 +313,7 @@ fun MeasurementDetailScreen(
if (allConversionsOk) { if (allConversionsOk) {
sharedViewModel.saveMeasurement(measurementToSave, valueList) sharedViewModel.saveMeasurement(measurementToSave, valueList)
pendingUserId = null
isPendingNavigation = true // Trigger loading indicator and navigate back. isPendingNavigation = true // Trigger loading indicator and navigate back.
navController.popBackStack() navController.popBackStack()
} }
@@ -346,6 +350,16 @@ fun MeasurementDetailScreen(
displayValue = timeFormat.format(Date(measurementTimestampState)) displayValue = timeFormat.format(Date(measurementTimestampState))
currentValueForIncrementDecrement = null // Not applicable currentValueForIncrementDecrement = null // Not applicable
} }
InputFieldType.USER -> {
val effectiveUserId = pendingUserId ?: currentUserIdState
val selectedUserName = allUsers
.firstOrNull { it.id == effectiveUserId }
?.name
?: stringResource(R.string.placeholder_empty_value)
displayValue = selectedUserName
currentValueForIncrementDecrement = null
}
else -> { // For FLOAT, INT, TEXT else -> { // For FLOAT, INT, TEXT
displayValue = valuesState[type.id] ?: "" displayValue = valuesState[type.id] ?: ""
currentValueForIncrementDecrement = valuesState[type.id] currentValueForIncrementDecrement = valuesState[type.id]
@@ -362,6 +376,7 @@ fun MeasurementDetailScreen(
when (type.inputType) { when (type.inputType) {
InputFieldType.DATE -> showDatePickerForMainTimestamp = true InputFieldType.DATE -> showDatePickerForMainTimestamp = true
InputFieldType.TIME -> showTimePickerForMainTimestamp = true InputFieldType.TIME -> showTimePickerForMainTimestamp = true
InputFieldType.USER -> showUserPicker = true
else -> dialogTargetType = type // Show generic dialog else -> dialogTargetType = type // Show generic dialog
} }
} }
@@ -464,7 +479,7 @@ fun MeasurementDetailScreen(
// --- Dialogs for the main measurement timestamp (measurementTimestampState) --- // --- Dialogs for the main measurement timestamp (measurementTimestampState) ---
if (showDatePickerForMainTimestamp) { if (showDatePickerForMainTimestamp) {
val triggeringType = allMeasurementTypes.find { it.key == MeasurementTypeKey.DATE } val triggeringType = allMeasurementTypes.find { it.key == MeasurementTypeKey.DATE }
val dateDialogTitle = stringResource(R.string.dialog_title_change_date, triggeringType?.getDisplayName(context) ?: stringResource(R.string.label_date)) val dateDialogTitle = stringResource(R.string.dialog_title_change_value, triggeringType?.getDisplayName(context) ?: stringResource(R.string.label_date))
DateInputDialog( DateInputDialog(
title = dateDialogTitle, title = dateDialogTitle,
initialTimestamp = measurementTimestampState, initialTimestamp = measurementTimestampState,
@@ -483,7 +498,7 @@ fun MeasurementDetailScreen(
if (showTimePickerForMainTimestamp) { if (showTimePickerForMainTimestamp) {
val triggeringType = allMeasurementTypes.find { it.key == MeasurementTypeKey.TIME } val triggeringType = allMeasurementTypes.find { it.key == MeasurementTypeKey.TIME }
val timeDialogTitle = stringResource(R.string.dialog_title_change_time, triggeringType?.getDisplayName(context) ?: stringResource(R.string.label_time)) val timeDialogTitle = stringResource(R.string.dialog_title_change_value, triggeringType?.getDisplayName(context) ?: stringResource(R.string.label_time))
TimeInputDialog( TimeInputDialog(
title = timeDialogTitle, title = timeDialogTitle,
initialTimestamp = measurementTimestampState, initialTimestamp = measurementTimestampState,
@@ -500,6 +515,24 @@ fun MeasurementDetailScreen(
} }
) )
} }
if (showUserPicker) {
val triggeringType = allMeasurementTypes.find { it.key == MeasurementTypeKey.USER }
val userDialogTitle = stringResource(R.string.dialog_title_change_value, triggeringType?.getDisplayName(context) ?: stringResource(R.string.measurement_type_user))
UserInputDialog(
title = userDialogTitle,
users = allUsers,
initialSelectedId = pendingUserId ?: currentUserIdState,
measurementIcon = triggeringType?.icon ?: MeasurementTypeIcon.IC_USER,
iconBackgroundColor = triggeringType?.let { Color(it.color) } ?: MaterialTheme.colorScheme.primary,
onDismiss = { showUserPicker = false },
onConfirm = { id ->
pendingUserId = id
showUserPicker = false
}
)
}
} }
/** /**

View File

@@ -930,6 +930,7 @@ fun MeasurementValueRow(
InputFieldType.TIME -> originalValue.dateValue?.let { InputFieldType.TIME -> originalValue.dateValue?.let {
DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()).format(Date(it)) DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()).format(Date(it))
} }
InputFieldType.USER -> null
} ?: "-" } ?: "-"
val context = LocalContext.current val context = LocalContext.current

View File

@@ -523,8 +523,10 @@ fun MeasurementTypeDetailScreen(
OutlinedSettingRow(label = stringResource(R.string.measurement_type_label_pinned)) { OutlinedSettingRow(label = stringResource(R.string.measurement_type_label_pinned)) {
Switch(checked = isPinned, onCheckedChange = { isPinned = it }) Switch(checked = isPinned, onCheckedChange = { isPinned = it })
} }
OutlinedSettingRow(label = stringResource(R.string.measurement_type_label_on_right_y_axis)) { if (selectedInputType == InputFieldType.FLOAT || selectedInputType == InputFieldType.INT) {
Switch(checked = isOnRightYAxis, onCheckedChange = { isOnRightYAxis = it }) OutlinedSettingRow(label = stringResource(R.string.measurement_type_label_on_right_y_axis)) {
Switch(checked = isOnRightYAxis, onCheckedChange = { isOnRightYAxis = it })
}
} }
} }

View File

@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="350"
android:viewportHeight="350">
<path
android:fillColor="#FF000000"
android:pathData="M175,171.173c38.914,0 70.463,-38.318 70.463,-85.586C245.463,38.318 235.105,0 175,0s-70.465,38.318 -70.465,85.587C104.535,132.855 136.084,171.173 175,171.173z"/>
<path
android:fillColor="#FF000000"
android:pathData="M41.909,301.853C41.897,298.971 41.885,301.041 41.909,301.853L41.909,301.853z"/>
<path
android:fillColor="#FF000000"
android:pathData="M308.085,304.104C308.123,303.315 308.098,298.63 308.085,304.104L308.085,304.104z"/>
<path
android:fillColor="#FF000000"
android:pathData="M307.935,298.397c-1.305,-82.342 -12.059,-105.805 -94.352,-120.657c0,0 -11.584,14.761 -38.584,14.761s-38.586,-14.761 -38.586,-14.761c-81.395,14.69 -92.803,37.805 -94.303,117.982c-0.123,6.547 -0.18,6.891 -0.202,6.131c0.005,1.424 0.011,4.058 0.011,8.651c0,0 19.592,39.496 133.08,39.496c113.486,0 133.08,-39.496 133.08,-39.496c0,-2.951 0.002,-5.003 0.005,-6.399C308.062,304.575 308.018,303.664 307.935,298.397z"/>
</vector>

View File

@@ -102,8 +102,7 @@
<!-- Dialoge (Allgemein & Messwertbearbeitung) --> <!-- Dialoge (Allgemein & Messwertbearbeitung) -->
<string name="dialog_title_edit_value">%1$s bearbeiten</string> <!-- Beispiel: "Gewicht bearbeiten" --> <string name="dialog_title_edit_value">%1$s bearbeiten</string> <!-- Beispiel: "Gewicht bearbeiten" -->
<string name="dialog_title_change_date">%1$s ändern</string> <!-- Beispiel: "Datum ändern" oder "Messdatum ändern" --> <string name="dialog_title_change_value">%1$s ändern</string> <!-- Beispiel: "Datum ändern" oder "Messdatum ändern" -->
<string name="dialog_title_change_time">%1$s ändern</string> <!-- Beispiel: "Zeit ändern" oder "Messzeit ändern" -->
<string name="dialog_title_select_color">Farbe auswählen</string> <string name="dialog_title_select_color">Farbe auswählen</string>
<string name="dialog_title_select_icon">Symbol auswählen</string> <string name="dialog_title_select_icon">Symbol auswählen</string>
<string name="dialog_title_input_value">Wert eingeben</string> <string name="dialog_title_input_value">Wert eingeben</string>
@@ -153,6 +152,7 @@
<string name="measurement_type_comment">Kommentar</string> <string name="measurement_type_comment">Kommentar</string>
<string name="measurement_type_date">Datum</string> <string name="measurement_type_date">Datum</string>
<string name="measurement_type_time">Uhrzeit</string> <string name="measurement_type_time">Uhrzeit</string>
<string name="measurement_type_user">Benutzer</string>
<string name="measurement_type_custom_default_name">Benutzerdefinierter Typ</string> <string name="measurement_type_custom_default_name">Benutzerdefinierter Typ</string>
<!-- Einstellungen für Messarten --> <!-- Einstellungen für Messarten -->

View File

@@ -102,9 +102,8 @@
<string name="toast_enter_valid_data">Please enter valid data.</string> <string name="toast_enter_valid_data">Please enter valid data.</string>
<!-- Dialogs (General & Measurement Edit) --> <!-- Dialogs (General & Measurement Edit) -->
<string name="dialog_title_edit_value">Edit %1$s</string> <!-- Example: "Edit Weight" --> <string name="dialog_title_edit_value">Edit %1$s</string>
<string name="dialog_title_change_date">Change %1$s</string> <!-- Example: "Change Date" or "Change Measurement Date" --> <string name="dialog_title_change_value">Change %1$s</string>
<string name="dialog_title_change_time">Change %1$s</string> <!-- Example: "Change Time" or "Change Measurement Time" -->
<string name="dialog_title_select_color">Select color</string> <string name="dialog_title_select_color">Select color</string>
<string name="dialog_title_select_icon">Select icon</string> <string name="dialog_title_select_icon">Select icon</string>
<string name="dialog_title_input_value">Input value</string> <string name="dialog_title_input_value">Input value</string>
@@ -155,6 +154,7 @@
<string name="measurement_type_comment">Comment</string> <string name="measurement_type_comment">Comment</string>
<string name="measurement_type_date">Date</string> <string name="measurement_type_date">Date</string>
<string name="measurement_type_time">Time</string> <string name="measurement_type_time">Time</string>
<string name="measurement_type_user">User</string>
<string name="measurement_type_custom_default_name">Custom Type</string> <string name="measurement_type_custom_default_name">Custom Type</string>
<!-- Measurement Type Settings --> <!-- Measurement Type Settings -->