Fix camera, add photo preview, native date/time pickers, auto-navigate dashboard after save
- Replace TakePicturePreview with TakePicture + FileProvider for reliable full-res camera capture - Add FileProvider to manifest with cache-path for camera photos - Show selected photo as a thumbnail preview in the Log screen - Replace plain date/time text fields with clickable fields that open native Android pickers - After AI analysis or manual edit save, auto-navigate to Dashboard so results are visible immediately - Clear editing/image state after successful save so Log screen resets for next entry Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
af35b70399
commit
b93e479be4
5 changed files with 209 additions and 52 deletions
|
|
@ -17,5 +17,14 @@
|
|||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.danvics.calorieai
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
|
|
@ -8,6 +10,7 @@ import androidx.activity.compose.setContent
|
|||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.content.FileProvider
|
||||
import com.danvics.calorieai.ai.ImagePayload
|
||||
import com.danvics.calorieai.ai.NutritionParser
|
||||
import com.danvics.calorieai.data.*
|
||||
|
|
@ -19,6 +22,7 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
|
@ -38,6 +42,11 @@ private fun AppRoot(repo: ApiRepository) {
|
|||
val scope = rememberCoroutineScope()
|
||||
val parser = remember { NutritionParser() }
|
||||
|
||||
val cameraImageFile = remember { File(context.cacheDir, "cam_capture.jpg") }
|
||||
val cameraImageUri: Uri = remember {
|
||||
FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", cameraImageFile)
|
||||
}
|
||||
|
||||
var connected by remember { mutableStateOf(repo.isConnected()) }
|
||||
var connectError by remember { mutableStateOf("") }
|
||||
var connecting by remember { mutableStateOf(false) }
|
||||
|
|
@ -45,30 +54,48 @@ private fun AppRoot(repo: ApiRepository) {
|
|||
var appState by remember { mutableStateOf(AppState()) }
|
||||
var editing by remember { mutableStateOf<MealEntry?>(null) }
|
||||
var selectedImage by remember { mutableStateOf<ImagePayload?>(null) }
|
||||
var selectedImageBitmap by remember { mutableStateOf<Bitmap?>(null) }
|
||||
var status by remember { mutableStateOf("") }
|
||||
var busy by remember { mutableStateOf(false) }
|
||||
var planStatus by remember { mutableStateOf("") }
|
||||
var planBusy by remember { mutableStateOf(false) }
|
||||
var syncing by remember { mutableStateOf(false) }
|
||||
var mealSaveCount by remember { mutableStateOf(0) }
|
||||
|
||||
val imagePicker = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
|
||||
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()
|
||||
)
|
||||
val bytes = context.contentResolver.openInputStream(uri)?.use { it.readBytes() }
|
||||
if (bytes != null) {
|
||||
val bitmap = decodeSampledBitmap(bytes)
|
||||
val payload = ImagePayload.fromBytes(
|
||||
uri.lastPathSegment ?: "image",
|
||||
context.contentResolver.getType(uri) ?: "image/jpeg",
|
||||
bytes
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
selectedImage = payload
|
||||
selectedImageBitmap = bitmap
|
||||
}
|
||||
}.getOrNull()
|
||||
withContext(Dispatchers.Main) { if (payload != null) selectedImage = payload }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val camera = rememberLauncherForActivityResult(ActivityResultContracts.TakePicturePreview()) { bitmap ->
|
||||
if (bitmap != null) selectedImage = ImagePayload.fromBitmap("camera photo", bitmap)
|
||||
|
||||
val camera = rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) { success ->
|
||||
if (success) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val bytes = cameraImageFile.readBytes()
|
||||
if (bytes.isNotEmpty()) {
|
||||
val bitmap = decodeSampledBitmap(bytes)
|
||||
val payload = ImagePayload.fromBytes("photo.jpg", "image/jpeg", bytes)
|
||||
withContext(Dispatchers.Main) {
|
||||
selectedImage = payload
|
||||
selectedImageBitmap = bitmap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun handleUnauth() { connected = false; appState = AppState() }
|
||||
|
|
@ -88,21 +115,21 @@ private fun AppRoot(repo: ApiRepository) {
|
|||
|
||||
if (!connected) {
|
||||
CalorieTheme {
|
||||
ConnectScreen(error = connectError, connecting = connecting) { url, user, pass ->
|
||||
connecting = true
|
||||
connectError = ""
|
||||
scope.launch(Dispatchers.IO) {
|
||||
runCatching { repo.login(url, user, pass) }
|
||||
.onSuccess { cookie ->
|
||||
repo.saveConfig(url, cookie)
|
||||
withContext(Dispatchers.Main) { connecting = false; connected = true }
|
||||
}
|
||||
.onFailure { e ->
|
||||
withContext(Dispatchers.Main) { connecting = false; connectError = e.message ?: "Connection failed" }
|
||||
}
|
||||
ConnectScreen(error = connectError, connecting = connecting) { url, user, pass ->
|
||||
connecting = true
|
||||
connectError = ""
|
||||
scope.launch(Dispatchers.IO) {
|
||||
runCatching { repo.login(url, user, pass) }
|
||||
.onSuccess { cookie ->
|
||||
repo.saveConfig(url, cookie)
|
||||
withContext(Dispatchers.Main) { connecting = false; connected = true }
|
||||
}
|
||||
.onFailure { e ->
|
||||
withContext(Dispatchers.Main) { connecting = false; connectError = e.message ?: "Connection failed" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -113,13 +140,20 @@ private fun AppRoot(repo: ApiRepository) {
|
|||
busy = busy,
|
||||
editing = editing,
|
||||
selectedImageName = selectedImage?.name.orEmpty(),
|
||||
selectedImageBitmap = selectedImageBitmap,
|
||||
planStatus = planStatus,
|
||||
planBusy = planBusy,
|
||||
syncing = syncing,
|
||||
mealSaveCount = mealSaveCount,
|
||||
onSync = { syncState() },
|
||||
onPickImage = { imagePicker.launch("image/*") },
|
||||
onTakePhoto = { camera.launch(null) },
|
||||
onCancelEdit = { editing = null; selectedImage = null; status = "" },
|
||||
onTakePhoto = { camera.launch(cameraImageUri) },
|
||||
onCancelEdit = {
|
||||
editing = null
|
||||
selectedImage = null
|
||||
selectedImageBitmap = null
|
||||
status = ""
|
||||
},
|
||||
onSaveManualEdit = { updated ->
|
||||
scope.launch(Dispatchers.IO) {
|
||||
runCatching { repo.upsertEntry(updated) }
|
||||
|
|
@ -127,7 +161,10 @@ private fun AppRoot(repo: ApiRepository) {
|
|||
withContext(Dispatchers.Main) {
|
||||
appState = appState.copy(entries = entries, trash = trash)
|
||||
editing = null
|
||||
selectedImage = null
|
||||
selectedImageBitmap = null
|
||||
status = "Meal updated."
|
||||
mealSaveCount++
|
||||
}
|
||||
}
|
||||
.onFailure { e -> withContext(Dispatchers.Main) { if (e is UnauthorizedException) handleUnauth() else status = "Save failed: ${e.message}" } }
|
||||
|
|
@ -158,9 +195,11 @@ private fun AppRoot(repo: ApiRepository) {
|
|||
val (entries, trash) = pair
|
||||
withContext(Dispatchers.Main) {
|
||||
appState = appState.copy(entries = entries, trash = trash)
|
||||
editing = meal
|
||||
editing = null
|
||||
selectedImage = null
|
||||
selectedImageBitmap = null
|
||||
status = "Saved. ${meal.estimate.mealName} · ${meal.estimate.calories} kcal"
|
||||
mealSaveCount++
|
||||
busy = false
|
||||
}
|
||||
}.onFailure { e ->
|
||||
|
|
@ -289,6 +328,14 @@ private fun AppRoot(repo: ApiRepository) {
|
|||
)
|
||||
}
|
||||
|
||||
private fun decodeSampledBitmap(bytes: ByteArray, maxWidth: Int = 600): Bitmap {
|
||||
val opts = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
||||
BitmapFactory.decodeByteArray(bytes, 0, bytes.size, opts)
|
||||
var sample = 1
|
||||
while (opts.outWidth / sample > maxWidth) sample *= 2
|
||||
return BitmapFactory.decodeByteArray(bytes, 0, bytes.size, BitmapFactory.Options().apply { inSampleSize = sample })!!
|
||||
}
|
||||
|
||||
private fun buildVisionBody(model: String, description: String, measure: String, image: ImagePayload): JSONObject =
|
||||
JSONObject().put("model", model).put("temperature", 0.15)
|
||||
.put("messages", JSONArray().put(JSONObject().put("role", "user").put("content",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.danvics.calorieai.ui
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
|
|
@ -29,9 +30,11 @@ fun CalorieAiApp(
|
|||
busy: Boolean,
|
||||
editing: MealEntry?,
|
||||
selectedImageName: String,
|
||||
selectedImageBitmap: Bitmap?,
|
||||
planStatus: String,
|
||||
planBusy: Boolean,
|
||||
syncing: Boolean,
|
||||
mealSaveCount: Int,
|
||||
onSync: () -> Unit,
|
||||
onPickImage: () -> Unit,
|
||||
onTakePhoto: () -> Unit,
|
||||
|
|
@ -51,6 +54,11 @@ fun CalorieAiApp(
|
|||
) {
|
||||
CalorieTheme {
|
||||
var screen by remember { mutableStateOf(Screen.Dashboard) }
|
||||
|
||||
LaunchedEffect(mealSaveCount) {
|
||||
if (mealSaveCount > 0) screen = Screen.Dashboard
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
|
|
@ -88,7 +96,18 @@ fun CalorieAiApp(
|
|||
Box(Modifier.padding(padding).fillMaxSize()) {
|
||||
when (screen) {
|
||||
Screen.Dashboard -> DashboardScreen(appState.entries, appState.settings, onLog = { screen = Screen.Log })
|
||||
Screen.Log -> LogMealScreen(editing, selectedImageName, status, busy, onPickImage, onTakePhoto, onAnalyze, onSaveManualEdit, onCancelEdit)
|
||||
Screen.Log -> LogMealScreen(
|
||||
editing = editing,
|
||||
selectedImageName = selectedImageName,
|
||||
selectedImageBitmap = selectedImageBitmap,
|
||||
status = status,
|
||||
busy = busy,
|
||||
onPickImage = onPickImage,
|
||||
onTakePhoto = onTakePhoto,
|
||||
onAnalyze = onAnalyze,
|
||||
onSaveManualEdit = onSaveManualEdit,
|
||||
onCancelEdit = onCancelEdit
|
||||
)
|
||||
Screen.Diary -> DiaryScreen(
|
||||
entries = appState.entries,
|
||||
trash = appState.trash,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,31 @@
|
|||
package com.danvics.calorieai.ui
|
||||
|
||||
import android.app.DatePickerDialog
|
||||
import android.app.TimePickerDialog
|
||||
import android.graphics.Bitmap
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CalendarToday
|
||||
import androidx.compose.material.icons.filled.CameraAlt
|
||||
import androidx.compose.material.icons.filled.PhotoLibrary
|
||||
import androidx.compose.material.icons.filled.Schedule
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.danvics.calorieai.data.MealEntry
|
||||
import com.danvics.calorieai.data.NutritionEstimate
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Calendar
|
||||
|
||||
private val MEAL_TYPES = listOf("Breakfast", "Lunch", "Dinner", "Snack", "Other")
|
||||
|
||||
|
|
@ -23,6 +34,7 @@ private val MEAL_TYPES = listOf("Breakfast", "Lunch", "Dinner", "Snack", "Other"
|
|||
fun LogMealScreen(
|
||||
editing: MealEntry?,
|
||||
selectedImageName: String,
|
||||
selectedImageBitmap: Bitmap?,
|
||||
status: String,
|
||||
busy: Boolean,
|
||||
onPickImage: () -> Unit,
|
||||
|
|
@ -31,6 +43,8 @@ fun LogMealScreen(
|
|||
onSaveManualEdit: (MealEntry) -> Unit,
|
||||
onCancelEdit: () -> Unit
|
||||
) = Page {
|
||||
val context = LocalContext.current
|
||||
|
||||
var date by remember(editing?.id) { mutableStateOf(editing?.date ?: LocalDate.now().toString()) }
|
||||
var time by remember(editing?.id) { mutableStateOf(editing?.time ?: LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm"))) }
|
||||
var mealType by remember(editing?.id) { mutableStateOf(editing?.mealType ?: "Breakfast") }
|
||||
|
|
@ -38,6 +52,41 @@ fun LogMealScreen(
|
|||
var measure by remember(editing?.id) { mutableStateOf(editing?.measure ?: "") }
|
||||
var estimate by remember(editing?.id) { mutableStateOf(editing?.estimate ?: NutritionEstimate()) }
|
||||
var showImageSheet by remember { mutableStateOf(false) }
|
||||
var showDatePicker by remember { mutableStateOf(false) }
|
||||
var showTimePicker by remember { mutableStateOf(false) }
|
||||
|
||||
if (showDatePicker) {
|
||||
val cal = Calendar.getInstance()
|
||||
runCatching {
|
||||
val d = LocalDate.parse(date)
|
||||
cal.set(d.year, d.monthValue - 1, d.dayOfMonth)
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
val dialog = DatePickerDialog(
|
||||
context,
|
||||
{ _, year, month, day -> date = "%04d-%02d-%02d".format(year, month + 1, day) },
|
||||
cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)
|
||||
)
|
||||
dialog.setOnDismissListener { showDatePicker = false }
|
||||
dialog.show()
|
||||
onDispose { if (dialog.isShowing) dialog.dismiss() }
|
||||
}
|
||||
}
|
||||
|
||||
if (showTimePicker) {
|
||||
val h = time.split(":").getOrNull(0)?.toIntOrNull() ?: LocalTime.now().hour
|
||||
val m = time.split(":").getOrNull(1)?.toIntOrNull() ?: LocalTime.now().minute
|
||||
DisposableEffect(Unit) {
|
||||
val dialog = TimePickerDialog(
|
||||
context,
|
||||
{ _, hour, minute -> time = "%02d:%02d".format(hour, minute) },
|
||||
h, m, true
|
||||
)
|
||||
dialog.setOnDismissListener { showTimePicker = false }
|
||||
dialog.show()
|
||||
onDispose { if (dialog.isShowing) dialog.dismiss() }
|
||||
}
|
||||
}
|
||||
|
||||
if (showImageSheet) {
|
||||
ModalBottomSheet(onDismissRequest = { showImageSheet = false }) {
|
||||
|
|
@ -51,18 +100,12 @@ fun LogMealScreen(
|
|||
ListItem(
|
||||
headlineContent = { Text("Take photo") },
|
||||
leadingContent = { Icon(Icons.Default.CameraAlt, contentDescription = null) },
|
||||
modifier = Modifier.clickable {
|
||||
showImageSheet = false
|
||||
onTakePhoto()
|
||||
}
|
||||
modifier = Modifier.clickable { showImageSheet = false; onTakePhoto() }
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = { Text("Choose from gallery") },
|
||||
leadingContent = { Icon(Icons.Default.PhotoLibrary, contentDescription = null) },
|
||||
modifier = Modifier.clickable {
|
||||
showImageSheet = false
|
||||
onPickImage()
|
||||
}
|
||||
modifier = Modifier.clickable { showImageSheet = false; onPickImage() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -70,12 +113,42 @@ fun LogMealScreen(
|
|||
|
||||
SectionCard("Meal details") {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Field("Date", date, { date = it }, modifier = Modifier.weight(1f), placeholder = "YYYY-MM-DD")
|
||||
Field("Time", time, { time = it }, modifier = Modifier.weight(1f), placeholder = "HH:MM")
|
||||
Box(Modifier.weight(1f)) {
|
||||
OutlinedTextField(
|
||||
value = date,
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
label = { Text("Date") },
|
||||
trailingIcon = { Icon(Icons.Default.CalendarToday, contentDescription = null, modifier = Modifier.size(18.dp)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Box(Modifier.matchParentSize().clickable { showDatePicker = true })
|
||||
}
|
||||
Box(Modifier.weight(1f)) {
|
||||
OutlinedTextField(
|
||||
value = time,
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
label = { Text("Time") },
|
||||
trailingIcon = { Icon(Icons.Default.Schedule, contentDescription = null, modifier = Modifier.size(18.dp)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Box(Modifier.matchParentSize().clickable { showTimePicker = true })
|
||||
}
|
||||
}
|
||||
DropdownField("Meal type", MEAL_TYPES, mealType, { mealType = it })
|
||||
Field("Description", description, { description = it }, singleLine = false, placeholder = "e.g. grilled salmon, rice, broccoli")
|
||||
Field("Portion or measure", measure, { measure = it }, placeholder = "e.g. one plate, 450g")
|
||||
|
||||
if (selectedImageBitmap != null) {
|
||||
Image(
|
||||
bitmap = selectedImageBitmap.asImageBitmap(),
|
||||
contentDescription = "Meal photo preview",
|
||||
modifier = Modifier.fillMaxWidth().height(180.dp).clip(MaterialTheme.shapes.medium),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedButton(
|
||||
onClick = { showImageSheet = true },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
|
|
@ -83,31 +156,26 @@ fun LogMealScreen(
|
|||
Icon(
|
||||
if (selectedImageName.isNotBlank()) Icons.Default.CameraAlt else Icons.Default.PhotoLibrary,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp).padding(end = 0.dp)
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(if (selectedImageName.isNotBlank()) "Change photo" else "Add photo")
|
||||
}
|
||||
if (selectedImageName.isNotBlank()) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
selectedImageName,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
enabled = !busy && (description.isNotBlank() || selectedImageName.isNotBlank()),
|
||||
onClick = { onAnalyze(buildDraft(editing, date, time, mealType, description, measure, estimate)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) { Text(if (busy) "Analyzing..." else if (editing == null) "Analyze and save" else "Analyze again") }
|
||||
|
||||
if (status.isNotBlank()) {
|
||||
Text(
|
||||
status,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = if (status.startsWith("Analysis failed") || status.startsWith("No AI")) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary
|
||||
color = if (status.startsWith("Analysis failed") || status.startsWith("No AI") || status.startsWith("Save failed"))
|
||||
MaterialTheme.colorScheme.error
|
||||
else
|
||||
MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -140,5 +208,15 @@ fun LogMealScreen(
|
|||
}
|
||||
}
|
||||
|
||||
private fun buildDraft(editing: MealEntry?, date: String, time: String, mealType: String, description: String, measure: String, estimate: NutritionEstimate) =
|
||||
MealEntry(editing?.id.orEmpty(), date, time, mealType, description, measure, editing?.imageIncluded ?: false, editing?.imageName.orEmpty(), editing?.visionEstimate.orEmpty(), estimate)
|
||||
private fun buildDraft(
|
||||
editing: MealEntry?,
|
||||
date: String,
|
||||
time: String,
|
||||
mealType: String,
|
||||
description: String,
|
||||
measure: String,
|
||||
estimate: NutritionEstimate
|
||||
) = MealEntry(
|
||||
editing?.id.orEmpty(), date, time, mealType, description, measure,
|
||||
editing?.imageIncluded ?: false, editing?.imageName.orEmpty(), editing?.visionEstimate.orEmpty(), estimate
|
||||
)
|
||||
|
|
|
|||
4
app/src/main/res/xml/file_paths.xml
Normal file
4
app/src/main/res/xml/file_paths.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<cache-path name="camera_photos" path="." />
|
||||
</paths>
|
||||
Loading…
Reference in a new issue