diff --git a/app/build.gradle b/app/build.gradle index 2617ca9..171a4c2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { applicationId 'com.danvics.calorieai' minSdk 26 targetSdk 35 - versionCode 2 - versionName '1.1' + versionCode 3 + versionName '1.2' } buildFeatures { diff --git a/app/src/main/java/com/danvics/calorieai/MainActivity.kt b/app/src/main/java/com/danvics/calorieai/MainActivity.kt index 1e5cca9..6ab5819 100644 --- a/app/src/main/java/com/danvics/calorieai/MainActivity.kt +++ b/app/src/main/java/com/danvics/calorieai/MainActivity.kt @@ -52,8 +52,19 @@ private fun AppRoot(repo: ApiRepository) { var syncing by remember { mutableStateOf(false) } val imagePicker = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? -> - if (uri != null) selectedImage = context.contentResolver.openInputStream(uri)?.use { stream -> - ImagePayload.fromBytes(uri.lastPathSegment ?: "image", context.contentResolver.getType(uri) ?: "image/jpeg", stream.readBytes()) + if (uri != null) { + scope.launch(Dispatchers.IO) { + val payload = runCatching { + context.contentResolver.openInputStream(uri)?.use { stream -> + ImagePayload.fromBytes( + uri.lastPathSegment ?: "image", + context.contentResolver.getType(uri) ?: "image/jpeg", + stream.readBytes() + ) + } + }.getOrNull() + withContext(Dispatchers.Main) { if (payload != null) selectedImage = payload } + } } } val camera = rememberLauncherForActivityResult(ActivityResultContracts.TakePicturePreview()) { bitmap -> diff --git a/app/src/main/java/com/danvics/calorieai/ui/Components.kt b/app/src/main/java/com/danvics/calorieai/ui/Components.kt index 671fb26..05d192e 100644 --- a/app/src/main/java/com/danvics/calorieai/ui/Components.kt +++ b/app/src/main/java/com/danvics/calorieai/ui/Components.kt @@ -1,12 +1,18 @@ package com.danvics.calorieai.ui +import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import com.danvics.calorieai.data.DayTotals @Composable fun SectionCard(title: String, modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) { @@ -78,6 +84,38 @@ fun MacroBar(label: String, grams: Int, totalGrams: Int, color: Color) { } } +@Composable +fun WeekBarChart(week: List, modifier: Modifier = Modifier) { + val primary = MaterialTheme.colorScheme.primary + val track = MaterialTheme.colorScheme.surfaceVariant + val maxCal = week.maxOfOrNull { it.calories }.takeIf { it != null && it > 0 } ?: 1 + Column(modifier) { + Canvas(modifier = Modifier.fillMaxWidth().height(96.dp)) { + val slotW = size.width / week.size + val barW = slotW * 0.55f + val maxH = size.height + week.forEachIndexed { i, day -> + val x = i * slotW + (slotW - barW) / 2 + val fillH = (day.calories.toFloat() / maxCal) * maxH + drawRoundRect(track, Offset(x, 0f), Size(barW, maxH), CornerRadius(6f)) + if (fillH > 0f) drawRoundRect(primary, Offset(x, maxH - fillH), Size(barW, fillH), CornerRadius(6f)) + } + } + Spacer(Modifier.height(4.dp)) + Row(Modifier.fillMaxWidth()) { + week.forEach { day -> + Text( + day.label, + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + modifier = Modifier.weight(1f) + ) + } + } + } +} + @Composable fun CalorieGoalBar(consumed: Int, target: Int) { val fraction = (consumed.toFloat() / target).coerceIn(0f, 1f) diff --git a/app/src/main/java/com/danvics/calorieai/ui/DashboardScreen.kt b/app/src/main/java/com/danvics/calorieai/ui/DashboardScreen.kt index f4de8e5..fd155e4 100644 --- a/app/src/main/java/com/danvics/calorieai/ui/DashboardScreen.kt +++ b/app/src/main/java/com/danvics/calorieai/ui/DashboardScreen.kt @@ -66,6 +66,8 @@ fun DashboardScreen(entries: List, settings: ServerSettings, onLog: ( if (week.any { it.calories > 0 }) { SectionCard("Last 7 days") { + WeekBarChart(week, Modifier.fillMaxWidth()) + HorizontalDivider(thickness = 0.5.dp, color = MaterialTheme.colorScheme.outlineVariant) week.reversed().forEach { day -> Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Text(day.label, style = MaterialTheme.typography.bodyMedium)