diff --git a/app/.gitignore b/app/.gitignore index 42afabf..67e07b8 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1 +1,2 @@ -/build \ No newline at end of file +/build +/release diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3d7c119..dd1f51c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,55 +1,63 @@ plugins { - alias(libs.plugins.android.application) - alias(libs.plugins.kotlin.android) - alias(libs.plugins.kotlin.compose) + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) alias(libs.plugins.devtools.ksp) } android { - namespace = "xyz.pixelatedw.recipe" - compileSdk = 35 + namespace = "xyz.pixelatedw.recipe" + compileSdk = 35 - defaultConfig { - applicationId = "xyz.pixelatedw.recipe" - minSdk = 24 - targetSdk = 34 - versionCode = 1 - versionName = "1.0" + defaultConfig { + applicationId = "xyz.pixelatedw.recipe" + minSdk = 24 + targetSdk = 34 + versionCode = 2 + versionName = "1.1" - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - kotlinOptions { - jvmTarget = "11" - } - buildFeatures { - compose = true - } + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = "11" + } + + buildFeatures { + compose = true + } + + lint { + checkReleaseBuilds = false + abortOnError = false + } } dependencies { - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.lifecycle.runtime.ktx) - implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) - implementation(libs.androidx.ui) - implementation(libs.androidx.ui.graphics) - implementation(libs.androidx.ui.tooling.preview) - implementation(libs.androidx.material3) - implementation(libs.androidx.documentfile) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + 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) @@ -62,11 +70,11 @@ dependencies { testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) - androidTestImplementation(platform(libs.androidx.compose.bom)) - androidTestImplementation(libs.androidx.ui.test.junit4) + 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) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) } diff --git a/app/src/main/java/xyz/pixelatedw/recipe/MainActivity.kt b/app/src/main/java/xyz/pixelatedw/recipe/MainActivity.kt index 904842d..ab6cea4 100644 --- a/app/src/main/java/xyz/pixelatedw/recipe/MainActivity.kt +++ b/app/src/main/java/xyz/pixelatedw/recipe/MainActivity.kt @@ -11,10 +11,10 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.ui.Modifier import androidx.room.Room -import xyz.pixelatedw.recipe.ui.theme.RecipeTheme 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 import xyz.pixelatedw.recipe.utils.parseRecipes class MainActivity : ComponentActivity() { @@ -54,8 +54,7 @@ class MainActivity : ComponentActivity() { result.data?.data?.let { uri -> parseRecipes(this, uri) } - } - else { + } else { importError = "Import cancelled" } diff --git a/app/src/main/java/xyz/pixelatedw/recipe/data/RecipeWithTags.kt b/app/src/main/java/xyz/pixelatedw/recipe/data/RecipeWithTags.kt index 57a9263..b8c5cdd 100644 --- a/app/src/main/java/xyz/pixelatedw/recipe/data/RecipeWithTags.kt +++ b/app/src/main/java/xyz/pixelatedw/recipe/data/RecipeWithTags.kt @@ -37,7 +37,6 @@ data class RecipeWithTags( @Dao interface RecipeWithTagsDao { - @Transaction @Query("SELECT * FROM recipe") fun getAll(): List @@ -47,6 +46,7 @@ interface RecipeWithTagsDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(recipe: RecipeTag) + @Transaction @Query("DELETE FROM recipetag WHERE recipetag.title = :recipe") fun delete(recipe: String) } diff --git a/app/src/main/java/xyz/pixelatedw/recipe/data/Tag.kt b/app/src/main/java/xyz/pixelatedw/recipe/data/Tag.kt index 1b1779d..6174bfa 100644 --- a/app/src/main/java/xyz/pixelatedw/recipe/data/Tag.kt +++ b/app/src/main/java/xyz/pixelatedw/recipe/data/Tag.kt @@ -5,6 +5,8 @@ import androidx.room.Entity import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.PrimaryKey +import androidx.room.Query +import androidx.room.Transaction @Entity data class Tag( @@ -14,6 +16,16 @@ data class Tag( @Dao interface TagDao { + @Query("SELECT * FROM tag") + fun getAll(): List + @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(tag: Tag) + + @Query("SELECT COUNT(*) FROM tag") + fun count(): Int + + @Transaction + @Query("DELETE FROM tag WHERE tag.name = :name") + fun delete(name: String) } diff --git a/app/src/main/java/xyz/pixelatedw/recipe/ui/components/MainScreen.kt b/app/src/main/java/xyz/pixelatedw/recipe/ui/components/MainScreen.kt index 59ecd6a..80c7ad8 100644 --- a/app/src/main/java/xyz/pixelatedw/recipe/ui/components/MainScreen.kt +++ b/app/src/main/java/xyz/pixelatedw/recipe/ui/components/MainScreen.kt @@ -49,7 +49,6 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) { val search by view.search.collectAsState() val filters by view.tagFilters.collectAsState() - var activeRecipes by remember { mutableStateOf(recipes) } var activeTags = 0 val navController = rememberNavController() @@ -80,8 +79,9 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) { value = search.orEmpty(), onValueChange = { search -> view.setSearch(search) - activeRecipes = + val newRecipes = recipes.filter { filterRecipe(it, search, filters) } + view.setRecipes(newRecipes) }, ) // Tags / Delete @@ -141,10 +141,10 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) { state = gridState, ) { items( - count = activeRecipes.size, - key = { activeRecipes[it].recipe.title }, + count = recipes.size, + key = { recipes[it].recipe.title }, itemContent = { entryId -> - val entry = activeRecipes[entryId] + val entry = recipes[entryId] val isSelected = selectedEntries.contains(entry) RecipePreview(entry, isSelected, onClick = { view.setActive(entry) @@ -194,13 +194,14 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) { openTagFilterDialog = false view.reloadTagFilterState() activeTags = filters.count { f -> f.checked } - activeRecipes = recipes.filter { + val newRecipes = recipes.filter { filterRecipe( it, search, filters ) } + ctx.recipeView.setRecipes(newRecipes) }, view, ) diff --git a/app/src/main/java/xyz/pixelatedw/recipe/utils/RecipeParser.kt b/app/src/main/java/xyz/pixelatedw/recipe/utils/RecipeParser.kt index 1228567..a16b945 100644 --- a/app/src/main/java/xyz/pixelatedw/recipe/utils/RecipeParser.kt +++ b/app/src/main/java/xyz/pixelatedw/recipe/utils/RecipeParser.kt @@ -42,6 +42,14 @@ fun parseRecipeFiles(ctx: MainActivity) { reader.close() } + + // Clearing unused tags after an import/sync + val usedTags = ctx.recipeView.tagFilters.value.map { t -> t.tag } + for (tag in ctx.db.tagDao().getAll()) { + if (!usedTags.contains(tag.name)) { + ctx.db.tagDao().delete(tag.name) + } + } } fun parseDir(ctx: Context, dir: DocumentFile, path: String) { diff --git a/app/src/main/java/xyz/pixelatedw/recipe/utils/SyncRecipes.kt b/app/src/main/java/xyz/pixelatedw/recipe/utils/SyncRecipes.kt index 0c726fa..34545c1 100644 --- a/app/src/main/java/xyz/pixelatedw/recipe/utils/SyncRecipes.kt +++ b/app/src/main/java/xyz/pixelatedw/recipe/utils/SyncRecipes.kt @@ -59,11 +59,11 @@ fun sync(ctx: MainActivity, nav: NavHostController, setLoading: (Boolean) -> Uni inputStream.read(buffer) val contentBufLen = ByteBuffer.wrap(buffer).getLong().toInt() - var parentDir = ctx.filesDir + var parentDir = File(ctx.filesDir, ".sync") if (filePath.isNotEmpty()) { parentDir = File(ctx.filesDir, filePath) - parentDir.mkdirs() } + parentDir.mkdirs() val newFile = File(parentDir, fileName) @@ -94,6 +94,10 @@ fun sync(ctx: MainActivity, nav: NavHostController, setLoading: (Boolean) -> Uni parseDir(ctx, docDir, "") parseRecipeFiles(ctx) + val syncFolder = File(ctx.filesDir, ".sync") + if (syncFolder.exists()) { + syncFolder.deleteRecursively() + } } catch (e: Exception) { ctx.importError = when (e) { is SocketTimeoutException -> "Connection timed out"