This commit is contained in:
mac
2026-02-22 20:28:25 -05:00
parent 1140dcc1fc
commit 670880d197
15 changed files with 84 additions and 34 deletions

View File

@@ -76,7 +76,7 @@ data class N8nMealRequest(
)
interface N8nApi {
@POST("webhook-test/v1/gemini-proxy")
@POST("webhook/v1/gemini-proxy")
suspend fun analyzeMeal(
@Header("X-API-KEY") apiKey: String,
@Body request: N8nMealRequest
@@ -278,7 +278,7 @@ 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 dans Firebase. Assurez-vous d\u0027avoir ajouté le SHA-1 de TOUTES vos clés de signature."
7 -> "Erreur 7 : Problème de réseau."
12500 -> "Erreur 12500 : Problème de configuration Google Play Services."
else -> "Erreur Google (Code ${e.statusCode})."
@@ -333,7 +333,7 @@ fun AccessDeniedScreen(onLogout: () -> Unit) {
Spacer(Modifier.height(16.dp))
Text("Accès Refusé", style = MaterialTheme.typography.headlineMedium, color = MaterialTheme.colorScheme.error)
Spacer(Modifier.height(8.dp))
Text("Votre compte n'est pas autorisé à utiliser cette application.", style = MaterialTheme.typography.bodyLarge)
Text("Votre compte n\u0027est pas autorisé à utiliser cette application.", style = MaterialTheme.typography.bodyLarge)
Spacer(Modifier.height(32.dp))
Button(onClick = onLogout) { Text("Changer de compte") }
}
@@ -471,7 +471,7 @@ fun SetupScreen(prefs: SharedPreferences, onComplete: () -> Unit) {
Spacer(Modifier.height(16.dp))
Text("Niveau d'activité :", style = MaterialTheme.typography.titleMedium, modifier = Modifier.align(Alignment.Start))
Text("Niveau d\u0027activité :", style = MaterialTheme.typography.titleMedium, modifier = Modifier.align(Alignment.Start))
activityLevels.forEach { level ->
Row(
Modifier
@@ -607,7 +607,7 @@ fun CaptureScreen(dao: AppDao, prefs: SharedPreferences, isDiabetic: Boolean) {
currentMealData = data
showBottomSheet = true
} else {
Toast.makeText(context, "L'IA n'a pas pu identifier le repas.", Toast.LENGTH_LONG).show()
Toast.makeText(context, "L\u0027IA n\u0027a pas pu identifier le repas.", Toast.LENGTH_LONG).show()
}
}, coroutineScope)
} catch (e: Exception) {
@@ -665,7 +665,7 @@ fun CaptureScreen(dao: AppDao, prefs: SharedPreferences, isDiabetic: Boolean) {
OutlinedTextField(
value = editableDesc,
onValueChange = { editableDesc = it },
label = { Text("Description / Précisions pour l'IA") },
label = { Text("Description / Précisions pour l\u0027IA") },
modifier = Modifier.fillMaxWidth(),
minLines = 3
)
@@ -686,7 +686,7 @@ fun CaptureScreen(dao: AppDao, prefs: SharedPreferences, isDiabetic: Boolean) {
) {
Icon(Icons.Default.Refresh, null)
Spacer(Modifier.width(8.dp))
Text("Ressoumettre à l'IA")
Text("Ressoumettre à l\u0027IA")
}
Spacer(Modifier.height(16.dp))
@@ -771,7 +771,7 @@ fun CaptureScreen(dao: AppDao, prefs: SharedPreferences, isDiabetic: Boolean) {
Spacer(Modifier.height(32.dp))
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth()) {
CircularProgressIndicator()
Text("Analyse par l'IA en cours...", modifier = Modifier.padding(top = 8.dp))
Text("Analyse par l\u0027IA en cours...", modifier = Modifier.padding(top = 8.dp))
}
}
@@ -783,7 +783,7 @@ fun CaptureScreen(dao: AppDao, prefs: SharedPreferences, isDiabetic: Boolean) {
OutlinedTextField(
value = manualMealName,
onValueChange = { manualMealName = it },
label = { Text("Qu'avez-vous mangé ?") },
label = { Text("Qu\u0027avez-vous mangé ?") },
placeholder = { Text("ex: Un sandwich au poulet et une pomme") },
modifier = Modifier.fillMaxWidth()
)
@@ -869,6 +869,7 @@ fun HistoryScreen(dao: AppDao, prefs: SharedPreferences) {
val isDiabetic = prefs.getBoolean("is_diabetic", false)
var selectedMealForDetail by remember { mutableStateOf<Meal?>(null) }
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val tCal = prefs.getString("target_calories", "2000")?.toIntOrNull() ?: 2000
val tCarb = prefs.getString("target_carbs", "250")?.toIntOrNull() ?: 250
@@ -972,7 +973,15 @@ fun HistoryScreen(dao: AppDao, prefs: SharedPreferences) {
if (isDiabetic && beforeGly != null) {
item {
Surface(color = Color.Blue.copy(alpha = 0.1f), modifier = Modifier.fillMaxWidth().clip(MaterialTheme.shapes.small)) {
Text("🩸 Glycémie Avant: ${beforeGly.value} mmol/L", modifier = Modifier.padding(8.dp), style = MaterialTheme.typography.bodyMedium)
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp)) {
Text("🩸 Glycémie Avant: ${beforeGly.value} mmol/L", modifier = Modifier.weight(1f), style = MaterialTheme.typography.bodyMedium)
IconButton(onClick = {
coroutineScope.launch {
dao.deleteGlycemia(beforeGly)
Toast.makeText(context, "Glycémie supprimée", Toast.LENGTH_SHORT).show()
}
}) { Icon(Icons.Default.Delete, null, modifier = Modifier.size(20.dp)) }
}
}
}
}
@@ -982,7 +991,12 @@ fun HistoryScreen(dao: AppDao, prefs: SharedPreferences) {
headlineContent = { Text(meal.name) },
supportingContent = { Text("${meal.totalCalories} kcal - G:${meal.carbs}g P:${meal.protein}g L:${meal.fat}g") },
trailingContent = {
IconButton(onClick = { /* TODO */ }) { Icon(Icons.Default.Delete, null) }
IconButton(onClick = {
coroutineScope.launch {
dao.deleteMeal(meal)
Toast.makeText(context, "Repas supprimé", Toast.LENGTH_SHORT).show()
}
}) { Icon(Icons.Default.Delete, null) }
},
modifier = Modifier.clickable { selectedMealForDetail = meal }
)
@@ -991,7 +1005,15 @@ fun HistoryScreen(dao: AppDao, prefs: SharedPreferences) {
if (isDiabetic && afterGly != null) {
item {
Surface(color = Color.Green.copy(alpha = 0.1f), modifier = Modifier.fillMaxWidth().clip(MaterialTheme.shapes.small)) {
Text("🩸 Glycémie Après: ${afterGly.value} mmol/L", modifier = Modifier.padding(8.dp), style = MaterialTheme.typography.bodyMedium)
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp)) {
Text("🩸 Glycémie Après: ${afterGly.value} mmol/L", modifier = Modifier.weight(1f), style = MaterialTheme.typography.bodyMedium)
IconButton(onClick = {
coroutineScope.launch {
dao.deleteGlycemia(afterGly)
Toast.makeText(context, "Glycémie supprimée", Toast.LENGTH_SHORT).show()
}
}) { Icon(Icons.Default.Delete, null, modifier = Modifier.size(20.dp)) }
}
}
}
}
@@ -1033,9 +1055,9 @@ private fun analyzeImage(
val prompt = if (bitmap != null && textDescription == null) {
"Analyze this food image in FRENCH. Provide ONLY: 1. Name, 2. Summary description in FRENCH, 3. Macros. Format EXACTLY as: {\"name\": \"...\", \"description\": \"...\", \"calories\": int, \"carbs\": int, \"protein\": int, \"fat\": int}"
} else if (bitmap != null && textDescription != null) {
"Analyze this food image in FRENCH, taking into account these corrections or details: '$textDescription'. Provide ONLY: 1. Name, 2. Summary description in FRENCH, 3. Macros. Format EXACTLY as: {\"name\": \"...\", \"description\": \"...\", \"calories\": int, \"carbs\": int, \"protein\": int, \"fat\": int}"
"Analyze this food image in FRENCH, taking into account these corrections or details: \u0027$textDescription\u0027. Provide ONLY: 1. Name, 2. Summary description in FRENCH, 3. Macros. Format EXACTLY as: {\"name\": \"...\", \"description\": \"...\", \"calories\": int, \"carbs\": int, \"protein\": int, \"fat\": int}"
} else {
"Analyze this meal description in FRENCH: '$textDescription'. Estimate the macros. Provide ONLY a JSON object. Format EXACTLY as: {\"name\": \"...\", \"description\": \"...\", \"calories\": int, \"carbs\": int, \"protein\": int, \"fat\": int}"
"Analyze this meal description in FRENCH: \u0027$textDescription\u0027. Estimate the macros. Provide ONLY a JSON object. Format EXACTLY as: {\"name\": \"...\", \"description\": \"...\", \"calories\": int, \"carbs\": int, \"protein\": int, \"fat\": int}"
}
scope.launch {

View File

@@ -0,0 +1,11 @@
package com.example.scanwich.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

View File

@@ -0,0 +1,48 @@
package com.example.scanwich.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
)
@Composable
fun ScanwichTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

View File

@@ -0,0 +1,18 @@
package com.example.scanwich.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
)