mirror of
https://github.com/oliexdev/openScale.git
synced 2025-08-16 13:44:26 +02:00
Refactor measurement type to improves the logic for setting the initial and updating selectedInputType
based on allowed types.
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
package com.health.openscale.core.data
|
||||
|
||||
import android.text.InputType
|
||||
import androidx.annotation.StringRes
|
||||
import com.health.openscale.R
|
||||
import java.util.Locale
|
||||
@@ -145,35 +146,36 @@ enum class MeasureUnit {
|
||||
enum class MeasurementTypeKey(
|
||||
val id: Int,
|
||||
@StringRes val localizedNameResId: Int,
|
||||
val allowedUnitTypes: List<UnitType>
|
||||
val allowedUnitTypes: List<UnitType>,
|
||||
val allowedInputType: List<InputFieldType>
|
||||
) {
|
||||
WEIGHT(1, R.string.measurement_type_weight, listOf(UnitType.KG, UnitType.LB, UnitType.ST)),
|
||||
BMI(2, R.string.measurement_type_bmi, listOf(UnitType.NONE)),
|
||||
BODY_FAT(3, R.string.measurement_type_body_fat, listOf(UnitType.PERCENT)),
|
||||
WATER(4, R.string.measurement_type_water, listOf(UnitType.PERCENT)),
|
||||
MUSCLE(5, R.string.measurement_type_muscle, listOf(UnitType.PERCENT, UnitType.KG, UnitType.LB)),
|
||||
LBM(6, R.string.measurement_type_lbm, listOf(UnitType.KG, UnitType.LB, UnitType.ST)),
|
||||
BONE(7, R.string.measurement_type_bone, listOf(UnitType.KG, UnitType.LB)),
|
||||
WAIST(8, R.string.measurement_type_waist, listOf(UnitType.CM, UnitType.INCH)),
|
||||
WHR(9, R.string.measurement_type_whr, listOf(UnitType.NONE)),
|
||||
WHTR(10, R.string.measurement_type_whtr, listOf(UnitType.NONE)),
|
||||
HIPS(11, R.string.measurement_type_hips, listOf(UnitType.CM, UnitType.INCH)),
|
||||
VISCERAL_FAT(12, R.string.measurement_type_visceral_fat, listOf(UnitType.PERCENT, UnitType.NONE)),
|
||||
CHEST(13, R.string.measurement_type_chest, listOf(UnitType.CM, UnitType.INCH)),
|
||||
THIGH(14, R.string.measurement_type_thigh, listOf(UnitType.CM, UnitType.INCH)),
|
||||
BICEPS(15, R.string.measurement_type_biceps, listOf(UnitType.CM, UnitType.INCH)),
|
||||
NECK(16, R.string.measurement_type_neck, listOf(UnitType.CM, UnitType.INCH)),
|
||||
CALIPER_1(17, R.string.measurement_type_caliper1, listOf(UnitType.CM, UnitType.INCH)),
|
||||
CALIPER_2(18, R.string.measurement_type_caliper2, listOf(UnitType.CM, UnitType.INCH)),
|
||||
CALIPER_3(19, R.string.measurement_type_caliper3, listOf(UnitType.CM, UnitType.INCH)),
|
||||
CALIPER(20, R.string.measurement_type_fat_caliper, listOf(UnitType.PERCENT, UnitType.NONE)),
|
||||
BMR(21, R.string.measurement_type_bmr, listOf(UnitType.KCAL)),
|
||||
TDEE(22, R.string.measurement_type_tdee, listOf(UnitType.KCAL)),
|
||||
CALORIES(23, R.string.measurement_type_calories, listOf(UnitType.KCAL)),
|
||||
DATE(24, R.string.measurement_type_date, listOf(UnitType.NONE)),
|
||||
TIME(25, R.string.measurement_type_time, listOf(UnitType.NONE)),
|
||||
COMMENT(26, R.string.measurement_type_comment, listOf(UnitType.NONE)),
|
||||
CUSTOM(99, R.string.measurement_type_custom_default_name, UnitType.entries.toList());
|
||||
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)),
|
||||
BODY_FAT(3, R.string.measurement_type_body_fat, listOf(UnitType.PERCENT), listOf(InputFieldType.FLOAT)),
|
||||
WATER(4, R.string.measurement_type_water, listOf(UnitType.PERCENT), listOf(InputFieldType.FLOAT)),
|
||||
MUSCLE(5, R.string.measurement_type_muscle, listOf(UnitType.PERCENT, UnitType.KG, UnitType.LB), 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)),
|
||||
WAIST(8, R.string.measurement_type_waist, listOf(UnitType.CM, UnitType.INCH), listOf(InputFieldType.FLOAT)),
|
||||
WHR(9, R.string.measurement_type_whr, listOf(UnitType.NONE), listOf(InputFieldType.FLOAT)),
|
||||
WHTR(10, R.string.measurement_type_whtr, listOf(UnitType.NONE), listOf(InputFieldType.FLOAT)),
|
||||
HIPS(11, R.string.measurement_type_hips, listOf(UnitType.CM, UnitType.INCH), listOf(InputFieldType.FLOAT)),
|
||||
VISCERAL_FAT(12, R.string.measurement_type_visceral_fat, listOf(UnitType.PERCENT, UnitType.NONE), listOf(InputFieldType.INT, InputFieldType.FLOAT)),
|
||||
CHEST(13, R.string.measurement_type_chest, listOf(UnitType.CM, UnitType.INCH), listOf(InputFieldType.FLOAT)),
|
||||
THIGH(14, R.string.measurement_type_thigh, listOf(UnitType.CM, UnitType.INCH), listOf(InputFieldType.FLOAT)),
|
||||
BICEPS(15, R.string.measurement_type_biceps, listOf(UnitType.CM, UnitType.INCH), listOf(InputFieldType.FLOAT)),
|
||||
NECK(16, R.string.measurement_type_neck, listOf(UnitType.CM, UnitType.INCH), listOf(InputFieldType.FLOAT)),
|
||||
CALIPER_1(17, R.string.measurement_type_caliper1, listOf(UnitType.CM, UnitType.INCH), listOf(InputFieldType.FLOAT)),
|
||||
CALIPER_2(18, R.string.measurement_type_caliper2, listOf(UnitType.CM, UnitType.INCH), listOf(InputFieldType.FLOAT)),
|
||||
CALIPER_3(19, R.string.measurement_type_caliper3, listOf(UnitType.CM, UnitType.INCH), listOf(InputFieldType.FLOAT)),
|
||||
CALIPER(20, R.string.measurement_type_fat_caliper, listOf(UnitType.PERCENT, UnitType.NONE), listOf(InputFieldType.FLOAT)),
|
||||
BMR(21, R.string.measurement_type_bmr, listOf(UnitType.KCAL), listOf(InputFieldType.INT)),
|
||||
TDEE(22, R.string.measurement_type_tdee, listOf(UnitType.KCAL), listOf(InputFieldType.INT)),
|
||||
CALORIES(23, R.string.measurement_type_calories, listOf(UnitType.KCAL), listOf(InputFieldType.INT)),
|
||||
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)),
|
||||
COMMENT(26, R.string.measurement_type_comment, listOf(UnitType.NONE), listOf(InputFieldType.TEXT)),
|
||||
CUSTOM(99, R.string.measurement_type_custom_default_name, UnitType.entries.toList(), listOf(InputFieldType.FLOAT, InputFieldType.INT, InputFieldType.TEXT, InputFieldType.DATE, InputFieldType.TIME));
|
||||
}
|
||||
|
||||
|
||||
|
@@ -111,6 +111,10 @@ fun MeasurementTypeDetailScreen(
|
||||
currentMeasurementTypeKey.allowedUnitTypes
|
||||
}
|
||||
|
||||
val allowedInputTypesForKey = remember(currentMeasurementTypeKey) {
|
||||
currentMeasurementTypeKey.allowedInputType
|
||||
}
|
||||
|
||||
var name by remember { mutableStateOf(originalExistingType?.getDisplayName(context).orEmpty()) }
|
||||
|
||||
// Safely set selectedUnit. If the existing unit isn't allowed or if no existing unit,
|
||||
@@ -124,7 +128,14 @@ fun MeasurementTypeDetailScreen(
|
||||
}
|
||||
}
|
||||
|
||||
var selectedInputType by remember { mutableStateOf(originalExistingType?.inputType ?: InputFieldType.FLOAT) }
|
||||
var selectedInputType by remember {
|
||||
val initialInputType = originalExistingType?.inputType
|
||||
if (initialInputType != null && initialInputType in allowedInputTypesForKey) {
|
||||
mutableStateOf(initialInputType)
|
||||
} else {
|
||||
mutableStateOf(allowedInputTypesForKey.firstOrNull() ?: InputFieldType.FLOAT)
|
||||
}
|
||||
}
|
||||
var selectedColor by remember { mutableStateOf(originalExistingType?.color ?: 0xFF6200EE.toInt()) }
|
||||
var selectedIcon by remember { mutableStateOf(originalExistingType?.icon ?: "ic_weight") }
|
||||
var isEnabled by remember { mutableStateOf(originalExistingType?.isEnabled ?: true) }
|
||||
@@ -142,13 +153,13 @@ fun MeasurementTypeDetailScreen(
|
||||
val titleEdit = stringResource(R.string.measurement_type_detail_title_edit)
|
||||
val titleAdd = stringResource(R.string.measurement_type_detail_title_add)
|
||||
|
||||
// Determines if the unit dropdown should be enabled (i.e., if there's more than one allowed unit).
|
||||
val unitDropdownEnabled by remember(allowedUnitsForKey) {
|
||||
derivedStateOf { allowedUnitsForKey.size > 1 }
|
||||
}
|
||||
val inputTypeDropdownEnabled by remember(allowedInputTypesForKey) {
|
||||
derivedStateOf { allowedInputTypesForKey.size > 1 }
|
||||
}
|
||||
|
||||
// Effect to re-evaluate and set selectedUnit if originalExistingType or allowedUnitsForKey change.
|
||||
// This ensures selectedUnit is always valid.
|
||||
LaunchedEffect(originalExistingType, allowedUnitsForKey) {
|
||||
val currentUnitInExistingType = originalExistingType?.unit
|
||||
if (currentUnitInExistingType != null && currentUnitInExistingType in allowedUnitsForKey) {
|
||||
@@ -163,6 +174,19 @@ fun MeasurementTypeDetailScreen(
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(originalExistingType, allowedInputTypesForKey) {
|
||||
val currentInputTypeInExistingType = originalExistingType?.inputType
|
||||
if (currentInputTypeInExistingType != null && currentInputTypeInExistingType in allowedInputTypesForKey) {
|
||||
if (selectedInputType != currentInputTypeInExistingType) {
|
||||
selectedInputType = currentInputTypeInExistingType
|
||||
}
|
||||
} else if (allowedInputTypesForKey.isNotEmpty() && selectedInputType !in allowedInputTypesForKey) {
|
||||
selectedInputType = allowedInputTypesForKey.first()
|
||||
} else if (allowedInputTypesForKey.isEmpty()) {
|
||||
selectedInputType = InputFieldType.FLOAT
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.setTopBarTitle(if (isEdit) titleEdit else titleAdd)
|
||||
sharedViewModel.setTopBarAction(
|
||||
@@ -252,15 +276,14 @@ fun MeasurementTypeDetailScreen(
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedTextField(
|
||||
value = name,
|
||||
onValueChange = { name = it },
|
||||
label = { Text(stringResource(R.string.measurement_type_label_name)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
// Name field is editable for new types or existing CUSTOM types.
|
||||
// For predefined types, the name is typically not user-editable.
|
||||
enabled = !isEdit || (originalExistingType?.key == MeasurementTypeKey.CUSTOM)
|
||||
)
|
||||
if (!isEdit || (originalExistingType?.key == MeasurementTypeKey.CUSTOM)) {
|
||||
OutlinedTextField(
|
||||
value = name,
|
||||
onValueChange = { name = it },
|
||||
label = { Text(stringResource(R.string.measurement_type_label_name)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedTextField(
|
||||
value = String.format("#%06X", 0xFFFFFF and selectedColor),
|
||||
@@ -376,32 +399,37 @@ fun MeasurementTypeDetailScreen(
|
||||
}
|
||||
|
||||
// InputFieldType Dropdown
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expandedInputType,
|
||||
onExpandedChange = { expandedInputType = !expandedInputType }
|
||||
) {
|
||||
OutlinedTextField(
|
||||
readOnly = true,
|
||||
value = selectedInputType.name.lowercase().replaceFirstChar { it.uppercase() },
|
||||
onValueChange = {},
|
||||
label = { Text(stringResource(R.string.measurement_type_label_input_type)) },
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expandedInputType) },
|
||||
modifier = Modifier
|
||||
.menuAnchor(type = MenuAnchorType.PrimaryNotEditable, enabled = true)
|
||||
.fillMaxWidth()
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
if (inputTypeDropdownEnabled) {
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expandedInputType,
|
||||
onDismissRequest = { expandedInputType = false }
|
||||
onExpandedChange = { expandedInputType = !expandedInputType }
|
||||
) {
|
||||
InputFieldType.entries.forEach { type ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(type.name.lowercase().replaceFirstChar { it.uppercase() }) },
|
||||
onClick = {
|
||||
selectedInputType = type
|
||||
expandedInputType = false
|
||||
}
|
||||
)
|
||||
OutlinedTextField(
|
||||
readOnly = true,
|
||||
value = selectedInputType.name.lowercase().replaceFirstChar { it.uppercase() },
|
||||
onValueChange = {},
|
||||
label = { Text(stringResource(R.string.measurement_type_label_input_type)) },
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expandedInputType) },
|
||||
modifier = Modifier
|
||||
.menuAnchor(type = MenuAnchorType.PrimaryNotEditable, enabled = true)
|
||||
.fillMaxWidth()
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
expanded = expandedInputType,
|
||||
onDismissRequest = { expandedInputType = false }
|
||||
) {
|
||||
allowedInputTypesForKey.forEach { type ->
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
type.name.lowercase().replaceFirstChar { it.uppercase() })
|
||||
},
|
||||
onClick = {
|
||||
selectedInputType = type
|
||||
expandedInputType = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -251,7 +251,6 @@ fun UserDetailScreen(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Text(stringResource(id = R.string.user_detail_label_height))
|
||||
OutlinedTextField(
|
||||
value = heightValueString,
|
||||
onValueChange = { newValue ->
|
||||
@@ -302,9 +301,6 @@ fun UserDetailScreen(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
Text(stringResource(id = R.string.user_detail_label_gender)) // "Gender"
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
GenderType.entries.forEach { option ->
|
||||
Row(
|
||||
@@ -358,9 +354,9 @@ fun UserDetailScreen(
|
||||
}
|
||||
}
|
||||
|
||||
Text(stringResource(id = R.string.user_detail_label_birth_date)) // "Birth Date"
|
||||
OutlinedTextField(
|
||||
value = dateFormatter.format(Date(birthDate)),
|
||||
label = { Text(stringResource(R.string.user_detail_label_birth_date)) },
|
||||
onValueChange = {}, // Input is read-only, selection via DatePicker
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
Reference in New Issue
Block a user