changes #6

Merged
macharest merged 3 commits from changes into master 2026-02-24 12:57:45 -05:00
3 changed files with 122 additions and 50 deletions
Showing only changes of commit 93c8814b84 - Show all commits

View File

@@ -41,10 +41,11 @@ import androidx.security.crypto.MasterKey
import com.example.scanwich.LoginScreen
import com.example.scanwich.MainApp
import com.example.scanwich.AccessDeniedScreen
import com.google.firebase.firestore.FirebaseFirestore
@Composable
@Suppress("DEPRECATION")
fun AuthWrapper(dao: AppDao, ) {
fun AuthWrapper(dao: AppDao) {
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val auth = remember { FirebaseAuth.getInstance() }
@@ -56,8 +57,8 @@ fun AuthWrapper(dao: AppDao, ) {
}
val googleSignInClient = remember { GoogleSignIn.getClient(context, gso) }
var firebaseUser by remember { mutableStateOf<FirebaseUser?>(auth.currentUser) }
val allowedEmails = listOf("marcandre.charest@gmail.com", "everousseau07@gmail.com")
var isAuthorized by remember { mutableStateOf<Boolean?>(null) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
@@ -68,7 +69,6 @@ fun AuthWrapper(dao: AppDao, ) {
try {
val authResult = auth.signInWithCredential(credential).await()
firebaseUser = authResult.user
Log.d("Auth", "Connecté à Firebase avec : ${firebaseUser?.email}")
} catch (e: Exception) {
Log.e("Auth", "Erreur Firebase Auth : ${e.message}")
Toast.makeText(context, "Erreur de synchronisation Firebase.", Toast.LENGTH_LONG).show()
@@ -77,9 +77,8 @@ fun AuthWrapper(dao: AppDao, ) {
} catch (e: ApiException) {
Log.e("Auth", "Erreur Google Sign-In : ${e.statusCode}")
val msg = when (e.statusCode) {
10 -> "Erreur 10 : SHA-1 non reconnu dans Firebase. Assurez-vous d'avoir ajouté le SHA-1 de TOUTES vos clés de signature."
10 -> "Erreur 10 : SHA-1 non reconnu. Assurez-vous d'avoir ajouté le SHA-1 de VOS clés."
7 -> "Erreur 7 : Problème de réseau."
12500 -> "Erreur 12500 : Problème de configuration Google Play Services."
else -> "Erreur Google (Code ${e.statusCode})."
}
Toast.makeText(context, msg, Toast.LENGTH_LONG).show()
@@ -90,17 +89,52 @@ fun AuthWrapper(dao: AppDao, ) {
auth.signOut()
googleSignInClient.signOut().addOnCompleteListener {
firebaseUser = null
isAuthorized = null
}
}
LaunchedEffect(firebaseUser) {
isAuthorized = null
val email = firebaseUser?.email?.trim()?.lowercase()
if (email != null && email.isNotEmpty()) {
Log.d("Auth", "Vérification de l'autorisation pour l'email: '$email'")
try {
// On spécifie explicitement la base de données "scan-wich"
val db = FirebaseFirestore.getInstance("scan-wich")
val docRef = db.collection("authorized_users").document(email)
val document = docRef.get().await()
if (document.exists()) {
Log.d("Auth", "Accès AUTORISÉ pour '$email'. Document trouvé.")
isAuthorized = true
} else {
Log.w("Auth", "Accès REFUSÉ pour '$email'. Document NON trouvé.")
isAuthorized = false
}
} catch (e: Exception) {
Log.e("Auth", "Erreur critique Firestore. Vérifiez les règles de sécurité.", e)
isAuthorized = false
}
} else if (firebaseUser != null) {
Log.w("Auth", "L'utilisateur est connecté mais son email est vide.")
isAuthorized = false
}
}
if (firebaseUser == null) {
LoginScreen { launcher.launch(googleSignInClient.signInIntent) }
} else {
val userEmail = firebaseUser?.email?.lowercase() ?: ""
if (allowedEmails.contains(userEmail)) {
MainApp(dao = dao, onLogout = onLogout, userId = firebaseUser!!.uid)
} else {
AccessDeniedScreen(onLogout)
when (isAuthorized) {
true -> MainApp(dao = dao, onLogout = onLogout, userId = firebaseUser!!.uid)
false -> AccessDeniedScreen(onLogout)
null -> {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CircularProgressIndicator()
Spacer(modifier = Modifier.height(8.dp))
Text("Vérification de l'accès...")
}
}
}
}
}
}

View File

@@ -1,24 +1,23 @@
package com.example.scanwich
import android.content.SharedPreferences
import android.util.Log
import android.widget.Toast
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.foundation.background
import androidx.compose.runtime.Composable
import androidx.core.content.edit
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.FirebaseFirestore
@Composable
fun SetupScreen(prefs: SharedPreferences, onComplete: () -> Unit) {
@@ -31,6 +30,7 @@ fun SetupScreen(prefs: SharedPreferences, onComplete: () -> Unit) {
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")
@@ -139,6 +139,12 @@ fun SetupScreen(prefs: SharedPreferences, onComplete: () -> Unit) {
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
@@ -160,22 +166,48 @@ fun SetupScreen(prefs: SharedPreferences, onComplete: () -> Unit) {
val targetProtein = (targetCals * 0.2 / 4).toInt()
val targetFat = (targetCals * 0.3 / 9).toInt()
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)
}
onComplete()
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()

View File

@@ -1,40 +1,46 @@
📝 Notes de version - Scan-Wich
Dernières mises à jour :
🛠️ Correctifs et Améliorations Strava :
- Résolution d'un problème de compilation bloquant sur l'écran des sports.
- Intégration d'un nouvel algorithme d'estimation des calories basé sur les MET (Metabolic Equivalent of Task) pour une précision accrue des activités Strava sans données de calories natives.
- Amélioration de la fiabilité du parsing des dates d'activités Strava.
**Changements majeurs de la version actuelle :**
🛡️ **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.
- **Correctif Critique de Connexion :** Résolution d'un bug majeur qui empêchait l'application de se connecter correctement à la base de données Firestore, causant des accès refusés inattendus pour les utilisateurs légitimes. L'application est désormais compatible avec les bases de données Firestore nommées.
---
Nouveautés et Améliorations précédentes :
**Mises à jour précédentes :**
🛡 Sécurité renforcée :
🛠 **Correctifs et Améliorations Strava :**
- Résolution d'un problème de compilation bloquant sur l'écran des sports.
- Intégration d'un nouvel algorithme d'estimation des calories basé sur les MET (Metabolic Equivalent of Task) pour une précision accrue.
- Amélioration de la fiabilité du parsing des dates d'activités Strava.
🛡️ **Sécurité renforcée :**
- Intégration de Firebase App Check (Play Integrity) pour protéger l'API contre les accès non autorisés.
- Migration de la clé API vers Google Cloud Secret Manager, supprimant toute information sensible du code source.
⚡ Analyse Ultra-Rapide :
**Analyse Ultra-Rapide :**
- Nouveau moteur de compression d'image intelligent (réduction de ~2.2 Mo à 150 Ko par scan), accélérant drastiquement l'analyse IA.
🤖 IA Sécurisée :
🤖 **IA Sécurisée :**
- Migration de la logique d'analyse (prompts) vers des Cloud Functions pour garantir des résultats plus fiables et protégés.
📄 Export PDF Professionnel :
📄 **Export PDF Professionnel :**
- Nouvelle fonctionnalité d'exportation de l'historique. Générez un rapport PDF complet incluant vos repas, activités sportives et suivis de glycémie.
🩸 Suivi Diabétique complet :
🩸 **Suivi Diabétique complet :**
- Possibilité d'enregistrer et de visualiser sa glycémie avant/après chaque catégorie de repas directement dans l'historique.
🚴 Synchronisation Strava :
🚴 **Synchronisation Strava :**
- Connexion directe à Strava pour importer vos activités et calculer précisément les calories brûlées.
⭐ Gestion des Favoris :
**Gestion des Favoris :**
- Refonte de l'interface des favoris avec une nouvelle fenêtre modale pour un ajout rapide de vos repas récurrents.
🔍 Scanner de Code-barres :
🔍 **Scanner de Code-barres :**
- Intégration d'Open Food Facts pour identifier instantanément les produits industriels via leur code-barres.
🔧 Stabilité et Modernisation :
🔧 **Stabilité et Modernisation :**
- Optimisation pour Android 36.
- Correction de bugs majeurs sur le stockage sécurisé des préférences (MasterKey) et la synchronisation Firebase (collectAsState).