mirror of
https://github.com/oliexdev/openScale.git
synced 2025-08-18 22:41:44 +02:00
Enable measurement value conversion when unit changes
This commit is contained in:
@@ -178,8 +178,11 @@ enum class MeasurementTypeKey(
|
|||||||
|
|
||||||
enum class UnitType(val displayName: String) {
|
enum class UnitType(val displayName: String) {
|
||||||
KG("kg"),
|
KG("kg"),
|
||||||
|
LB("lb"),
|
||||||
|
ST("st"),
|
||||||
PERCENT("%"),
|
PERCENT("%"),
|
||||||
CM("cm"),
|
CM("cm"),
|
||||||
|
INCH("in"),
|
||||||
KCAL("kcal"),
|
KCAL("kcal"),
|
||||||
NONE("")
|
NONE("")
|
||||||
}
|
}
|
||||||
|
@@ -179,6 +179,9 @@ class DatabaseRepository(
|
|||||||
fun getValuesForMeasurement(measurementId: Int): Flow<List<MeasurementValue>> =
|
fun getValuesForMeasurement(measurementId: Int): Flow<List<MeasurementValue>> =
|
||||||
measurementValueDao.getValuesForMeasurement(measurementId)
|
measurementValueDao.getValuesForMeasurement(measurementId)
|
||||||
|
|
||||||
|
fun getValuesForType(typeId: Int): Flow<List<MeasurementValue>> =
|
||||||
|
measurementValueDao.getValuesForType(typeId)
|
||||||
|
|
||||||
// --- Measurement Type Operations ---
|
// --- Measurement Type Operations ---
|
||||||
|
|
||||||
fun getAllMeasurementTypes(): Flow<List<MeasurementType>> = measurementTypeDao.getAll()
|
fun getAllMeasurementTypes(): Flow<List<MeasurementType>> = measurementTypeDao.getAll()
|
||||||
|
@@ -40,4 +40,7 @@ interface MeasurementValueDao {
|
|||||||
|
|
||||||
@Query("SELECT * FROM MeasurementValue WHERE measurementId = :measurementId")
|
@Query("SELECT * FROM MeasurementValue WHERE measurementId = :measurementId")
|
||||||
fun getValuesForMeasurement(measurementId: Int): Flow<List<MeasurementValue>>
|
fun getValuesForMeasurement(measurementId: Int): Flow<List<MeasurementValue>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM MeasurementValue WHERE typeId = :typeId")
|
||||||
|
fun getValuesForType(typeId: Int): Flow<List<MeasurementValue>>
|
||||||
}
|
}
|
@@ -18,6 +18,7 @@
|
|||||||
package com.health.openscale.core.utils
|
package com.health.openscale.core.utils
|
||||||
|
|
||||||
import com.health.openscale.core.data.MeasureUnit
|
import com.health.openscale.core.data.MeasureUnit
|
||||||
|
import com.health.openscale.core.data.UnitType
|
||||||
import com.health.openscale.core.data.WeightUnit
|
import com.health.openscale.core.data.WeightUnit
|
||||||
|
|
||||||
|
|
||||||
@@ -174,4 +175,64 @@ object Converters {
|
|||||||
toInt32Be(data, 0, value)
|
toInt32Be(data, 0, value)
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a Float value from one UnitType to another, if a conversion is defined.
|
||||||
|
* Returns the original value if no conversion is applicable or units are the same.
|
||||||
|
*
|
||||||
|
* @param value The float value to convert.
|
||||||
|
* @param fromUnit The original UnitType of the value.
|
||||||
|
* @param toUnit The target UnitType for the value.
|
||||||
|
* @return The converted float value, or the original value if no conversion is done.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun convertFloatValueUnit(value: Float, fromUnit: UnitType, toUnit: UnitType): Float {
|
||||||
|
if (fromUnit == toUnit) return value
|
||||||
|
|
||||||
|
// KG -> Andere Gewichtseinheiten
|
||||||
|
if (fromUnit == UnitType.KG) {
|
||||||
|
return when (toUnit) {
|
||||||
|
UnitType.LB -> fromKilogram(value, WeightUnit.LB)
|
||||||
|
UnitType.ST -> fromKilogram(value, WeightUnit.ST)
|
||||||
|
else -> value // Keine Umrechnung zu anderen Typen von KG aus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// LB -> Andere Gewichtseinheiten (erst zu KG, dann zum Ziel)
|
||||||
|
if (fromUnit == UnitType.LB) {
|
||||||
|
val kgValue = toKilogram(value, WeightUnit.LB)
|
||||||
|
return when (toUnit) {
|
||||||
|
UnitType.KG -> kgValue
|
||||||
|
UnitType.ST -> fromKilogram(kgValue, WeightUnit.ST)
|
||||||
|
else -> value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ST -> Andere Gewichtseinheiten (erst zu KG, dann zum Ziel)
|
||||||
|
if (fromUnit == UnitType.ST) {
|
||||||
|
val kgValue = toKilogram(value, WeightUnit.ST)
|
||||||
|
return when (toUnit) {
|
||||||
|
UnitType.KG -> kgValue
|
||||||
|
UnitType.LB -> fromKilogram(kgValue, WeightUnit.LB)
|
||||||
|
else -> value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CM -> Andere Längeneinheiten
|
||||||
|
if (fromUnit == UnitType.CM) {
|
||||||
|
return when (toUnit) {
|
||||||
|
UnitType.INCH -> fromCentimeter(value, MeasureUnit.INCH)
|
||||||
|
else -> value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromUnit == UnitType.INCH) {
|
||||||
|
val cmValue = toCentimeter(value, MeasureUnit.INCH)
|
||||||
|
return when (toUnit) {
|
||||||
|
UnitType.CM -> cmValue
|
||||||
|
else -> value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -35,6 +35,7 @@ import androidx.compose.foundation.shape.CircleShape
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.QuestionMark
|
import androidx.compose.material.icons.filled.QuestionMark
|
||||||
import androidx.compose.material.icons.filled.Save
|
import androidx.compose.material.icons.filled.Save
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
@@ -48,6 +49,7 @@ import androidx.compose.material3.OutlinedTextFieldDefaults
|
|||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TextFieldDefaults
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -76,6 +78,7 @@ import com.health.openscale.ui.screen.SharedViewModel
|
|||||||
import com.health.openscale.ui.screen.dialog.ColorPickerDialog
|
import com.health.openscale.ui.screen.dialog.ColorPickerDialog
|
||||||
import com.health.openscale.ui.screen.dialog.IconPickerDialog
|
import com.health.openscale.ui.screen.dialog.IconPickerDialog
|
||||||
import com.health.openscale.ui.screen.dialog.getIconResIdByName
|
import com.health.openscale.ui.screen.dialog.getIconResIdByName
|
||||||
|
import kotlin.text.lowercase
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Composable screen for creating or editing a [MeasurementType].
|
* Composable screen for creating or editing a [MeasurementType].
|
||||||
@@ -117,6 +120,9 @@ fun MeasurementTypeDetailScreen(
|
|||||||
var showColorPicker by remember { mutableStateOf(false) }
|
var showColorPicker by remember { mutableStateOf(false) }
|
||||||
var showIconPicker by remember { mutableStateOf(false) }
|
var showIconPicker by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
var showConfirmDialog by remember { mutableStateOf(false) }
|
||||||
|
var pendingUpdatedType by remember { mutableStateOf<MeasurementType?>(null) }
|
||||||
|
|
||||||
val titleEdit = stringResource(R.string.measurement_type_detail_title_edit)
|
val titleEdit = stringResource(R.string.measurement_type_detail_title_edit)
|
||||||
val titleAdd = stringResource(R.string.measurement_type_detail_title_add)
|
val titleAdd = stringResource(R.string.measurement_type_detail_title_add)
|
||||||
|
|
||||||
@@ -144,11 +150,20 @@ fun MeasurementTypeDetailScreen(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
settingsViewModel.updateMeasurementType(updatedType)
|
val unitChanged = existingType!!.unit != updatedType.unit
|
||||||
|
val inputTypesAreFloat = existingType!!.inputType == InputFieldType.FLOAT && updatedType.inputType == InputFieldType.FLOAT
|
||||||
|
|
||||||
|
if (unitChanged && inputTypesAreFloat) {
|
||||||
|
pendingUpdatedType = updatedType
|
||||||
|
showConfirmDialog = true
|
||||||
|
} else {
|
||||||
|
settingsViewModel.updateMeasurementType(updatedType)
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
settingsViewModel.addMeasurementType(updatedType)
|
settingsViewModel.addMeasurementType(updatedType)
|
||||||
|
navController.popBackStack()
|
||||||
}
|
}
|
||||||
navController.popBackStack()
|
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(context, R.string.toast_enter_valid_data, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.toast_enter_valid_data, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
@@ -156,6 +171,40 @@ fun MeasurementTypeDetailScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showConfirmDialog && existingType != null && pendingUpdatedType != null) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showConfirmDialog = false },
|
||||||
|
title = { Text(stringResource(R.string.measurement_type_dialog_confirm_unit_change_title)) },
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
stringResource(
|
||||||
|
R.string.measurement_type_dialog_confirm_unit_change_message,
|
||||||
|
existingType!!.getDisplayName(context),
|
||||||
|
existingType!!.unit.name.lowercase().replaceFirstChar { it.uppercase() },
|
||||||
|
pendingUpdatedType!!.unit.name.lowercase().replaceFirstChar { it.uppercase() }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
|
settingsViewModel.updateMeasurementTypeAndConvertDataViewModelCentric(
|
||||||
|
originalType = existingType!!,
|
||||||
|
updatedType = pendingUpdatedType!!
|
||||||
|
)
|
||||||
|
showConfirmDialog = false
|
||||||
|
navController.popBackStack()
|
||||||
|
}) {
|
||||||
|
Text(stringResource(R.string.confirm_button))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { showConfirmDialog = false }) {
|
||||||
|
Text(stringResource(R.string.cancel_button))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
|
@@ -19,6 +19,7 @@ package com.health.openscale.ui.screen.settings
|
|||||||
|
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import androidx.compose.material3.SnackbarDuration
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.github.doyaaaaaken.kotlincsv.dsl.csvReader
|
import com.github.doyaaaaaken.kotlincsv.dsl.csvReader
|
||||||
@@ -31,6 +32,7 @@ import com.health.openscale.core.data.MeasurementTypeKey
|
|||||||
import com.health.openscale.core.data.MeasurementValue
|
import com.health.openscale.core.data.MeasurementValue
|
||||||
import com.health.openscale.core.data.User
|
import com.health.openscale.core.data.User
|
||||||
import com.health.openscale.core.model.MeasurementWithValues
|
import com.health.openscale.core.model.MeasurementWithValues
|
||||||
|
import com.health.openscale.core.utils.Converters
|
||||||
import com.health.openscale.core.utils.LogManager
|
import com.health.openscale.core.utils.LogManager
|
||||||
import com.health.openscale.ui.screen.SharedViewModel
|
import com.health.openscale.ui.screen.SharedViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -1141,6 +1143,148 @@ class SettingsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing measurement type in the database.
|
||||||
|
* If the unit has changed for a FLOAT type, it will also convert associated measurement values.
|
||||||
|
* This version performs operations sequentially without a single overarching repository transaction
|
||||||
|
* and relies on the ViewModel for orchestration.
|
||||||
|
*
|
||||||
|
* @param originalType The MeasurementType as it was BEFORE any edits in the UI.
|
||||||
|
* Crucially contains the original unit and ID.
|
||||||
|
* @param updatedType The MeasurementType object with potentially updated information from the UI
|
||||||
|
* (new name, new unit, new color, etc.).
|
||||||
|
* @param showSnackbarMaster Boolean to control if a snackbar is shown after the entire operation.
|
||||||
|
* Individual snackbars for errors might still appear.
|
||||||
|
*/
|
||||||
|
fun updateMeasurementTypeAndConvertDataViewModelCentric(
|
||||||
|
originalType: MeasurementType,
|
||||||
|
updatedType: MeasurementType,
|
||||||
|
showSnackbarMaster: Boolean = true
|
||||||
|
) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
LogManager.i(
|
||||||
|
TAG,
|
||||||
|
"ViewModelCentric Update: Type ID ${originalType.id}. Original Unit: ${originalType.unit}, New Unit: ${updatedType.unit}"
|
||||||
|
)
|
||||||
|
|
||||||
|
var conversionErrorOccurred = false
|
||||||
|
var valuesConvertedCount = 0
|
||||||
|
|
||||||
|
if (originalType.unit != updatedType.unit && originalType.inputType == InputFieldType.FLOAT && updatedType.inputType == InputFieldType.FLOAT) {
|
||||||
|
LogManager.i(
|
||||||
|
TAG,
|
||||||
|
"Unit changed for FLOAT type ID ${originalType.id} from ${originalType.unit} to ${updatedType.unit}. Converting values."
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
val valuesToConvert = repository.getValuesForType(originalType.id).first() // .first() um den aktuellen Wert des Flows zu erhalten
|
||||||
|
|
||||||
|
if (valuesToConvert.isNotEmpty()) {
|
||||||
|
LogManager.d(TAG, "Found ${valuesToConvert.size} values of type ID ${originalType.id} to potentially convert.")
|
||||||
|
val updatedValuesBatch = mutableListOf<MeasurementValue>()
|
||||||
|
|
||||||
|
valuesToConvert.forEach { valueToConvert ->
|
||||||
|
valueToConvert.floatValue?.let { currentFloatVal ->
|
||||||
|
val convertedFloat = Converters.convertFloatValueUnit(
|
||||||
|
value = currentFloatVal,
|
||||||
|
fromUnit = originalType.unit,
|
||||||
|
toUnit = updatedType.unit
|
||||||
|
)
|
||||||
|
|
||||||
|
if (convertedFloat != currentFloatVal) {
|
||||||
|
updatedValuesBatch.add(valueToConvert.copy(floatValue = convertedFloat))
|
||||||
|
valuesConvertedCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatedValuesBatch.isNotEmpty()) {
|
||||||
|
LogManager.d(TAG, "Updating ${updatedValuesBatch.size} values in batch for type ID ${originalType.id}.")
|
||||||
|
// Aktualisiere jeden Wert einzeln. Dein Repository.updateMeasurementValue
|
||||||
|
// stößt die Neuberechnung der abgeleiteten Werte an.
|
||||||
|
updatedValuesBatch.forEach { repository.updateMeasurementValue(it) }
|
||||||
|
LogManager.d(TAG, "Batch update of ${updatedValuesBatch.size} values completed for type ID ${originalType.id}.")
|
||||||
|
} else {
|
||||||
|
LogManager.i(TAG, "No values required actual conversion or update for type ID ${originalType.id} after checking.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LogManager.i(TAG, "No values found for type ID ${originalType.id} to convert.")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
LogManager.e(TAG, "Error during value conversion/update for type ID ${originalType.id}", e)
|
||||||
|
conversionErrorOccurred = true
|
||||||
|
// Verwende hier getDisplayName vom originalType, da updatedType möglicherweise noch nicht committet wurde.
|
||||||
|
sharedViewModel.showSnackbar(
|
||||||
|
messageResId = R.string.measurement_type_update_error_conversion_failed,
|
||||||
|
formatArgs = listOf(originalType.name ?: originalType.key.toString())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (originalType.unit != updatedType.unit) {
|
||||||
|
LogManager.i(
|
||||||
|
TAG,
|
||||||
|
"Unit changed for type ID ${originalType.id}, but InputType is not FLOAT (Original: ${originalType.inputType}, Updated: ${updatedType.inputType}). No value conversion performed."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conversionErrorOccurred) {
|
||||||
|
try {
|
||||||
|
val finalTypeToUpdate = MeasurementType(
|
||||||
|
id = originalType.id,
|
||||||
|
key = originalType.key,
|
||||||
|
name = updatedType.name,
|
||||||
|
color = updatedType.color,
|
||||||
|
icon = updatedType.icon,
|
||||||
|
unit = updatedType.unit,
|
||||||
|
inputType = updatedType.inputType,
|
||||||
|
displayOrder = originalType.displayOrder,
|
||||||
|
isDerived = originalType.isDerived,
|
||||||
|
isEnabled = updatedType.isEnabled,
|
||||||
|
isPinned = updatedType.isPinned,
|
||||||
|
isOnRightYAxis = updatedType.isOnRightYAxis
|
||||||
|
)
|
||||||
|
|
||||||
|
repository.updateMeasurementType(finalTypeToUpdate)
|
||||||
|
LogManager.i(
|
||||||
|
TAG,
|
||||||
|
"MeasurementType (ID: ${originalType.id}) updated successfully to new unit '${finalTypeToUpdate.unit}'."
|
||||||
|
)
|
||||||
|
|
||||||
|
if (showSnackbarMaster) {
|
||||||
|
if (valuesConvertedCount > 0) {
|
||||||
|
sharedViewModel.showSnackbar(
|
||||||
|
messageResId = R.string.measurement_type_updated_and_values_converted_successfully,
|
||||||
|
formatArgs = listOf(updatedType.name ?: updatedType.key.toString(), valuesConvertedCount.toString()) // Context für getDisplayName wäre besser
|
||||||
|
)
|
||||||
|
} else if (originalType.unit != updatedType.unit && originalType.inputType == InputFieldType.FLOAT) {
|
||||||
|
sharedViewModel.showSnackbar(
|
||||||
|
messageResId = R.string.measurement_type_updated_unit_changed_no_values_converted,
|
||||||
|
formatArgs = listOf(updatedType.name ?: updatedType.key.toString()) // Context für getDisplayName wäre besser
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sharedViewModel.showSnackbar(
|
||||||
|
messageResId = R.string.measurement_type_updated_successfully,
|
||||||
|
formatArgs = listOf(updatedType.name ?: updatedType.key.toString()) // Context für getDisplayName wäre besser
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// sharedViewModel.refreshMeasurementTypes() // Optional, wenn die Liste sich nicht automatisch aktualisiert
|
||||||
|
} catch (e: Exception) {
|
||||||
|
LogManager.e(TAG, "Error updating MeasurementType (ID: ${originalType.id}) itself", e)
|
||||||
|
sharedViewModel.showSnackbar(
|
||||||
|
messageResId = R.string.measurement_type_updated_error,
|
||||||
|
formatArgs = listOf(originalType.name ?: originalType.key.toString()) // Context für getDisplayName wäre besser
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LogManager.w(
|
||||||
|
TAG,
|
||||||
|
"Skipped updating MeasurementType definition (ID: ${originalType.id}) due to prior value conversion error."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates an existing measurement type in the database.
|
* Updates an existing measurement type in the database.
|
||||||
* This operation is performed in a background coroutine.
|
* This operation is performed in a background coroutine.
|
||||||
|
@@ -152,6 +152,11 @@
|
|||||||
<string name="measurement_type_deleted_error">Fehler beim Löschen der Messart \'%1$s\'.</string>
|
<string name="measurement_type_deleted_error">Fehler beim Löschen der Messart \'%1$s\'.</string>
|
||||||
<string name="measurement_type_updated_successfully">Messart \'%1$s\' erfolgreich aktualisiert.</string>
|
<string name="measurement_type_updated_successfully">Messart \'%1$s\' erfolgreich aktualisiert.</string>
|
||||||
<string name="measurement_type_updated_error">Fehler beim Aktualisieren der Messart \'%1$s\'.</string>
|
<string name="measurement_type_updated_error">Fehler beim Aktualisieren der Messart \'%1$s\'.</string>
|
||||||
|
<string name="measurement_type_updated_and_values_converted_successfully">Typ \'%1$s\' aktualisiert und %2$s Wert(e) erfolgreich konvertiert.</string>
|
||||||
|
<string name="measurement_type_update_error_conversion_failed">Fehler beim Konvertieren der Werte für Typ \'%1$s\'. Die Typdefinition wurde nicht aktualisiert.</string>
|
||||||
|
<string name="measurement_type_updated_unit_changed_no_values_converted">Typ \'%1$s\' aktualisiert. Einheit geändert, aber es mussten keine vorhandenen Werte konvertiert werden.</string>
|
||||||
|
<string name="measurement_type_dialog_confirm_unit_change_title">Einheitenänderung bestätigen</string>
|
||||||
|
<string name="measurement_type_dialog_confirm_unit_change_message">Wenn Sie die Einheit für \'%1$s\' von %2$s auf %3$s ändern, werden alle vorhandenen Datenpunkte konvertiert. Dieser Vorgang kann einige Zeit dauern und kann nicht einfach rückgängig gemacht werden. Möchten Sie fortfahren?</string>
|
||||||
|
|
||||||
<!-- Bluetooth -->
|
<!-- Bluetooth -->
|
||||||
<string name="info_bluetooth_connection_error_scale_offline">Verbindung zur Waage fehlgeschlagen, bitte stellen Sie sicher, dass sie eingeschaltet ist.</string>
|
<string name="info_bluetooth_connection_error_scale_offline">Verbindung zur Waage fehlgeschlagen, bitte stellen Sie sicher, dass sie eingeschaltet ist.</string>
|
||||||
|
@@ -151,8 +151,13 @@
|
|||||||
<string name="measurement_type_added_error">Error adding measurement type \'%1$s\'.</string>
|
<string name="measurement_type_added_error">Error adding measurement type \'%1$s\'.</string>
|
||||||
<string name="measurement_type_deleted_successfully">Measurement type \'%1$s\' deleted successfully.</string>
|
<string name="measurement_type_deleted_successfully">Measurement type \'%1$s\' deleted successfully.</string>
|
||||||
<string name="measurement_type_deleted_error">Error deleting measurement type \'%1$s\'.</string>
|
<string name="measurement_type_deleted_error">Error deleting measurement type \'%1$s\'.</string>
|
||||||
<string name="measurement_type_updated_successfully">Measurement type \'%1$s\' updated successfully.</string>
|
|
||||||
<string name="measurement_type_updated_error">Error updating measurement type \'%1$s\'.</string>
|
<string name="measurement_type_updated_error">Error updating measurement type \'%1$s\'.</string>
|
||||||
|
<string name="measurement_type_updated_successfully">Measurement type \'%1$s\' updated successfully.</string>
|
||||||
|
<string name="measurement_type_updated_and_values_converted_successfully">Type \'%1$s\' updated and %2$s value(s) converted successfully.</string>
|
||||||
|
<string name="measurement_type_update_error_conversion_failed">Error converting values for type \'%1$s\'. The type definition was not updated.</string>
|
||||||
|
<string name="measurement_type_updated_unit_changed_no_values_converted">Type \'%1$s\' updated. Unit changed, but no existing values required conversion.</string>
|
||||||
|
<string name="measurement_type_dialog_confirm_unit_change_title">Confirm Unit Change</string>
|
||||||
|
<string name="measurement_type_dialog_confirm_unit_change_message">Changing the unit for \'%1$s\' from %2$s to %3$s will convert all existing data points. This action may take some time and cannot be easily undone. Do you want to proceed?</string>
|
||||||
|
|
||||||
<!-- Bluetooth -->
|
<!-- Bluetooth -->
|
||||||
<string name="info_bluetooth_connection_error_scale_offline">Could not connect to scale, please ensure it is on.</string>
|
<string name="info_bluetooth_connection_error_scale_offline">Could not connect to scale, please ensure it is on.</string>
|
||||||
|
Reference in New Issue
Block a user