diff --git a/android_app/app/src/main/java/com/health/openscale/core/data/Enums.kt b/android_app/app/src/main/java/com/health/openscale/core/data/Enums.kt index 18cb9604..4212d8cb 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/data/Enums.kt +++ b/android_app/app/src/main/java/com/health/openscale/core/data/Enums.kt @@ -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 + val allowedUnitTypes: List, + val allowedInputType: List ) { - 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)); } diff --git a/android_app/app/src/main/java/com/health/openscale/ui/screen/settings/MeasurementTypeDetailScreen.kt b/android_app/app/src/main/java/com/health/openscale/ui/screen/settings/MeasurementTypeDetailScreen.kt index e2284033..f03c0089 100644 --- a/android_app/app/src/main/java/com/health/openscale/ui/screen/settings/MeasurementTypeDetailScreen.kt +++ b/android_app/app/src/main/java/com/health/openscale/ui/screen/settings/MeasurementTypeDetailScreen.kt @@ -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 + } + ) + } } } } diff --git a/android_app/app/src/main/java/com/health/openscale/ui/screen/settings/UserDetailScreen.kt b/android_app/app/src/main/java/com/health/openscale/ui/screen/settings/UserDetailScreen.kt index 524bb5fb..4d7bc157 100644 --- a/android_app/app/src/main/java/com/health/openscale/ui/screen/settings/UserDetailScreen.kt +++ b/android_app/app/src/main/java/com/health/openscale/ui/screen/settings/UserDetailScreen.kt @@ -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()