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

2
.idea/.name generated
View File

@@ -1 +1 @@
coloricam
scan-wich

View File

@@ -1,9 +1,12 @@
import java.util.Properties
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.ksp)
alias(libs.plugins.secrets)
alias(libs.plugins.google.services)
alias(libs.plugins.firebase.appdistribution)
}
android {
@@ -21,23 +24,45 @@ android {
}
signingConfigs {
// On configure la release pour utiliser la même clé que le debug pour l'instant
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("local.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(keystorePropertiesFile.inputStream())
}
getByName("debug") {
storeFile = file(System.getProperty("user.home") + "/.android/debug.keystore")
storePassword = "android"
keyAlias = "androiddebugkey"
keyPassword = "android"
}
create("release") {
storeFile = file("C:\\Users\\mac\\keys\\keys")
storePassword = keystoreProperties.getProperty("RELEASE_STORE_PASSWORD")
keyAlias = keystoreProperties.getProperty("RELEASE_KEY_ALIAS") ?: "key0"
keyPassword = keystoreProperties.getProperty("RELEASE_KEY_PASSWORD")
}
}
buildTypes {
release {
isMinifyEnabled = false
signingConfig = signingConfigs.getByName("debug")
signingConfig = signingConfigs.getByName("release")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
configure<com.google.firebase.appdistribution.gradle.AppDistributionExtension> {
artifactType = "APK"
}
}
debug {
configure<com.google.firebase.appdistribution.gradle.AppDistributionExtension> {
artifactType = "APK"
}
}
}
compileOptions {
@@ -51,7 +76,6 @@ android {
}
secrets {
// A list of keys that should be ignored by the plugin by default.
ignoreList.add("properties")
}
@@ -68,24 +92,19 @@ dependencies {
implementation(libs.coil.compose)
implementation(libs.androidx.exifinterface)
// Navigation
implementation(libs.androidx.navigation.compose)
// Room
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)
// Network & Strava Auth
implementation(libs.retrofit.core)
implementation(libs.retrofit.gson)
implementation(libs.okhttp.logging)
implementation(libs.androidx.browser)
// Google Sign-In
implementation(libs.play.services.auth)
// Firebase
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.analytics)

View File

@@ -21,6 +21,14 @@
"certificate_hash": "2c39e4131dcac8a1d4257b804718ac113f855b04"
}
},
{
"client_id": "652626507041-i6ne7rt1b711gfpbc5f5hd1q60kd0ntv.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.example.scanwich",
"certificate_hash": "ebcc060f9a1fdeb1186536d3828574b42cefa03c"
}
},
{
"client_id": "652626507041-5n42q37adh1guuv9gibfcf5uvekgunbe.apps.googleusercontent.com",
"client_type": 3

Binary file not shown.

View File

@@ -1 +0,0 @@
// Ce fichier est obsolète. Utilisez celui dans le package com.example.scanwich.

View File

@@ -1 +0,0 @@
// Ce fichier est obsolète. Utilisez celui dans le package com.example.scanwich.

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

@@ -5,4 +5,5 @@ plugins {
alias(libs.plugins.ksp) apply false
alias(libs.plugins.secrets) apply false
alias(libs.plugins.google.services) apply false
alias(libs.plugins.firebase.appdistribution) apply false
}

View File

@@ -1,26 +1,27 @@
[versions]
agp = "9.0.1"
coreKtx = "1.10.1"
coreKtx = "1.12.0"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
lifecycleRuntimeKtx = "2.6.1"
activityCompose = "1.8.0"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.10.0"
kotlin = "2.0.21"
composeBom = "2024.09.00"
composeBom = "2025.02.00"
generativeai = "0.9.0"
coil = "2.7.0"
room = "2.8.4"
navigation = "2.7.7"
navigation = "2.8.7"
ksp = "2.0.21-1.0.27"
retrofit = "2.9.0"
okhttp = "4.12.0"
browser = "1.8.0"
exifinterface = "1.3.7"
secretsPlugin = "2.0.1"
playServicesAuth = "21.2.0"
playServicesAuth = "21.3.0"
googleServices = "4.4.2"
firebaseBom = "34.9.0"
firebaseAppDistribution = "5.2.1"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -58,3 +59,4 @@ kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "ko
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secretsPlugin" }
google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" }
firebase-appdistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" }

View File

@@ -22,5 +22,5 @@ dependencyResolutionManagement {
}
}
rootProject.name = "coloricam"
rootProject.name = "scan-wich"
include(":app")