Deleting images from the file system when the recipe gets deleted and a nice fade animation for previews with multiple images

master
Wynd 2025-12-21 14:53:15 +02:00
parent 519bbd4a89
commit 06949f0f8d
3 changed files with 93 additions and 32 deletions

View File

@ -25,8 +25,8 @@ data class Recipe(
val content: String,
val hash: Int
) {
fun previewImage(ctx: Context): Bitmap? {
return showImage(ctx, 0)
fun previewImage(ctx: Context, idx: Int): Bitmap? {
return showImage(ctx, idx)
}
fun showImage(ctx: Context, idx: Int): Bitmap? {

View File

@ -33,6 +33,7 @@ import xyz.pixelatedw.recipe.MainActivity
import xyz.pixelatedw.recipe.R
import xyz.pixelatedw.recipe.data.RecipeWithTags
import xyz.pixelatedw.recipe.data.RecipesView
import java.io.File
@Composable
fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
@ -140,9 +141,8 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
LazyColumn {
items(recipes.value) { entry ->
if (isInSearch(entry)) {
val previewUri = entry.recipe.previewImage(LocalContext.current)
val isSelected = selectedEntries.value.contains(entry)
RecipePreview(entry, previewUri, isSelected, onClick = {
RecipePreview(entry, isSelected, onClick = {
view.setActive(entry)
navController.navigate("info")
}, onSelected = { flag ->
@ -167,6 +167,13 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
// TODO I feel like this could be done in batch or something...but I truly don't care atm
for (entry in selectedEntries.value) {
view.removeRecipe(entry)
// TODO This needs some refcounting or database stuff so it doesn't delete pics if they're used by multiple recipes, however this is not a problem atm
// Make sure to delete the pics from phone's storage so they don't waste space
for (picPath in entry.recipe.pics) {
File(ctx.filesDir, picPath).delete()
}
ctx.db.recipeDao().delete(entry.recipe)
ctx.db.recipeWithTagsDao().delete(entry.recipe.title)
}

View File

@ -1,9 +1,12 @@
package xyz.pixelatedw.recipe.ui.components
import android.graphics.Bitmap
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@ -14,52 +17,98 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.imageResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.em
import kotlinx.coroutines.delay
import xyz.pixelatedw.recipe.R
import xyz.pixelatedw.recipe.data.Recipe
import xyz.pixelatedw.recipe.data.RecipeWithTags
import kotlin.time.Duration.Companion.seconds
@Composable
fun RecipePreview(entry: RecipeWithTags, previewUri: Bitmap?, isSelected: Boolean, onClick: () -> Unit, onSelected: (Boolean) -> Unit) {
Column(modifier = Modifier
fun RecipePreview(
entry: RecipeWithTags,
isSelected: Boolean,
onClick: () -> Unit,
onSelected: (Boolean) -> Unit
) {
val availablePics = entry.recipe.pics.size
var displayImageId by remember { mutableIntStateOf(0) }
if (availablePics > 0) {
LaunchedEffect(Unit) {
while (true) {
delay(5.seconds)
displayImageId = (displayImageId + 1) % availablePics
}
}
}
val displayImage = entry.recipe.previewImage(LocalContext.current, displayImageId)
Column(
modifier = Modifier
.background(color = if (isSelected) Color(0x11FF0000) else Color(0x00FFFFFF))
.padding(8.dp)
.combinedClickable(
onLongClick = { onSelected(!isSelected) },
onClick = onClick
)) {
Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth().height(256.dp)) {
if (previewUri != null) {
)
) {
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.height(256.dp)
) {
if (displayImage != null) {
AnimatedContent(
displayImage,
label = "Recipe Preview",
transitionSpec = {
fadeIn(animationSpec = tween(500))
.togetherWith(fadeOut(animationSpec = tween(500)))
}
) { targetImage ->
Image(
bitmap = previewUri.asImageBitmap(),
bitmap = targetImage.asImageBitmap(),
contentDescription = "Recipe image",
contentScale = ContentScale.Crop,
modifier = Modifier.size(256.dp).padding(top = 16.dp, bottom = 8.dp),
modifier = Modifier
.size(256.dp)
.padding(top = 16.dp, bottom = 8.dp),
)
}
else {
} else {
Image(
bitmap = ImageBitmap.imageResource(R.drawable.missing_image),
contentDescription = "Missing recipe image",
contentScale = ContentScale.Crop,
modifier = Modifier.size(256.dp).padding(top = 16.dp, bottom = 8.dp),
modifier = Modifier
.size(256.dp)
.padding(top = 16.dp, bottom = 8.dp),
)
}
}
Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp)) {
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 8.dp)
) {
Text(
text = entry.recipe.title,
modifier = Modifier.fillMaxWidth(),
@ -69,7 +118,12 @@ fun RecipePreview(entry: RecipeWithTags, previewUri: Bitmap?, isSelected: Boolea
)
)
}
Row(horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp)) {
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp)
) {
for (tag in entry.tags) {
Tag(tag)
}