test
This commit is contained in:
@@ -21,200 +21,202 @@ import com.google.firebase.firestore.FirebaseFirestore
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SetupScreen(prefs: SharedPreferences, onComplete: () -> Unit) {
|
fun SetupScreen(prefs: SharedPreferences, onComplete: () -> Unit) {
|
||||||
var age by remember { mutableStateOf(prefs.getInt("age", 25).toString()) }
|
Surface(modifier = Modifier.fillMaxSize()) {
|
||||||
var heightCm by remember { mutableStateOf(prefs.getString("height_cm", "170") ?: "170") }
|
var age by remember { mutableStateOf(prefs.getInt("age", 25).toString()) }
|
||||||
var weight by remember { mutableStateOf(prefs.getString("weight_display", "70") ?: "70") }
|
var heightCm by remember { mutableStateOf(prefs.getString("height_cm", "170") ?: "170") }
|
||||||
var isLbs by remember { mutableStateOf(prefs.getBoolean("is_lbs", false)) }
|
var weight by remember { mutableStateOf(prefs.getString("weight_display", "70") ?: "70") }
|
||||||
var gender by remember { mutableStateOf(prefs.getString("gender", "Homme") ?: "Homme") }
|
var isLbs by remember { mutableStateOf(prefs.getBoolean("is_lbs", false)) }
|
||||||
var activityLevel by remember { mutableStateOf(prefs.getString("activity_level", "Sédentaire") ?: "Sédentaire") }
|
var gender by remember { mutableStateOf(prefs.getString("gender", "Homme") ?: "Homme") }
|
||||||
var goal by remember { mutableStateOf(prefs.getString("goal", "Maintenir le poids") ?: "Maintenir le poids") }
|
var activityLevel by remember { mutableStateOf(prefs.getString("activity_level", "Sédentaire") ?: "Sédentaire") }
|
||||||
var isDiabetic by remember { mutableStateOf(prefs.getBoolean("is_diabetic", false)) }
|
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 context = LocalContext.current
|
||||||
val activityLevels = listOf("Sédentaire", "Légèrement actif", "Modérément actif", "Très actif", "Extrêmement actif")
|
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 goals = listOf("Maintenir le poids", "Perdre du poids")
|
||||||
|
|
||||||
val activityMultipliers = mapOf(
|
val activityMultipliers = mapOf(
|
||||||
"Sédentaire" to 1.2,
|
"Sédentaire" to 1.2,
|
||||||
"Légèrement actif" to 1.375,
|
"Légèrement actif" to 1.375,
|
||||||
"Modérément actif" to 1.55,
|
"Modérément actif" to 1.55,
|
||||||
"Très actif" to 1.725,
|
"Très actif" to 1.725,
|
||||||
"Extrêmement actif" to 1.9
|
"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()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.height(16.dp))
|
Column(
|
||||||
|
|
||||||
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
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxSize()
|
||||||
.height(56.dp),
|
.padding(24.dp)
|
||||||
enabled = age.isNotBlank() && heightCm.isNotBlank() && weight.isNotBlank()
|
.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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
**Changements majeurs de la version actuelle :**
|
**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 :**
|
🛡️ **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.
|
- **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.
|
- **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.
|
||||||
|
|||||||
Reference in New Issue
Block a user