Fix image picker crash, add 7-day bar chart, bump to v1.2
- Image reading moved to IO thread with runCatching to prevent main thread crash and OOM on large gallery images - 7-day calorie bar chart added to dashboard using Canvas - Version bumped to 1.2 (code 3) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
709d6c1f6f
commit
af35b70399
4 changed files with 55 additions and 4 deletions
|
|
@ -12,8 +12,8 @@ android {
|
|||
applicationId 'com.danvics.calorieai'
|
||||
minSdk 26
|
||||
targetSdk 35
|
||||
versionCode 2
|
||||
versionName '1.1'
|
||||
versionCode 3
|
||||
versionName '1.2'
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
|
|
|
|||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -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<DayTotals>, 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)
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ fun DashboardScreen(entries: List<MealEntry>, 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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue