diff --git a/.idea/.name b/.idea/.name index ceab9ac..9040ef8 100644 --- a/.idea/.name +++ b/.idea/.name @@ -1 +1 @@ -coloricam \ No newline at end of file +scan-wich \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 39fb6ba..5a29abe 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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 { + artifactType = "APK" + } + } + + debug { + configure { + 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) diff --git a/app/google-services.json b/app/google-services.json index e9b8783..1098f98 100644 --- a/app/google-services.json +++ b/app/google-services.json @@ -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 diff --git a/app/release/app-release.apk b/app/release/app-release.apk index aa61d63..bbfc722 100644 Binary files a/app/release/app-release.apk and b/app/release/app-release.apk differ diff --git a/app/release/baselineProfiles/0/app-release.dm b/app/release/baselineProfiles/0/app-release.dm index 8867e9e..5e8f2a8 100644 Binary files a/app/release/baselineProfiles/0/app-release.dm and b/app/release/baselineProfiles/0/app-release.dm differ diff --git a/app/release/baselineProfiles/1/app-release.dm b/app/release/baselineProfiles/1/app-release.dm index 8af9ce4..dc729d0 100644 Binary files a/app/release/baselineProfiles/1/app-release.dm and b/app/release/baselineProfiles/1/app-release.dm differ diff --git a/app/src/main/java/com/example/coloricam/Database.kt b/app/src/main/java/com/example/coloricam/Database.kt deleted file mode 100644 index 8762c2f..0000000 --- a/app/src/main/java/com/example/coloricam/Database.kt +++ /dev/null @@ -1 +0,0 @@ -// Ce fichier est obsolète. Utilisez celui dans le package com.example.scanwich. diff --git a/app/src/main/java/com/example/coloricam/MainActivity.kt b/app/src/main/java/com/example/coloricam/MainActivity.kt deleted file mode 100644 index 8762c2f..0000000 --- a/app/src/main/java/com/example/coloricam/MainActivity.kt +++ /dev/null @@ -1 +0,0 @@ -// Ce fichier est obsolète. Utilisez celui dans le package com.example.scanwich. diff --git a/app/src/main/java/com/example/scanwich/MainActivity.kt b/app/src/main/java/com/example/scanwich/MainActivity.kt index bd752fe..37b6992 100644 --- a/app/src/main/java/com/example/scanwich/MainActivity.kt +++ b/app/src/main/java/com/example/scanwich/MainActivity.kt @@ -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(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 { diff --git a/app/src/main/java/com/example/coloricam/ui/theme/Color.kt b/app/src/main/java/com/example/scanwich/ui/theme/Color.kt similarity index 100% rename from app/src/main/java/com/example/coloricam/ui/theme/Color.kt rename to app/src/main/java/com/example/scanwich/ui/theme/Color.kt diff --git a/app/src/main/java/com/example/coloricam/ui/theme/Theme.kt b/app/src/main/java/com/example/scanwich/ui/theme/Theme.kt similarity index 100% rename from app/src/main/java/com/example/coloricam/ui/theme/Theme.kt rename to app/src/main/java/com/example/scanwich/ui/theme/Theme.kt diff --git a/app/src/main/java/com/example/coloricam/ui/theme/Type.kt b/app/src/main/java/com/example/scanwich/ui/theme/Type.kt similarity index 100% rename from app/src/main/java/com/example/coloricam/ui/theme/Type.kt rename to app/src/main/java/com/example/scanwich/ui/theme/Type.kt diff --git a/build.gradle.kts b/build.gradle.kts index 0330749..631610b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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 } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9ac1ab9..a9868aa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" } diff --git a/settings.gradle.kts b/settings.gradle.kts index c6e9c2a..8c781e8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,5 +22,5 @@ dependencyResolutionManagement { } } -rootProject.name = "coloricam" +rootProject.name = "scan-wich" include(":app")