changes
This commit is contained in:
@@ -31,6 +31,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.scanwich.ui.theme.ReadableAmber
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -329,13 +330,38 @@ fun HistoryScreen(dao: AppDao, prefs: android.content.SharedPreferences) {
|
||||
|
||||
val totalIn = meals.sumOf { it.totalCalories }
|
||||
val totalOut = sports.sumOf { it.calories?.toInt() ?: 0 }
|
||||
val netTotal = totalIn - totalOut
|
||||
val totalCarbs = meals.sumOf { it.carbs }
|
||||
val totalProt = meals.sumOf { it.protein }
|
||||
val totalFat = meals.sumOf { it.fat }
|
||||
|
||||
Card(modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant)) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text("Objectifs Quotidiens", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
||||
Text("Résumé Calorique", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text("Mangé", style = MaterialTheme.typography.labelMedium, color = Color.Gray)
|
||||
Text("$totalIn", style = MaterialTheme.typography.titleLarge, color = calorieColor, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
Text("-", style = MaterialTheme.typography.headlineMedium, color = Color.Gray)
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text("Dépensé", style = MaterialTheme.typography.labelMedium, color = Color.Gray)
|
||||
Text("$totalOut", style = MaterialTheme.typography.titleLarge, color = MaterialTheme.colorScheme.primary, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
Text("=", style = MaterialTheme.typography.headlineMedium, color = Color.Gray)
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text("Total Net", style = MaterialTheme.typography.labelMedium, color = Color.Gray)
|
||||
Text("$netTotal", style = MaterialTheme.typography.titleLarge, color = if(netTotal <= tCal) calorieColor else Color.Red, fontWeight = FontWeight.ExtraBold)
|
||||
}
|
||||
}
|
||||
Text("kcal", style = MaterialTheme.typography.labelSmall, modifier = Modifier.align(Alignment.End), color = Color.Gray)
|
||||
|
||||
Spacer(Modifier.height(12.dp))
|
||||
HorizontalDivider()
|
||||
Spacer(Modifier.height(12.dp))
|
||||
|
||||
Text("Objectifs Quotidiens", style = MaterialTheme.typography.titleSmall, fontWeight = FontWeight.Bold)
|
||||
Spacer(Modifier.height(12.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
|
||||
DailyGoalChart("Calories", totalIn, tCal, calorieColor)
|
||||
@@ -343,13 +369,6 @@ fun HistoryScreen(dao: AppDao, prefs: android.content.SharedPreferences) {
|
||||
DailyGoalChart("Protéines", totalProt, tProt, proteinColor)
|
||||
DailyGoalChart("Lipides", totalFat, tFat, fatColor)
|
||||
}
|
||||
Spacer(Modifier.height(12.dp))
|
||||
HorizontalDivider()
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Text("Sport (Brûlées):")
|
||||
Text("$totalOut kcal", fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.primary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package com.example.scanwich
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.example.scanwich.ui.theme.ScanwichTheme
|
||||
import com.google.firebase.Firebase
|
||||
import com.google.firebase.appcheck.appCheck
|
||||
@@ -20,11 +25,26 @@ import kotlinx.coroutines.launch
|
||||
import androidx.core.content.edit
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
private val requestPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted: Boolean ->
|
||||
if (isGranted) {
|
||||
NotificationHelper.scheduleReminders(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
checkNotificationPermission()
|
||||
NotificationHelper.scheduleReminders(this)
|
||||
|
||||
try {
|
||||
Firebase.initialize(this)
|
||||
val appCheckFactory = if (BuildConfig.DEBUG) {
|
||||
|
||||
// On utilise explicitement le package name pour BuildConfig car l'import peut échouer
|
||||
val appCheckFactory = if (com.example.scanwich.BuildConfig.DEBUG) {
|
||||
DebugAppCheckProviderFactory.getInstance()
|
||||
} else {
|
||||
PlayIntegrityAppCheckProviderFactory.getInstance()
|
||||
@@ -48,6 +68,16 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkNotificationPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) !=
|
||||
PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
handleStravaCallback(intent)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.example.scanwich
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationCompat
|
||||
|
||||
class MealReminderReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
|
||||
NotificationHelper.scheduleReminders(context)
|
||||
return
|
||||
}
|
||||
|
||||
val mealType = intent.getStringExtra("meal_type") ?: "repas"
|
||||
showNotification(context, mealType)
|
||||
}
|
||||
|
||||
private fun showNotification(context: Context, mealType: String) {
|
||||
val channelId = "meal_reminders"
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(
|
||||
channelId,
|
||||
"Rappels de repas",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
).apply {
|
||||
description = "Notifications pour ne pas oublier d'entrer vos repas"
|
||||
}
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
val activityIntent = Intent(context, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context, 0, activityIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val notification = NotificationCompat.Builder(context, channelId)
|
||||
.setSmallIcon(android.R.drawable.ic_dialog_info) // À remplacer par l'icône de l'app si dispo
|
||||
.setContentTitle("N'oubliez pas votre $mealType !")
|
||||
.setContentText("Prenez un moment pour enregistrer ce que vous avez mangé.")
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
|
||||
val notificationId = when(mealType.lowercase()) {
|
||||
"déjeuner" -> 1
|
||||
"dîner" -> 2
|
||||
"souper" -> 3
|
||||
else -> 0
|
||||
}
|
||||
notificationManager.notify(notificationId, notification)
|
||||
}
|
||||
}
|
||||
48
app/src/main/java/com/example/scanwich/NotificationHelper.kt
Normal file
48
app/src/main/java/com/example/scanwich/NotificationHelper.kt
Normal file
@@ -0,0 +1,48 @@
|
||||
package com.example.scanwich
|
||||
|
||||
import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import java.util.*
|
||||
|
||||
object NotificationHelper {
|
||||
fun scheduleReminders(context: Context) {
|
||||
scheduleMealAlarm(context, 8, 30, "Déjeuner", 101)
|
||||
scheduleMealAlarm(context, 12, 30, "Dîner", 102)
|
||||
scheduleMealAlarm(context, 19, 30, "Souper", 103)
|
||||
}
|
||||
|
||||
private fun scheduleMealAlarm(context: Context, hour: Int, minute: Int, mealType: String, requestCode: Int) {
|
||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
val intent = Intent(context, MealReminderReceiver::class.java).apply {
|
||||
putExtra("meal_type", mealType)
|
||||
}
|
||||
|
||||
val pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
requestCode,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val calendar = Calendar.getInstance().apply {
|
||||
timeInMillis = System.currentTimeMillis()
|
||||
set(Calendar.HOUR_OF_DAY, hour)
|
||||
set(Calendar.MINUTE, minute)
|
||||
set(Calendar.SECOND, 0)
|
||||
|
||||
// Si l'heure est déjà passée, on programme pour demain
|
||||
if (before(Calendar.getInstance())) {
|
||||
add(Calendar.DATE, 1)
|
||||
}
|
||||
}
|
||||
|
||||
alarmManager.setInexactRepeating(
|
||||
AlarmManager.RTC_WAKEUP,
|
||||
calendar.timeInMillis,
|
||||
AlarmManager.INTERVAL_DAY,
|
||||
pendingIntent
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,15 @@ fun analyzeImage(
|
||||
bitmap?.let { getOptimizedImageBase64(it) }
|
||||
}
|
||||
|
||||
val data = hashMapOf("imageBase64" to base64, "mealName" to textDescription)
|
||||
// Instruction pour que l'IA se concentre uniquement sur la nourriture (sans qualificatifs ni environnement)
|
||||
val aiInstruction = "Focus seulement sur la nourriture, pas de qualificatif, pas son environnement, seulement la nourriture."
|
||||
val mealDescriptionForAI = if (textDescription.isNullOrBlank()) {
|
||||
aiInstruction
|
||||
} else {
|
||||
"$textDescription. $aiInstruction"
|
||||
}
|
||||
|
||||
val data = hashMapOf("imageBase64" to base64, "mealName" to mealDescriptionForAI)
|
||||
|
||||
Firebase.functions("us-central1")
|
||||
.getHttpsCallable("analyzeMealProxy")
|
||||
|
||||
Reference in New Issue
Block a user