Caching loaded recipes in database, copying files to app storage for easier handling and some fixes
parent
0af796082c
commit
0b2e301f8a
|
@ -2,6 +2,7 @@ plugins {
|
|||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
alias(libs.plugins.devtools.ksp)
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -49,18 +50,22 @@ dependencies {
|
|||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.documentfile)
|
||||
implementation(libs.androidx.navigation.fragment)
|
||||
implementation(libs.androidx.navigation.navigation.ui)
|
||||
implementation(libs.androidx.navigation.navigation.compose)
|
||||
implementation(libs.androidx.room)
|
||||
implementation(libs.jtoml)
|
||||
implementation(libs.commonmark)
|
||||
|
||||
ksp(libs.androidx.room.compiler)
|
||||
|
||||
testImplementation(libs.junit)
|
||||
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
|
||||
implementation(libs.androidx.navigation.fragment)
|
||||
implementation(libs.androidx.navigation.navigation.ui)
|
||||
implementation(libs.androidx.navigation.navigation.compose)
|
||||
|
||||
implementation(libs.jtoml)
|
||||
implementation(libs.commonmark)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package xyz.pixelatedw.recipe
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
|
@ -11,6 +10,8 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.room.Room
|
||||
import xyz.pixelatedw.recipe.data.AppDatabase
|
||||
import xyz.pixelatedw.recipe.data.RecipesView
|
||||
import xyz.pixelatedw.recipe.ui.components.MainScreen
|
||||
import xyz.pixelatedw.recipe.ui.theme.RecipeTheme
|
||||
|
@ -18,11 +19,18 @@ import xyz.pixelatedw.recipe.utils.getRecipes
|
|||
|
||||
class MainActivity : ComponentActivity() {
|
||||
private val recipeView: RecipesView by viewModels()
|
||||
private lateinit var db: AppDatabase
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
findSourceDir()
|
||||
db = Room.databaseBuilder(
|
||||
this,
|
||||
AppDatabase::class.java, "recipes"
|
||||
).allowMainThreadQueries().build()
|
||||
|
||||
val recipes = db.recipeWithTagsDao().getAll()
|
||||
recipeView.setRecipes(recipes)
|
||||
|
||||
enableEdgeToEdge()
|
||||
|
||||
|
@ -30,25 +38,22 @@ class MainActivity : ComponentActivity() {
|
|||
RecipeTheme {
|
||||
Surface {
|
||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||
MainScreen(innerPadding, recipeView)
|
||||
MainScreen(this, innerPadding, recipeView)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findSourceDir() {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
|
||||
val getContent =
|
||||
val sourceChooser =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
result.data?.data?.let { uri ->
|
||||
getRecipes(this, recipeView, uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
getRecipes(this, db, uri)
|
||||
|
||||
getContent.launch(intent)
|
||||
val recipes = db.recipeWithTagsDao().getAll()
|
||||
recipeView.setRecipes(recipes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package xyz.pixelatedw.recipe.data
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
|
||||
@Database(
|
||||
version = 1,
|
||||
entities = [Recipe::class, Tag::class, RecipeTag::class],
|
||||
)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun recipeWithTagsDao(): RecipeWithTagsDao
|
||||
|
||||
abstract fun recipeDao(): RecipeDao
|
||||
|
||||
abstract fun tagDao(): TagDao
|
||||
}
|
|
@ -1,15 +1,41 @@
|
|||
package xyz.pixelatedw.recipe.data
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import androidx.core.net.toUri
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.File
|
||||
|
||||
@Entity
|
||||
data class Recipe(
|
||||
@PrimaryKey
|
||||
val title: String,
|
||||
val tags: List<String>,
|
||||
val previews: List<Bitmap>,
|
||||
val preview: String?,
|
||||
val lastModified: Long,
|
||||
val content: String
|
||||
) {
|
||||
fun mainImage(): Bitmap? {
|
||||
return this.previews.getOrNull(0)
|
||||
fun mainImage(ctx: Context): Bitmap? {
|
||||
if (this.preview != null) {
|
||||
val file = File(ctx.filesDir, this.preview)
|
||||
if (file.exists()) {
|
||||
ctx.contentResolver.openInputStream(file.toUri()).use {
|
||||
val bitmap: Bitmap? = BitmapFactory.decodeStream(it)
|
||||
return bitmap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@Dao
|
||||
interface RecipeDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insert(recipe: Recipe)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package xyz.pixelatedw.recipe.data
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Junction
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Relation
|
||||
import androidx.room.Transaction
|
||||
|
||||
@Entity(primaryKeys = ["title", "name"])
|
||||
data class RecipeTag(
|
||||
val title: String,
|
||||
val name: String
|
||||
)
|
||||
|
||||
data class RecipeWithTags(
|
||||
@Embedded
|
||||
val recipe: Recipe,
|
||||
|
||||
@Relation(
|
||||
parentColumn = "title",
|
||||
entity = Tag::class,
|
||||
entityColumn = "name",
|
||||
associateBy = Junction(
|
||||
value = RecipeTag::class,
|
||||
parentColumn = "title",
|
||||
entityColumn = "name"
|
||||
)
|
||||
)
|
||||
val tags: List<Tag>
|
||||
)
|
||||
|
||||
@Dao
|
||||
interface RecipeWithTagsDao {
|
||||
@Transaction
|
||||
@Query("SELECT * FROM recipe")
|
||||
fun getAll(): List<RecipeWithTags>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insert(recipe: RecipeTag)
|
||||
}
|
|
@ -6,26 +6,24 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||
import kotlinx.coroutines.flow.update
|
||||
|
||||
class RecipesView : ViewModel() {
|
||||
private val _activeRecipe = MutableStateFlow<Recipe?>( null )
|
||||
private val _activeRecipe = MutableStateFlow<RecipeWithTags?>( null )
|
||||
val activeRecipe = _activeRecipe.asStateFlow()
|
||||
|
||||
private val _recipes = MutableStateFlow<List<Recipe>>( arrayListOf() )
|
||||
private val _recipes = MutableStateFlow<List<RecipeWithTags>>( arrayListOf() )
|
||||
val recipes = _recipes.asStateFlow()
|
||||
|
||||
private val _search = MutableStateFlow<String?>(null)
|
||||
val search = _search.asStateFlow()
|
||||
|
||||
fun setRecipes(recipes: List<RecipeWithTags>) {
|
||||
_recipes.update { recipes }
|
||||
}
|
||||
|
||||
fun setSearch(search: String) {
|
||||
_search.update { search }
|
||||
}
|
||||
|
||||
fun addRecipe(recipe: Recipe) {
|
||||
_recipes.update {
|
||||
it + recipe
|
||||
}
|
||||
}
|
||||
|
||||
fun setActive(recipe: Recipe) {
|
||||
fun setActive(recipe: RecipeWithTags) {
|
||||
_activeRecipe.update { recipe }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package xyz.pixelatedw.recipe.data
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity
|
||||
data class Tag(
|
||||
@PrimaryKey val name: String
|
||||
)
|
||||
|
||||
@Dao
|
||||
interface TagDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insert(tag: Tag)
|
||||
}
|
|
@ -1,34 +1,44 @@
|
|||
package xyz.pixelatedw.recipe.ui.components
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import xyz.pixelatedw.recipe.data.Recipe
|
||||
import xyz.pixelatedw.recipe.MainActivity
|
||||
import xyz.pixelatedw.recipe.data.RecipeWithTags
|
||||
import xyz.pixelatedw.recipe.data.RecipesView
|
||||
|
||||
@Composable
|
||||
fun MainScreen(padding: PaddingValues, view: RecipesView) {
|
||||
fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
|
||||
val recipes = view.recipes.collectAsState()
|
||||
val active = view.activeRecipe.collectAsState()
|
||||
val search = view.search.collectAsState()
|
||||
|
||||
val navController = rememberNavController()
|
||||
|
||||
val isInSearch = isInSearch@{ recipe: Recipe ->
|
||||
val hasTitle = recipe.title.contains(search.value.orEmpty(), ignoreCase = true)
|
||||
val hasTags = recipe.tags.isNotEmpty() && recipe.tags.stream()
|
||||
.filter { tag -> tag.contains(search.value.orEmpty(), ignoreCase = true) }.count() > 0
|
||||
val isInSearch = isInSearch@{ entry: RecipeWithTags ->
|
||||
val hasTitle = entry.recipe.title.contains(search.value.orEmpty(), ignoreCase = true)
|
||||
val hasTags = entry.tags.isNotEmpty() && entry.tags.stream()
|
||||
.filter { tag -> tag.name.contains(search.value.orEmpty(), ignoreCase = true) }
|
||||
.count() > 0
|
||||
|
||||
hasTitle || hasTags
|
||||
}
|
||||
|
@ -39,18 +49,33 @@ fun MainScreen(padding: PaddingValues, view: RecipesView) {
|
|||
) {
|
||||
composable("list") {
|
||||
Column(modifier = Modifier.padding(padding)) {
|
||||
OutlinedTextField(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = search.value.orEmpty(),
|
||||
onValueChange = { search -> view.setSearch(search) })
|
||||
onValueChange = { search -> view.setSearch(search) },
|
||||
)
|
||||
Button(
|
||||
onClick = { ctx.sourceChooser.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)) },
|
||||
) {
|
||||
Text(
|
||||
text = "Load",
|
||||
maxLines = 1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
LazyColumn {
|
||||
items(recipes.value) { recipe ->
|
||||
if (isInSearch(recipe)) {
|
||||
val previewUri = recipe.mainImage()
|
||||
RecipePreview(recipe, previewUri, onClick = {
|
||||
view.setActive(recipe)
|
||||
items(recipes.value) { entry ->
|
||||
if (isInSearch(entry)) {
|
||||
val previewUri = entry.recipe.mainImage(LocalContext.current)
|
||||
RecipePreview(entry, previewUri, onClick = {
|
||||
view.setActive(entry)
|
||||
navController.navigate("info")
|
||||
})
|
||||
}
|
||||
|
|
|
@ -16,10 +16,11 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import xyz.pixelatedw.recipe.data.Recipe
|
||||
import xyz.pixelatedw.recipe.data.RecipeWithTags
|
||||
import xyz.pixelatedw.recipe.utils.parseMarkdown
|
||||
|
||||
@Composable
|
||||
fun RecipeInfo(padding: PaddingValues, active: Recipe) {
|
||||
fun RecipeInfo(padding: PaddingValues, active: RecipeWithTags) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
|
@ -30,7 +31,7 @@ fun RecipeInfo(padding: PaddingValues, active: Recipe) {
|
|||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = active.title,
|
||||
text = active.recipe.title,
|
||||
style = MaterialTheme.typography.displayLarge,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
@ -40,7 +41,7 @@ fun RecipeInfo(padding: PaddingValues, active: Recipe) {
|
|||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
val annotatedString =
|
||||
parseMarkdown(active.content, MaterialTheme.typography)
|
||||
parseMarkdown(active.recipe.content, MaterialTheme.typography)
|
||||
Text(
|
||||
text = annotatedString,
|
||||
modifier = Modifier.padding(16.dp),
|
||||
|
|
|
@ -20,9 +20,10 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.em
|
||||
import xyz.pixelatedw.recipe.data.Recipe
|
||||
import xyz.pixelatedw.recipe.data.RecipeWithTags
|
||||
|
||||
@Composable
|
||||
fun RecipePreview(recipe: Recipe, previewUri: Bitmap?, onClick: () -> Unit) {
|
||||
fun RecipePreview(entry: RecipeWithTags, previewUri: Bitmap?, onClick: () -> Unit) {
|
||||
Column(modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.clickable(onClick = onClick)) {
|
||||
|
@ -38,7 +39,7 @@ fun RecipePreview(recipe: Recipe, previewUri: Bitmap?, onClick: () -> Unit) {
|
|||
}
|
||||
Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = recipe.title,
|
||||
text = entry.recipe.title,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = TextStyle(
|
||||
textAlign = TextAlign.Center,
|
||||
|
@ -47,7 +48,7 @@ fun RecipePreview(recipe: Recipe, previewUri: Bitmap?, onClick: () -> Unit) {
|
|||
)
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp)) {
|
||||
for (tag in recipe.tags) {
|
||||
for (tag in entry.tags) {
|
||||
Tag(tag)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,10 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import xyz.pixelatedw.recipe.data.Tag
|
||||
|
||||
@Composable
|
||||
fun Tag(tag: String) {
|
||||
fun Tag(tag: Tag) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(start = 8.dp)
|
||||
|
@ -21,7 +22,7 @@ fun Tag(tag: String) {
|
|||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 8.dp, end = 8.dp),
|
||||
text = tag
|
||||
text = tag.name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,61 +1,56 @@
|
|||
package xyz.pixelatedw.recipe.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import androidx.core.text.trimmedLength
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import io.github.wasabithumb.jtoml.JToml
|
||||
import xyz.pixelatedw.recipe.data.AppDatabase
|
||||
import xyz.pixelatedw.recipe.data.Recipe
|
||||
import xyz.pixelatedw.recipe.data.RecipesView
|
||||
import xyz.pixelatedw.recipe.data.RecipeTag
|
||||
import xyz.pixelatedw.recipe.data.Tag
|
||||
import java.io.BufferedReader
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.InputStreamReader
|
||||
|
||||
private val previews = HashMap<String, Bitmap>()
|
||||
private val recipeFiles = mutableListOf<DocumentFile>()
|
||||
|
||||
fun getRecipes(ctx: Context, view: RecipesView, uri: Uri?) {
|
||||
fun getRecipes(ctx: Context, db: AppDatabase, uri: Uri?) {
|
||||
if (uri == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val path = ""
|
||||
|
||||
val dir = DocumentFile.fromTreeUri(ctx, uri)
|
||||
if (dir != null) {
|
||||
parseDir(ctx, dir)
|
||||
parseDir(ctx, dir, path)
|
||||
|
||||
for (file in recipeFiles) {
|
||||
val recipe = parseRecipe(ctx, file)
|
||||
if (recipe != null) {
|
||||
view.addRecipe(recipe)
|
||||
}
|
||||
parseRecipe(ctx, db, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseDir(ctx: Context, dir: DocumentFile) {
|
||||
fun parseDir(ctx: Context, dir: DocumentFile, path: String) {
|
||||
val fileList: Array<DocumentFile> = dir.listFiles()
|
||||
for (file in fileList) {
|
||||
if (file.isDirectory) {
|
||||
parseDir(ctx, file)
|
||||
parseDir(ctx, file, path + File.separator + file.name)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file.isFile && file.name?.endsWith(".jpg") == true) {
|
||||
val inStream = ctx.contentResolver.openInputStream(file.uri)
|
||||
val picsDir = File(ctx.filesDir, path)
|
||||
picsDir.mkdirs()
|
||||
|
||||
var bitmap: Bitmap? = null
|
||||
if (file.exists()) {
|
||||
try {
|
||||
bitmap = BitmapFactory.decodeStream(inStream)
|
||||
}
|
||||
finally {
|
||||
inStream?.close()
|
||||
}
|
||||
}
|
||||
val newFile = File(picsDir, file.name!!)
|
||||
|
||||
if (bitmap != null) {
|
||||
previews[file.name!!] = bitmap
|
||||
ctx.contentResolver.openInputStream(file.uri).use { inStream ->
|
||||
FileOutputStream(newFile, false).use { outStream ->
|
||||
inStream?.copyTo(outStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (file.isFile && file.name?.endsWith(".md") == true) {
|
||||
|
@ -64,7 +59,7 @@ fun parseDir(ctx: Context, dir: DocumentFile) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun parseRecipe(ctx: Context, file: DocumentFile): Recipe? {
|
||||
private fun parseRecipe(ctx: Context, db: AppDatabase, file: DocumentFile) {
|
||||
val lastModified = file.lastModified()
|
||||
|
||||
val inputStream = ctx.contentResolver.openInputStream(file.uri)
|
||||
|
@ -75,9 +70,8 @@ private fun parseRecipe(ctx: Context, file: DocumentFile): Recipe? {
|
|||
val sb = StringBuilder()
|
||||
var hasToml = false
|
||||
var frontMatterSize = 0
|
||||
// TODO This could use some improvements as it always assumes frontmatter is the very first thing in the file
|
||||
for (i in 0..lines.size) {
|
||||
val line = lines[i]
|
||||
|
||||
for (line in lines) {
|
||||
frontMatterSize += line.trimmedLength() + 1
|
||||
|
||||
if (line == "+++") {
|
||||
|
@ -91,38 +85,57 @@ private fun parseRecipe(ctx: Context, file: DocumentFile): Recipe? {
|
|||
sb.appendLine(line)
|
||||
}
|
||||
|
||||
if (!hasToml) {
|
||||
reader.close()
|
||||
return
|
||||
}
|
||||
|
||||
val toml = JToml.jToml()
|
||||
val doc = toml.readFromString(sb.toString())
|
||||
|
||||
val tags = arrayListOf<String>()
|
||||
for (tomlElem in doc["tags"]!!.asArray()) {
|
||||
tags.add(tomlElem!!.asPrimitive().asString())
|
||||
if (!doc.contains("title")) {
|
||||
reader.close()
|
||||
return
|
||||
}
|
||||
|
||||
val recipeTitle = doc["title"]!!.asPrimitive().asString()
|
||||
|
||||
val pics = arrayListOf<String>()
|
||||
if (doc.contains("pics")) {
|
||||
for (tomlElem in doc["pics"]!!.asArray()) {
|
||||
pics.add(tomlElem!!.asPrimitive().asString())
|
||||
}
|
||||
|
||||
val recipePreviews = mutableListOf<Bitmap>()
|
||||
for (pic in pics) {
|
||||
val bitmap = previews[pic]
|
||||
if (bitmap != null) {
|
||||
recipePreviews.add(bitmap)
|
||||
}
|
||||
|
||||
val tags = arrayListOf<Tag>()
|
||||
if (doc.contains("tags")) {
|
||||
for (tomlElem in doc["tags"]!!.asArray()) {
|
||||
val tag = Tag(tomlElem!!.asPrimitive().asString())
|
||||
tags.add(tag)
|
||||
|
||||
val recipeWithTags = RecipeTag(recipeTitle, tag.name)
|
||||
db.tagDao().insert(tag)
|
||||
db.recipeWithTagsDao().insert(recipeWithTags)
|
||||
}
|
||||
}
|
||||
|
||||
var recipePreview: String? = null
|
||||
for (pic in pics) {
|
||||
recipePreview = pic
|
||||
}
|
||||
|
||||
val content = text.substring(frontMatterSize..<text.length)
|
||||
|
||||
val recipe = Recipe(
|
||||
title = doc["title"]!!.asPrimitive().asString(),
|
||||
tags = tags,
|
||||
previews = recipePreviews,
|
||||
title = recipeTitle,
|
||||
preview = recipePreview,
|
||||
lastModified = lastModified,
|
||||
content = content
|
||||
)
|
||||
|
||||
reader.close()
|
||||
db.recipeDao().insert(recipe)
|
||||
|
||||
return recipe
|
||||
println(recipe)
|
||||
|
||||
reader.close()
|
||||
}
|
||||
|
|
|
@ -3,4 +3,5 @@ plugins {
|
|||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.kotlin.android) apply false
|
||||
alias(libs.plugins.kotlin.compose) apply false
|
||||
alias(libs.plugins.devtools.ksp) apply false
|
||||
}
|
||||
|
|
|
@ -12,9 +12,13 @@ composeBom = "2025.07.00"
|
|||
documentfile = "1.1.0"
|
||||
commonmark = "0.25.1"
|
||||
navVersion = "2.9.3"
|
||||
room = "2.7.2"
|
||||
roomCompiler = "2.7.2"
|
||||
ksp = "2.0.21-1.0.27"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomCompiler" }
|
||||
jtoml = { group = "io.github.wasabithumb", name = "jtoml", version.ref = "jtoml" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
|
@ -34,9 +38,10 @@ commonmark = { group = "org.commonmark", name = "commonmark", version.ref = "com
|
|||
androidx-navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navVersion"}
|
||||
androidx-navigation-navigation-ui = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navVersion"}
|
||||
androidx-navigation-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navVersion"}
|
||||
androidx-room = { group = "androidx.room", name = "room-runtime", version.ref = "room"}
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
|
||||
devtools-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp"}
|
||||
|
|
Loading…
Reference in New Issue