From c191394ee61a9ba7fac8a5b85b0a1870baf5ceb3 Mon Sep 17 00:00:00 2001 From: mac Date: Tue, 24 Feb 2026 12:46:27 -0500 Subject: [PATCH] test --- .../java/com/example/scanwich/SetupScreen.kt | 378 +++++++++--------- release-notes.txt | 3 + 2 files changed, 193 insertions(+), 188 deletions(-) diff --git a/app/src/main/java/com/example/scanwich/SetupScreen.kt b/app/src/main/java/com/example/scanwich/SetupScreen.kt index a49cc7a..a88d509 100644 --- a/app/src/main/java/com/example/scanwich/SetupScreen.kt +++ b/app/src/main/java/com/example/scanwich/SetupScreen.kt @@ -21,200 +21,202 @@ import com.google.firebase.firestore.FirebaseFirestore @Composable fun SetupScreen(prefs: SharedPreferences, onComplete: () -> Unit) { - var age by remember { mutableStateOf(prefs.getInt("age", 25).toString()) } - var heightCm by remember { mutableStateOf(prefs.getString("height_cm", "170") ?: "170") } - var weight by remember { mutableStateOf(prefs.getString("weight_display", "70") ?: "70") } - var isLbs by remember { mutableStateOf(prefs.getBoolean("is_lbs", false)) } - var gender by remember { mutableStateOf(prefs.getString("gender", "Homme") ?: "Homme") } - var activityLevel by remember { mutableStateOf(prefs.getString("activity_level", "Sédentaire") ?: "Sédentaire") } - var goal by remember { mutableStateOf(prefs.getString("goal", "Maintenir le poids") ?: "Maintenir le poids") } - var isDiabetic by remember { mutableStateOf(prefs.getBoolean("is_diabetic", false)) } + Surface(modifier = Modifier.fillMaxSize()) { + var age by remember { mutableStateOf(prefs.getInt("age", 25).toString()) } + var heightCm by remember { mutableStateOf(prefs.getString("height_cm", "170") ?: "170") } + var weight by remember { mutableStateOf(prefs.getString("weight_display", "70") ?: "70") } + var isLbs by remember { mutableStateOf(prefs.getBoolean("is_lbs", false)) } + var gender by remember { mutableStateOf(prefs.getString("gender", "Homme") ?: "Homme") } + var activityLevel by remember { mutableStateOf(prefs.getString("activity_level", "Sédentaire") ?: "Sédentaire") } + var goal by remember { mutableStateOf(prefs.getString("goal", "Maintenir le poids") ?: "Maintenir le poids") } + var isDiabetic by remember { mutableStateOf(prefs.getBoolean("is_diabetic", false)) } - val context = LocalContext.current - val activityLevels = listOf("Sédentaire", "Légèrement actif", "Modérément actif", "Très actif", "Extrêmement actif") - val goals = listOf("Maintenir le poids", "Perdre du poids") + val context = LocalContext.current + val activityLevels = listOf("Sédentaire", "Légèrement actif", "Modérément actif", "Très actif", "Extrêmement actif") + val goals = listOf("Maintenir le poids", "Perdre du poids") - val activityMultipliers = mapOf( - "Sédentaire" to 1.2, - "Légèrement actif" to 1.375, - "Modérément actif" to 1.55, - "Très actif" to 1.725, - "Extrêmement actif" to 1.9 - ) - - Column( - modifier = Modifier - .fillMaxSize() - .padding(24.dp) - .verticalScroll(rememberScrollState()), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text("Configuration du profil", style = MaterialTheme.typography.headlineLarge) - Spacer(Modifier.height(24.dp)) - - Text("Votre objectif :", style = MaterialTheme.typography.titleMedium, modifier = Modifier.align(Alignment.Start)) - goals.forEach { g -> - Row( - Modifier - .fillMaxWidth() - .clickable { goal = g } - .padding(vertical = 4.dp), - verticalAlignment = Alignment.CenterVertically - ) { - RadioButton(selected = goal == g, onClick = { goal = g }) - Text(g) - } - } - - Spacer(Modifier.height(16.dp)) - - Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { - Text("Genre : ", Modifier.width(80.dp)) - RadioButton(selected = gender == "Homme", onClick = { gender = "Homme" }) - Text("Homme") - Spacer(Modifier.width(16.dp)) - RadioButton(selected = gender == "Femme", onClick = { gender = "Femme" }) - Text("Femme") - } - - Spacer(Modifier.height(16.dp)) - - OutlinedTextField( - value = age, - onValueChange = { age = it }, - label = { Text("Âge") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - modifier = Modifier.fillMaxWidth() + val activityMultipliers = mapOf( + "Sédentaire" to 1.2, + "Légèrement actif" to 1.375, + "Modérément actif" to 1.55, + "Très actif" to 1.725, + "Extrêmement actif" to 1.9 ) - Spacer(Modifier.height(16.dp)) - - OutlinedTextField( - value = heightCm, - onValueChange = { heightCm = it }, - label = { Text("Taille (cm)") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - modifier = Modifier.fillMaxWidth() - ) - - Spacer(Modifier.height(16.dp)) - - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { - OutlinedTextField( - value = weight, - onValueChange = { weight = it }, - label = { Text(if (isLbs) "Poids (lbs)" else "Poids (kg)") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - modifier = Modifier.weight(1f) - ) - Spacer(Modifier.width(16.dp)) - Switch(checked = isLbs, onCheckedChange = { isLbs = it }) - Text(if (isLbs) "lbs" else "kg") - } - - Spacer(Modifier.height(16.dp)) - - Text("Niveau d'activité :", style = MaterialTheme.typography.titleMedium, modifier = Modifier.align(Alignment.Start)) - activityLevels.forEach { level -> - Row( - Modifier - .fillMaxWidth() - .clickable { activityLevel = level } - .padding(vertical = 4.dp), - verticalAlignment = Alignment.CenterVertically - ) { - RadioButton(selected = activityLevel == level, onClick = { activityLevel = level }) - Text(level) - } - } - - Spacer(Modifier.height(16.dp)) - - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { - Checkbox(checked = isDiabetic, onCheckedChange = { isDiabetic = it }) - Text("Je suis diabétique") - } - - Spacer(Modifier.height(32.dp)) - - Button( - onClick = { - val currentUser = FirebaseAuth.getInstance().currentUser - if (currentUser == null) { - Toast.makeText(context, "Erreur : Vous devez être connecté pour sauvegarder.", Toast.LENGTH_LONG).show() - return@Button - } - - val ageInt = age.toIntOrNull() ?: 0 - val height = heightCm.toDoubleOrNull() ?: 0.0 - var weightKg = weight.toDoubleOrNull() ?: 0.0 - val weightDisplay = weight - if (isLbs) weightKg *= 0.453592 - - val bmr = if (gender == "Homme") { - (10 * weightKg) + (6.25 * height) - (5 * ageInt) + 5 - } else { - (10 * weightKg) + (6.25 * height) - (5 * ageInt) - 161 - } - - val multiplier = activityMultipliers[activityLevel] ?: 1.2 - var targetCals = (bmr * multiplier).toInt() - - if (goal == "Perdre du poids") targetCals -= 500 - - val targetCarbs = (targetCals * 0.5 / 4).toInt() - val targetProtein = (targetCals * 0.2 / 4).toInt() - val targetFat = (targetCals * 0.3 / 9).toInt() - - val userProfile = hashMapOf( - "age" to ageInt, - "height_cm" to height, - "weight_kg" to weightKg, - "is_lbs" to isLbs, - "gender" to gender, - "activity_level" to activityLevel, - "goal" to goal, - "is_diabetic" to isDiabetic, - "target_calories" to targetCals, - "target_carbs" to targetCarbs, - "target_protein" to targetProtein, - "target_fat" to targetFat - ) - - // On spécifie explicitement la base de données "scan-wich" - FirebaseFirestore.getInstance("scan-wich").collection("users").document(currentUser.uid) - .set(userProfile) - .addOnSuccessListener { - Log.d("SetupScreen", "User profile saved to Firestore.") - prefs.edit { - putString("target_calories", targetCals.toString()) - putString("target_carbs", targetCarbs.toString()) - putString("target_protein", targetProtein.toString()) - putString("target_fat", targetFat.toString()) - putString("weight_kg", weightKg.toString()) - putString("weight_display", weightDisplay) - putBoolean("is_lbs", isLbs) - putString("height_cm", heightCm) - putBoolean("is_diabetic", isDiabetic) - putInt("age", ageInt) - putString("gender", gender) - putString("activity_level", activityLevel) - putString("goal", goal) - } - Toast.makeText(context, "Profil sauvegardé sur votre compte !", Toast.LENGTH_SHORT).show() - onComplete() - } - .addOnFailureListener { e -> - Log.w("SetupScreen", "Error writing user profile to Firestore", e) - Toast.makeText(context, "Erreur de sauvegarde du profil: ${e.message}", Toast.LENGTH_LONG).show() - } - }, + Column( modifier = Modifier - .fillMaxWidth() - .height(56.dp), - enabled = age.isNotBlank() && heightCm.isNotBlank() && weight.isNotBlank() + .fillMaxSize() + .padding(24.dp) + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally ) { - Text("Sauvegarder le profil") + Text("Configuration du profil", style = MaterialTheme.typography.headlineLarge) + Spacer(Modifier.height(24.dp)) + + Text("Votre objectif :", style = MaterialTheme.typography.titleMedium, modifier = Modifier.align(Alignment.Start)) + goals.forEach { g -> + Row( + Modifier + .fillMaxWidth() + .clickable { goal = g } + .padding(vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + RadioButton(selected = goal == g, onClick = { goal = g }) + Text(g) + } + } + + Spacer(Modifier.height(16.dp)) + + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + Text("Genre : ", Modifier.width(80.dp)) + RadioButton(selected = gender == "Homme", onClick = { gender = "Homme" }) + Text("Homme") + Spacer(Modifier.width(16.dp)) + RadioButton(selected = gender == "Femme", onClick = { gender = "Femme" }) + Text("Femme") + } + + Spacer(Modifier.height(16.dp)) + + OutlinedTextField( + value = age, + onValueChange = { age = it }, + label = { Text("Âge") }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + modifier = Modifier.fillMaxWidth() + ) + + Spacer(Modifier.height(16.dp)) + + OutlinedTextField( + value = heightCm, + onValueChange = { heightCm = it }, + label = { Text("Taille (cm)") }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + modifier = Modifier.fillMaxWidth() + ) + + Spacer(Modifier.height(16.dp)) + + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + OutlinedTextField( + value = weight, + onValueChange = { weight = it }, + label = { Text(if (isLbs) "Poids (lbs)" else "Poids (kg)") }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + modifier = Modifier.weight(1f) + ) + Spacer(Modifier.width(16.dp)) + Switch(checked = isLbs, onCheckedChange = { isLbs = it }) + Text(if (isLbs) "lbs" else "kg") + } + + Spacer(Modifier.height(16.dp)) + + Text("Niveau d'activité :", style = MaterialTheme.typography.titleMedium, modifier = Modifier.align(Alignment.Start)) + activityLevels.forEach { level -> + Row( + Modifier + .fillMaxWidth() + .clickable { activityLevel = level } + .padding(vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + RadioButton(selected = activityLevel == level, onClick = { activityLevel = level }) + Text(level) + } + } + + Spacer(Modifier.height(16.dp)) + + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + Checkbox(checked = isDiabetic, onCheckedChange = { isDiabetic = it }) + Text("Je suis diabétique") + } + + Spacer(Modifier.height(32.dp)) + + Button( + onClick = { + val currentUser = FirebaseAuth.getInstance().currentUser + if (currentUser == null) { + Toast.makeText(context, "Erreur : Vous devez être connecté pour sauvegarder.", Toast.LENGTH_LONG).show() + return@Button + } + + val ageInt = age.toIntOrNull() ?: 0 + val height = heightCm.toDoubleOrNull() ?: 0.0 + var weightKg = weight.toDoubleOrNull() ?: 0.0 + val weightDisplay = weight + if (isLbs) weightKg *= 0.453592 + + val bmr = if (gender == "Homme") { + (10 * weightKg) + (6.25 * height) - (5 * ageInt) + 5 + } else { + (10 * weightKg) + (6.25 * height) - (5 * ageInt) - 161 + } + + val multiplier = activityMultipliers[activityLevel] ?: 1.2 + var targetCals = (bmr * multiplier).toInt() + + if (goal == "Perdre du poids") targetCals -= 500 + + val targetCarbs = (targetCals * 0.5 / 4).toInt() + val targetProtein = (targetCals * 0.2 / 4).toInt() + val targetFat = (targetCals * 0.3 / 9).toInt() + + val userProfile = hashMapOf( + "age" to ageInt, + "height_cm" to height, + "weight_kg" to weightKg, + "is_lbs" to isLbs, + "gender" to gender, + "activity_level" to activityLevel, + "goal" to goal, + "is_diabetic" to isDiabetic, + "target_calories" to targetCals, + "target_carbs" to targetCarbs, + "target_protein" to targetProtein, + "target_fat" to targetFat + ) + + // On spécifie explicitement la base de données "scan-wich" + FirebaseFirestore.getInstance("scan-wich").collection("users").document(currentUser.uid) + .set(userProfile) + .addOnSuccessListener { + Log.d("SetupScreen", "User profile saved to Firestore.") + prefs.edit { + putString("target_calories", targetCals.toString()) + putString("target_carbs", targetCarbs.toString()) + putString("target_protein", targetProtein.toString()) + putString("target_fat", targetFat.toString()) + putString("weight_kg", weightKg.toString()) + putString("weight_display", weightDisplay) + putBoolean("is_lbs", isLbs) + putString("height_cm", heightCm) + putBoolean("is_diabetic", isDiabetic) + putInt("age", ageInt) + putString("gender", gender) + putString("activity_level", activityLevel) + putString("goal", goal) + } + Toast.makeText(context, "Profil sauvegardé sur votre compte !", Toast.LENGTH_SHORT).show() + onComplete() + } + .addOnFailureListener { e -> + Log.w("SetupScreen", "Error writing user profile to Firestore", e) + Toast.makeText(context, "Erreur de sauvegarde du profil: ${e.message}", Toast.LENGTH_LONG).show() + } + }, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + enabled = age.isNotBlank() && heightCm.isNotBlank() && weight.isNotBlank() + ) { + Text("Sauvegarder le profil") + } } } } diff --git a/release-notes.txt b/release-notes.txt index d2dd64d..811eaa8 100644 --- a/release-notes.txt +++ b/release-notes.txt @@ -2,6 +2,9 @@ **Changements majeurs de la version actuelle :** +🎨 **Améliorations de l'Interface Utilisateur :** +- **Correction du Thème :** Résolution d'un problème de contraste sur l'écran de configuration du profil qui rendait le texte illisible (texte jaune sur fond blanc). L'écran respecte désormais correctement le thème de l'application. + 🛡️ **Refonte de l'Architecture de Sécurité et de Données :** - **Gestion des accès centralisée :** L'ancien système d'utilisateurs autorisés codé en dur dans l'application a été supprimé. L'accès est désormais contrôlé de manière sécurisée et dynamique via une liste d'autorisation sur le serveur Firebase Firestore. Cela ouvre la voie à la gestion d'abonnements. - **Profils Utilisateurs dans le Cloud :** Les données de profil (poids, objectifs, etc.) sont maintenant synchronisées avec le compte Firebase de l'utilisateur, permettant une expérience cohérente sur plusieurs appareils.