mirror of
https://github.com/oliexdev/openScale.git
synced 2025-08-22 16:23:09 +02:00
enhances unit conversion capabilities, particularly for BODY_FAT
, WATER
, and MUSCLE
measurement types. These types can now be converted between percentage (%) and absolute mass units (kg, lb, st)
This commit is contained in:
@@ -153,9 +153,9 @@ enum class MeasurementTypeKey(
|
|||||||
) {
|
) {
|
||||||
WEIGHT(1, R.string.measurement_type_weight, listOf(UnitType.KG, UnitType.LB, UnitType.ST), listOf(InputFieldType.FLOAT)),
|
WEIGHT(1, R.string.measurement_type_weight, listOf(UnitType.KG, UnitType.LB, UnitType.ST), listOf(InputFieldType.FLOAT)),
|
||||||
BMI(2, R.string.measurement_type_bmi, listOf(UnitType.NONE), listOf(InputFieldType.FLOAT)),
|
BMI(2, R.string.measurement_type_bmi, listOf(UnitType.NONE), listOf(InputFieldType.FLOAT)),
|
||||||
BODY_FAT(3, R.string.measurement_type_body_fat, listOf(UnitType.PERCENT), listOf(InputFieldType.FLOAT)),
|
BODY_FAT(3, R.string.measurement_type_body_fat, listOf(UnitType.PERCENT, UnitType.KG, UnitType.LB, UnitType.ST), listOf(InputFieldType.FLOAT)),
|
||||||
WATER(4, R.string.measurement_type_water, listOf(UnitType.PERCENT), listOf(InputFieldType.FLOAT)),
|
WATER(4, R.string.measurement_type_water, listOf(UnitType.PERCENT, UnitType.KG, UnitType.LB, UnitType.ST), listOf(InputFieldType.FLOAT)),
|
||||||
MUSCLE(5, R.string.measurement_type_muscle, listOf(UnitType.PERCENT), listOf(InputFieldType.FLOAT)),
|
MUSCLE(5, R.string.measurement_type_muscle, listOf(UnitType.PERCENT, UnitType.KG, UnitType.LB, UnitType.ST), listOf(InputFieldType.FLOAT)),
|
||||||
LBM(6, R.string.measurement_type_lbm, listOf(UnitType.KG, UnitType.LB, UnitType.ST), listOf(InputFieldType.FLOAT)),
|
LBM(6, R.string.measurement_type_lbm, listOf(UnitType.KG, UnitType.LB, UnitType.ST), listOf(InputFieldType.FLOAT)),
|
||||||
BONE(7, R.string.measurement_type_bone, listOf(UnitType.KG, UnitType.LB), listOf(InputFieldType.FLOAT)),
|
BONE(7, R.string.measurement_type_bone, listOf(UnitType.KG, UnitType.LB), listOf(InputFieldType.FLOAT)),
|
||||||
WAIST(8, R.string.measurement_type_waist, listOf(UnitType.CM, UnitType.INCH), listOf(InputFieldType.FLOAT)),
|
WAIST(8, R.string.measurement_type_waist, listOf(UnitType.CM, UnitType.INCH), listOf(InputFieldType.FLOAT)),
|
||||||
@@ -189,7 +189,11 @@ enum class UnitType(val displayName: String) {
|
|||||||
CM("cm"),
|
CM("cm"),
|
||||||
INCH("in"),
|
INCH("in"),
|
||||||
KCAL("kcal"),
|
KCAL("kcal"),
|
||||||
NONE("")
|
NONE("");
|
||||||
|
|
||||||
|
fun isWeightUnit(): Boolean {
|
||||||
|
return this == KG || this == LB || this == ST
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class InputFieldType {
|
enum class InputFieldType {
|
||||||
|
@@ -272,7 +272,7 @@ class DatabaseRepository(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If derived value is not null, insert or update it
|
// If derived value is not null, insert or update it
|
||||||
val roundedValue = roundTo(derivedValue) // Apply rounding
|
val roundedValue = CalculationUtil.roundTo(derivedValue) // Apply rounding
|
||||||
if (existingDerivedValueObject != null) {
|
if (existingDerivedValueObject != null) {
|
||||||
if (existingDerivedValueObject.floatValue != roundedValue) {
|
if (existingDerivedValueObject.floatValue != roundedValue) {
|
||||||
measurementValueDao.update(existingDerivedValueObject.copy(floatValue = roundedValue))
|
measurementValueDao.update(existingDerivedValueObject.copy(floatValue = roundedValue))
|
||||||
@@ -550,12 +550,5 @@ class DatabaseRepository(
|
|||||||
fatPercentage
|
fatPercentage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Rounds a float value to two decimal places.
|
|
||||||
*/
|
|
||||||
private fun roundTo(value: Float): Float {
|
|
||||||
return (value * 100).toInt() / 100.0f
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -39,6 +39,13 @@ object CalculationUtil {
|
|||||||
|
|
||||||
return Period.between(birthDate, today).years
|
return Period.between(birthDate, today).years
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds a float value to two decimal places.
|
||||||
|
*/
|
||||||
|
fun roundTo(value: Float): Float {
|
||||||
|
return (value * 100).toInt() / 100.0f
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -17,13 +17,16 @@
|
|||||||
*/
|
*/
|
||||||
package com.health.openscale.ui.screen.dialog
|
package com.health.openscale.ui.screen.dialog
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.copy
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
@@ -33,6 +36,7 @@ import androidx.compose.material.icons.filled.KeyboardArrowDown
|
|||||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
@@ -49,14 +53,17 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
import com.health.openscale.R
|
import com.health.openscale.R
|
||||||
import com.health.openscale.core.data.InputFieldType
|
import com.health.openscale.core.data.InputFieldType
|
||||||
|
import com.health.openscale.core.data.UnitType
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NumberInputDialog(
|
fun NumberInputDialog(
|
||||||
title: String,
|
title: String,
|
||||||
initialValue: String,
|
initialValue: String,
|
||||||
inputType: InputFieldType,
|
inputType: InputFieldType,
|
||||||
|
unit: UnitType,
|
||||||
iconRes: Int,
|
iconRes: Int,
|
||||||
color: Color,
|
color: Color,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
@@ -112,6 +119,15 @@ fun NumberInputDialog(
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.End
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = unit.displayName,
|
||||||
|
modifier = Modifier.padding(end = 8.dp),
|
||||||
|
style = LocalTextStyle.current.copy(fontSize = 14.sp)
|
||||||
|
)
|
||||||
if (inputType == InputFieldType.INT || inputType == InputFieldType.FLOAT) {
|
if (inputType == InputFieldType.INT || inputType == InputFieldType.FLOAT) {
|
||||||
Column {
|
Column {
|
||||||
Icon(
|
Icon(
|
||||||
@@ -134,6 +150,7 @@ fun NumberInputDialog(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
|
@@ -372,6 +372,7 @@ fun MeasurementDetailScreen(
|
|||||||
title = dialogTitle,
|
title = dialogTitle,
|
||||||
initialValue = initialDialogValue,
|
initialValue = initialDialogValue,
|
||||||
inputType = currentType.inputType,
|
inputType = currentType.inputType,
|
||||||
|
unit = currentType.unit,
|
||||||
iconRes = typeIconRes,
|
iconRes = typeIconRes,
|
||||||
color = typeColor,
|
color = typeColor,
|
||||||
onDismiss = { dialogTargetType = null },
|
onDismiss = { dialogTargetType = null },
|
||||||
|
@@ -30,8 +30,11 @@ import com.health.openscale.core.data.Measurement
|
|||||||
import com.health.openscale.core.data.MeasurementType
|
import com.health.openscale.core.data.MeasurementType
|
||||||
import com.health.openscale.core.data.MeasurementTypeKey
|
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.UnitType
|
||||||
import com.health.openscale.core.data.User
|
import com.health.openscale.core.data.User
|
||||||
|
import com.health.openscale.core.data.WeightUnit
|
||||||
import com.health.openscale.core.model.MeasurementWithValues
|
import com.health.openscale.core.model.MeasurementWithValues
|
||||||
|
import com.health.openscale.core.utils.CalculationUtil
|
||||||
import com.health.openscale.core.utils.Converters
|
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
|
||||||
@@ -1158,28 +1161,31 @@ class SettingsViewModel(
|
|||||||
*/
|
*/
|
||||||
fun updateMeasurementTypeAndConvertDataViewModelCentric(
|
fun updateMeasurementTypeAndConvertDataViewModelCentric(
|
||||||
originalType: MeasurementType,
|
originalType: MeasurementType,
|
||||||
updatedType: MeasurementType, // This contains the new unit and other proposed changes from the UI
|
updatedType: MeasurementType, // Contains the new unit and other proposed changes from the UI
|
||||||
showSnackbarMaster: Boolean = true
|
showSnackbarMaster: Boolean = true
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
val typeKey = originalType.key
|
||||||
|
val oldUnit = originalType.unit
|
||||||
|
val newUnit = updatedType.unit
|
||||||
|
|
||||||
LogManager.i(
|
LogManager.i(
|
||||||
TAG,
|
TAG,
|
||||||
"ViewModelCentric Update: Type ID ${originalType.id}. Original Unit: ${originalType.unit}, New Unit: ${updatedType.unit}"
|
"ViewModelCentric Update for $typeKey (ID ${originalType.id}). From Unit: $oldUnit, To Unit: $newUnit"
|
||||||
)
|
)
|
||||||
|
|
||||||
var conversionErrorOccurred = false
|
var mainOperationErrorOccurred = false
|
||||||
var valuesConvertedCount = 0
|
var conversionProcessAttempted = false
|
||||||
|
var valuesSuccessfullyConvertedCount = 0
|
||||||
|
|
||||||
// 1. First, update the MeasurementType definition in the database.
|
// 1. Update the MeasurementType definition in the database.
|
||||||
// This ensures that any subsequent recalculations of derived values
|
|
||||||
// will use the correct (new) unit for this type.
|
|
||||||
val finalTypeToUpdate = MeasurementType(
|
val finalTypeToUpdate = MeasurementType(
|
||||||
id = originalType.id,
|
id = originalType.id,
|
||||||
key = originalType.key, // Key should be immutable for an existing type
|
key = typeKey,
|
||||||
name = updatedType.name,
|
name = updatedType.name,
|
||||||
color = updatedType.color,
|
color = updatedType.color,
|
||||||
icon = updatedType.icon,
|
icon = updatedType.icon,
|
||||||
unit = updatedType.unit, // Crucial: The new unit
|
unit = newUnit, // Apply the new unit
|
||||||
inputType = updatedType.inputType,
|
inputType = updatedType.inputType,
|
||||||
displayOrder = originalType.displayOrder,
|
displayOrder = originalType.displayOrder,
|
||||||
isDerived = originalType.isDerived,
|
isDerived = originalType.isDerived,
|
||||||
@@ -1192,101 +1198,162 @@ class SettingsViewModel(
|
|||||||
repository.updateMeasurementType(finalTypeToUpdate)
|
repository.updateMeasurementType(finalTypeToUpdate)
|
||||||
LogManager.i(
|
LogManager.i(
|
||||||
TAG,
|
TAG,
|
||||||
"MeasurementType (ID: ${originalType.id}) definition updated successfully to new unit '${finalTypeToUpdate.unit}'."
|
"MeasurementType $typeKey (ID: ${originalType.id}) definition updated successfully to new unit '$newUnit'."
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
LogManager.e(TAG, "Error updating MeasurementType (ID: ${originalType.id}) definition itself", e)
|
LogManager.e(TAG, "Error updating MeasurementType $typeKey (ID: ${originalType.id}) definition itself", e)
|
||||||
sharedViewModel.showSnackbar(
|
sharedViewModel.showSnackbar(
|
||||||
messageResId = R.string.measurement_type_updated_error,
|
messageResId = R.string.measurement_type_updated_error,
|
||||||
// Consider using context.getString for display names if not available in originalType
|
formatArgs = listOf(originalType.name ?: typeKey.toString())
|
||||||
formatArgs = listOf(originalType.name ?: originalType.key.toString())
|
|
||||||
)
|
)
|
||||||
conversionErrorOccurred = true // Prevent further steps if this critical update fails
|
mainOperationErrorOccurred = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. If the type definition was updated successfully AND the unit has changed for a FLOAT type,
|
// 2. If the type definition was updated successfully AND the unit has changed,
|
||||||
// convert the associated MeasurementValue entries.
|
// convert associated MeasurementValue entries.
|
||||||
if (!conversionErrorOccurred &&
|
// The repository.updateMeasurementValue() method will trigger derived value recalculations.
|
||||||
originalType.unit != updatedType.unit &&
|
if (!mainOperationErrorOccurred && oldUnit != newUnit) {
|
||||||
originalType.inputType == InputFieldType.FLOAT &&
|
conversionProcessAttempted = true
|
||||||
updatedType.inputType == InputFieldType.FLOAT
|
|
||||||
) {
|
|
||||||
LogManager.i(
|
LogManager.i(
|
||||||
TAG,
|
TAG,
|
||||||
"Unit changed for FLOAT type ID ${originalType.id}. Converting values AFTER type definition update."
|
"Unit changed for $typeKey (ID ${originalType.id}). Attempting to convert values."
|
||||||
)
|
)
|
||||||
try {
|
try {
|
||||||
// Fetch all values belonging to this type
|
// Fetch all current values for this type to process them.
|
||||||
val valuesToConvert = repository.getValuesForType(originalType.id).first()
|
val allValuesForThisType = repository.getValuesForType(originalType.id).first()
|
||||||
|
|
||||||
if (valuesToConvert.isNotEmpty()) {
|
if (allValuesForThisType.isNotEmpty()) {
|
||||||
LogManager.d(TAG, "Found ${valuesToConvert.size} values of type ID ${originalType.id} to potentially convert.")
|
LogManager.d(TAG, "Found ${allValuesForThisType.size} values of type $typeKey to potentially convert.")
|
||||||
val updatedValuesBatch = mutableListOf<MeasurementValue>()
|
|
||||||
|
|
||||||
valuesToConvert.forEach { valueToConvert ->
|
for (valueToConvert in allValuesForThisType) {
|
||||||
valueToConvert.floatValue?.let { currentFloatVal ->
|
val currentValue = valueToConvert.floatValue ?: continue // Skip if no float value
|
||||||
// Convert from the original unit of the values to the new target unit
|
var convertedValue: Float? = null
|
||||||
val convertedFloat = Converters.convertFloatValueUnit(
|
|
||||||
value = currentFloatVal,
|
|
||||||
fromUnit = originalType.unit, // Important: Use the unit the values currently have
|
|
||||||
toUnit = updatedType.unit // The new target unit
|
|
||||||
)
|
|
||||||
|
|
||||||
if (convertedFloat != currentFloatVal) { // Add only if the value actually changes
|
// --- Special handling for types like BODY_FAT, WATER, MUSCLE (Percent <-> Absolute Mass) ---
|
||||||
updatedValuesBatch.add(valueToConvert.copy(floatValue = convertedFloat))
|
if (typeKey == MeasurementTypeKey.BODY_FAT ||
|
||||||
valuesConvertedCount++
|
typeKey == MeasurementTypeKey.WATER ||
|
||||||
}
|
typeKey == MeasurementTypeKey.MUSCLE
|
||||||
}
|
) {
|
||||||
|
// Fetch the global MeasurementType for WEIGHT to get its ID and current unit
|
||||||
|
val weightMeasurementTypeEntity = repository.getAllMeasurementTypes().first()
|
||||||
|
.find { it.key == MeasurementTypeKey.WEIGHT }
|
||||||
|
|
||||||
|
// Fetch the specific MeasurementValue for WEIGHT for the current measurement being processed
|
||||||
|
val weightValueObject = if (weightMeasurementTypeEntity != null) {
|
||||||
|
repository.getValuesForMeasurement(valueToConvert.measurementId).first()
|
||||||
|
.find { it.typeId == weightMeasurementTypeEntity.id }
|
||||||
|
} else { null }
|
||||||
|
|
||||||
|
if (weightValueObject?.floatValue == null || weightMeasurementTypeEntity == null) {
|
||||||
|
LogManager.w(TAG, "Weight data not found for measurement ${valueToConvert.measurementId}. Skipping PERCENT conversion for $typeKey (ID: ${valueToConvert.id}) value '${currentValue}'.")
|
||||||
|
continue // Skip conversion for this specific item
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updatedValuesBatch.isNotEmpty()) {
|
val totalWeightRaw = weightValueObject.floatValue
|
||||||
LogManager.d(TAG, "Updating ${updatedValuesBatch.size} values in batch for type ID ${originalType.id}.")
|
val totalWeightUnitGlobal = weightMeasurementTypeEntity.unit // Current global unit of WEIGHT type
|
||||||
updatedValuesBatch.forEach { repository.updateMeasurementValue(it) }
|
|
||||||
// Consider a repository.justUpdateMeasurementValueData(it)
|
// Case 1: From PERCENT to an absolute weight unit (KG, LB, ST)
|
||||||
LogManager.d(TAG, "Batch update of ${updatedValuesBatch.size} values completed for type ID ${originalType.id}.")
|
if (oldUnit == UnitType.PERCENT && newUnit.isWeightUnit()) {
|
||||||
|
val weightInKg = when(totalWeightUnitGlobal) {
|
||||||
|
UnitType.KG -> totalWeightRaw
|
||||||
|
UnitType.LB -> Converters.toKilogram(totalWeightRaw, WeightUnit.LB)
|
||||||
|
UnitType.ST -> Converters.toKilogram(totalWeightRaw, WeightUnit.ST)
|
||||||
|
else -> { LogManager.e(TAG, "Unsupported weight unit '$totalWeightUnitGlobal' for WEIGHT type."); null }
|
||||||
|
}
|
||||||
|
if (weightInKg != null) {
|
||||||
|
val absoluteValueInKg = (currentValue / 100.0f) * weightInKg
|
||||||
|
convertedValue = Converters.convertFloatValueUnit(absoluteValueInKg, UnitType.KG, newUnit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Case 2: From an absolute weight unit (KG, LB, ST) to PERCENT
|
||||||
|
else if (oldUnit.isWeightUnit() && newUnit == UnitType.PERCENT) {
|
||||||
|
val currentAbsoluteValueInKg = Converters.convertFloatValueUnit(currentValue, oldUnit, UnitType.KG)
|
||||||
|
val totalWeightInKg = when(totalWeightUnitGlobal) {
|
||||||
|
UnitType.KG -> totalWeightRaw
|
||||||
|
UnitType.LB -> Converters.toKilogram(totalWeightRaw, WeightUnit.LB)
|
||||||
|
UnitType.ST -> Converters.toKilogram(totalWeightRaw, WeightUnit.ST)
|
||||||
|
else -> { LogManager.e(TAG, "Unsupported weight unit '$totalWeightUnitGlobal' for WEIGHT type."); null }
|
||||||
|
}
|
||||||
|
if (currentAbsoluteValueInKg != null && totalWeightInKg != null && totalWeightInKg != 0.0f) {
|
||||||
|
convertedValue = (currentAbsoluteValueInKg / totalWeightInKg) * 100.0f
|
||||||
|
} else if (totalWeightInKg == 0.0f) {
|
||||||
|
LogManager.w(TAG, "Total weight is 0 for measurement ${valueToConvert.measurementId}. Cannot calculate $typeKey as PERCENT.")
|
||||||
|
convertedValue = 0.0f // Or null, or specific error handling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Case 3: Between two absolute weight units (e.g. KG <-> LB for BODY_FAT itself if it was stored as absolute)
|
||||||
|
else if (oldUnit.isWeightUnit() && newUnit.isWeightUnit()) {
|
||||||
|
convertedValue = Converters.convertFloatValueUnit(currentValue, oldUnit, newUnit)
|
||||||
|
}
|
||||||
|
// Fallback for unhandled specific conversions for these types
|
||||||
|
else {
|
||||||
|
LogManager.w(TAG, "Unsupported unit conversion for $typeKey (ID: ${valueToConvert.id}) from $oldUnit to $newUnit. Value not changed.")
|
||||||
|
convertedValue = currentValue // Keep original value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- Standard conversion for other types (e.g., WEIGHT itself, WAIST, HIPS) ---
|
||||||
|
else {
|
||||||
|
convertedValue = Converters.convertFloatValueUnit(currentValue, oldUnit, newUnit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If value changed, update it. Repository's updateMeasurementValue will trigger recalculations.
|
||||||
|
if (convertedValue != null && CalculationUtil.roundTo(convertedValue) != CalculationUtil.roundTo(currentValue)) {
|
||||||
|
try {
|
||||||
|
repository.updateMeasurementValue(valueToConvert.copy(floatValue = CalculationUtil.roundTo(convertedValue)))
|
||||||
|
valuesSuccessfullyConvertedCount++
|
||||||
|
} catch (e: Exception) {
|
||||||
|
LogManager.e(TAG, "Error updating MeasurementValue (ID: ${valueToConvert.id}) for $typeKey.", e)
|
||||||
|
// Individual update error; loop continues.
|
||||||
|
}
|
||||||
|
} else if (convertedValue == null && oldUnit != newUnit) {
|
||||||
|
// Log if conversion was expected but resulted in null (e.g., unsupported path or missing prerequisites)
|
||||||
|
LogManager.e(TAG, "Conversion for $typeKey (ID: ${valueToConvert.id}) from $oldUnit to $newUnit resulted in null or was skipped. Original value '$currentValue' not changed.")
|
||||||
|
}
|
||||||
|
} // End forEach valueToConvert
|
||||||
|
|
||||||
|
LogManager.d(TAG, "$valuesSuccessfullyConvertedCount values successfully converted/updated for $typeKey (ID ${originalType.id}). Recalculation automatically triggered by repository for updated items.")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
LogManager.i(TAG, "No values required actual conversion or update for type ID ${originalType.id} after checking.")
|
LogManager.i(TAG, "No values found for $typeKey (ID ${originalType.id}) to convert.")
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LogManager.i(TAG, "No values found for type ID ${originalType.id} to convert.")
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
LogManager.e(TAG, "Error during value conversion/update for type ID ${originalType.id}", e)
|
// Catch errors during the overall value fetching/processing logic (e.g., .first() on Flow fails)
|
||||||
conversionErrorOccurred = true
|
LogManager.e(TAG, "Error during value conversion process for $typeKey (ID ${originalType.id})", e)
|
||||||
sharedViewModel.showSnackbar(
|
|
||||||
messageResId = R.string.measurement_type_update_error_conversion_failed,
|
|
||||||
formatArgs = listOf(originalType.name ?: originalType.key.toString())
|
|
||||||
)
|
|
||||||
// Optional: Consider reverting the MeasurementType update if value conversion fails (adds complexity).
|
|
||||||
}
|
}
|
||||||
} else if (!conversionErrorOccurred && originalType.unit != updatedType.unit) {
|
} else if (!mainOperationErrorOccurred && oldUnit != newUnit) {
|
||||||
|
// This block is for when unit changed, but the main conversion block wasn't entered.
|
||||||
|
// e.g., inputType is not FLOAT (though this is unlikely for types with units that change often).
|
||||||
|
// Recalculation for derived values in this scenario relies on other triggers or manual user actions.
|
||||||
LogManager.i(
|
LogManager.i(
|
||||||
TAG,
|
TAG,
|
||||||
"Unit changed for type ID ${originalType.id}, but InputType is not FLOAT or previous type update failed. " +
|
"Unit changed for $typeKey (ID ${originalType.id}), but no direct value conversion was performed in the main block. " +
|
||||||
"No direct value conversion, but affected measurements will be flagged for recalculation."
|
"Any necessary derived value recalculations depend on updates to their specific input values."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Show appropriate snackbar message.
|
// 3. Show appropriate snackbar message.
|
||||||
if (!conversionErrorOccurred && showSnackbarMaster) {
|
if (!mainOperationErrorOccurred && showSnackbarMaster) {
|
||||||
if (valuesConvertedCount > 0) {
|
if (conversionProcessAttempted) { // If conversion was relevant and attempted
|
||||||
|
if (valuesSuccessfullyConvertedCount > 0) {
|
||||||
sharedViewModel.showSnackbar(
|
sharedViewModel.showSnackbar(
|
||||||
messageResId = R.string.measurement_type_updated_and_values_converted_successfully,
|
messageResId = R.string.measurement_type_updated_and_values_converted_successfully,
|
||||||
formatArgs = listOf(
|
formatArgs = listOf(
|
||||||
updatedType.name ?: updatedType.key.toString(),
|
finalTypeToUpdate.name ?: finalTypeToUpdate.key.toString(),
|
||||||
valuesConvertedCount.toString()
|
valuesSuccessfullyConvertedCount.toString()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else if (originalType.unit != updatedType.unit && (originalType.inputType == InputFieldType.FLOAT)) {
|
} else { // Conversion attempted, but no values were actually changed/updated (e.g., all were same after conversion)
|
||||||
// Also show if unit changed but no values were converted (e.g., none existed or input type wasn't FLOAT but recalculation was still triggered)
|
|
||||||
sharedViewModel.showSnackbar(
|
sharedViewModel.showSnackbar(
|
||||||
messageResId = R.string.measurement_type_updated_unit_changed_no_values_converted, // Or a more specific message
|
messageResId = R.string.measurement_type_updated_unit_changed_no_values_converted,
|
||||||
formatArgs = listOf(updatedType.name ?: updatedType.key.toString())
|
formatArgs = listOf(finalTypeToUpdate.name ?: finalTypeToUpdate.key.toString())
|
||||||
)
|
)
|
||||||
} else { // Generic success if no unit change or no conversion needed
|
}
|
||||||
|
} else if (oldUnit == newUnit && !mainOperationErrorOccurred) {
|
||||||
|
// Case: Type updated (e.g. name, color changed) but unit was the same.
|
||||||
|
// Or unit changed but mainOperationErrorOccurred previously (though this snackbar won't show then).
|
||||||
sharedViewModel.showSnackbar(
|
sharedViewModel.showSnackbar(
|
||||||
messageResId = R.string.measurement_type_updated_successfully,
|
messageResId = R.string.measurement_type_updated_successfully, // Generic success for type definition update
|
||||||
formatArgs = listOf(updatedType.name ?: updatedType.key.toString())
|
formatArgs = listOf(finalTypeToUpdate.name ?: finalTypeToUpdate.key.toString())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user