From 2d51310c0873d0f0e595f796b27b8807d59cc6a2 Mon Sep 17 00:00:00 2001 From: machiav3lli Date: Fri, 15 Oct 2021 00:47:46 +0200 Subject: [PATCH] Add: Simple DAOs (4/5 in replacing SQLite with Room) --- .../com/looker/droidify/database/DAOs.kt | 89 +++++++++++++++++++ .../com/looker/droidify/database/Database.kt | 36 +++++++- .../com/looker/droidify/database/DatabaseX.kt | 21 ++++- .../com/looker/droidify/database/Tables.kt | 7 ++ .../looker/droidify/entity/InstalledItem.kt | 1 + 5 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/com/looker/droidify/database/DAOs.kt diff --git a/src/main/kotlin/com/looker/droidify/database/DAOs.kt b/src/main/kotlin/com/looker/droidify/database/DAOs.kt new file mode 100644 index 00000000..d332a6a0 --- /dev/null +++ b/src/main/kotlin/com/looker/droidify/database/DAOs.kt @@ -0,0 +1,89 @@ +package com.looker.droidify.database + +import android.database.SQLException +import androidx.room.* + +@Dao +interface RepositoryDao { + @Insert + @Throws(SQLException::class) + fun insert(vararg repository: Repository) + + @Update(onConflict = OnConflictStrategy.REPLACE) + fun update(vararg repository: Repository?) + + fun put(repository: Repository) { + if (repository.id >= 0L) update(repository) else insert(repository) + } + + @Query("SELECT * FROM repository WHERE _id = :id and deleted == 0") + fun get(id: Long): Repository? + + @get:Query("SELECT * FROM repository WHERE deleted == 0 ORDER BY _id ASC") + val all: List + + @get:Query("SELECT _id, deleted FROM repository WHERE deleted != 0 and enabled == 0 ORDER BY _id ASC") + val allDisabledDeleted: List + + @Delete + fun delete(repository: Repository) + + @Query("DELETE FROM repository WHERE _id = :id") + fun deleteById(vararg id: Long): Int + + @Update(onConflict = OnConflictStrategy.REPLACE) + fun markAsDeleted(id: Long) { + update(get(id).apply { this?.deleted = 1 }) + } +} + +@Dao +interface ProductDao { + @Query("SELECT COUNT(*) FROM product WHERE repository_id = :id") + fun countForRepository(id: Long): Long + + @Query("SELECT * FROM product WHERE package_name = :packageName") + fun get(packageName: String): Product? + + @Query("DELETE FROM product WHERE repository_id = :id") + fun deleteById(vararg id: Long): Int +} + +@Dao +interface CategoryDao { + @Query( + """SELECT DISTINCT category.name + FROM category AS category + JOIN repository AS repository + ON category.repository_id = repository._id + WHERE repository.enabled != 0 AND + repository.deleted == 0""" + ) + fun getAll(): List + + @Query("DELETE FROM category WHERE repository_id = :id") + fun deleteById(vararg id: Long): Int +} + +@Dao +interface InstalledDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + @Throws(SQLException::class) + fun insert(vararg installed: Installed) + + @Query("SELECT * FROM installed WHERE package_name = :packageName") + fun get(packageName: String): Installed? + + @Query("DELETE FROM installed WHERE package_name = :packageName") + fun delete(packageName: String) +} + +@Dao +interface LockDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + @Throws(SQLException::class) + fun insert(vararg lock: Lock) + + @Query("DELETE FROM lock WHERE package_name = :packageName") + fun delete(packageName: String) +} \ No newline at end of file diff --git a/src/main/kotlin/com/looker/droidify/database/Database.kt b/src/main/kotlin/com/looker/droidify/database/Database.kt index d083d7f4..61aef765 100644 --- a/src/main/kotlin/com/looker/droidify/database/Database.kt +++ b/src/main/kotlin/com/looker/droidify/database/Database.kt @@ -351,6 +351,7 @@ object Database { } object RepositoryAdapter { + // Done in put internal fun putWithoutNotification(repository: Repository, shouldReplace: Boolean): Long { return db.insertOrReplace(shouldReplace, Schema.Repository.name, ContentValues().apply { if (shouldReplace) { @@ -362,6 +363,7 @@ object Database { }) } + // Done fun put(repository: Repository): Repository { val shouldReplace = repository.id >= 0L val newId = putWithoutNotification(repository, shouldReplace) @@ -370,6 +372,7 @@ object Database { return if (newId != repository.id) repository.copy(id = newId) else repository } + // Done fun get(id: Long): Repository? { return db.query( Schema.Repository.name, @@ -381,6 +384,8 @@ object Database { .use { it.firstOrNull()?.let(::transform) } } + // Done + // MAYBE signal has to be considered fun getAll(signal: CancellationSignal?): List { return db.query( Schema.Repository.name, @@ -389,6 +394,8 @@ object Database { ).use { it.asSequence().map(::transform).toList() } } + // Done Pair instead + // MAYBE signal has to be considered fun getAllDisabledDeleted(signal: CancellationSignal?): Set> { return db.query( Schema.Repository.name, @@ -408,6 +415,7 @@ object Database { } } + // Done fun markAsDeleted(id: Long) { db.update(Schema.Repository.name, ContentValues().apply { put(Schema.Repository.ROW_DELETED, 1) @@ -415,6 +423,7 @@ object Database { notifyChanged(Subject.Repositories, Subject.Repository(id), Subject.Products) } + // Done fun cleanup(pairs: Set>) { val result = pairs.windowed(10, 10, true).map { val idsString = it.joinToString(separator = ", ") { it.first.toString() } @@ -442,6 +451,7 @@ object Database { } } + // get the cursor in the specific table. Unnecessary with Room fun query(signal: CancellationSignal?): Cursor { return db.query( Schema.Repository.name, @@ -450,6 +460,7 @@ object Database { ).observable(Subject.Repositories) } + // Unnecessary with Room fun transform(cursor: Cursor): Repository { return cursor.getBlob(cursor.getColumnIndex(Schema.Repository.ROW_DATA)) .jsonParse { @@ -461,6 +472,7 @@ object Database { } object ProductAdapter { + // Done fun get(packageName: String, signal: CancellationSignal?): List { return db.query( Schema.Product.name, @@ -474,6 +486,7 @@ object Database { ).use { it.asSequence().map(::transform).toList() } } + // Done fun getCount(repositoryId: Long): Int { return db.query( Schema.Product.name, columns = arrayOf("COUNT (*)"), @@ -485,6 +498,7 @@ object Database { .use { it.firstOrNull()?.getInt(0) ?: 0 } } + // Complex left to wiring phase fun query( installed: Boolean, updates: Boolean, searchQuery: String, section: ProductItem.Section, order: ProductItem.Order, signal: CancellationSignal? @@ -517,16 +531,17 @@ object Database { builder += """MAX((product.${Schema.Product.ROW_COMPATIBLE} AND (installed.${Schema.Installed.ROW_SIGNATURE} IS NULL OR $signatureMatches)) || PRINTF('%016X', product.${Schema.Product.ROW_VERSION_CODE})) FROM ${Schema.Product.name} AS product""" - builder += """JOIN ${Schema.Repository.name} AS repository ON product.${Schema.Product.ROW_REPOSITORY_ID} = repository.${Schema.Repository.ROW_ID}""" builder += """LEFT JOIN ${Schema.Lock.name} AS lock ON product.${Schema.Product.ROW_PACKAGE_NAME} = lock.${Schema.Lock.ROW_PACKAGE_NAME}""" + if (!installed && !updates) { builder += "LEFT" } builder += """JOIN ${Schema.Installed.name} AS installed ON product.${Schema.Product.ROW_PACKAGE_NAME} = installed.${Schema.Installed.ROW_PACKAGE_NAME}""" + if (section is ProductItem.Section.Category) { builder += """JOIN ${Schema.Category.name} AS category ON product.${Schema.Product.ROW_PACKAGE_NAME} = category.${Schema.Product.ROW_PACKAGE_NAME}""" @@ -534,6 +549,7 @@ object Database { builder += """WHERE repository.${Schema.Repository.ROW_ENABLED} != 0 AND repository.${Schema.Repository.ROW_DELETED} == 0""" + if (section is ProductItem.Section.Category) { builder += "AND category.${Schema.Category.ROW_NAME} = ?" builder %= section.name @@ -541,18 +557,22 @@ object Database { builder += "AND product.${Schema.Product.ROW_REPOSITORY_ID} = ?" builder %= section.id.toString() } + if (searchQuery.isNotEmpty()) { builder += """AND ${Schema.Synthetic.ROW_MATCH_RANK} > 0""" } builder += "GROUP BY product.${Schema.Product.ROW_PACKAGE_NAME} HAVING 1" + if (updates) { builder += "AND ${Schema.Synthetic.ROW_CAN_UPDATE}" } builder += "ORDER BY" + if (searchQuery.isNotEmpty()) { builder += """${Schema.Synthetic.ROW_MATCH_RANK} DESC,""" } + when (order) { ProductItem.Order.NAME -> Unit ProductItem.Order.DATE_ADDED -> builder += "product.${Schema.Product.ROW_ADDED} DESC," @@ -563,6 +583,7 @@ object Database { return builder.query(db, signal).observable(Subject.Products) } + // Unnecessary with Room private fun transform(cursor: Cursor): Product { return cursor.getBlob(cursor.getColumnIndex(Schema.Product.ROW_DATA)) .jsonParse { @@ -575,6 +596,7 @@ object Database { } } + // Unnecessary with Room fun transformItem(cursor: Cursor): ProductItem { return cursor.getBlob(cursor.getColumnIndex(Schema.Product.ROW_DATA_ITEM)) .jsonParse { @@ -602,6 +624,7 @@ object Database { } object CategoryAdapter { + // Done fun getAll(signal: CancellationSignal?): Set { val builder = QueryBuilder() @@ -620,6 +643,7 @@ object Database { } object InstalledAdapter { + // Done fun get(packageName: String, signal: CancellationSignal?): InstalledItem? { return db.query( Schema.Installed.name, @@ -632,6 +656,7 @@ object Database { ).use { it.firstOrNull()?.let(::transform) } } + // Done in insert private fun put(installedItem: InstalledItem, notify: Boolean) { db.insertOrReplace(true, Schema.Installed.name, ContentValues().apply { put(Schema.Installed.ROW_PACKAGE_NAME, installedItem.packageName) @@ -644,8 +669,10 @@ object Database { } } + // Done in insert fun put(installedItem: InstalledItem) = put(installedItem, true) + // Done in insert fun putAll(installedItems: List) { db.beginTransaction() try { @@ -657,6 +684,7 @@ object Database { } } + // Done fun delete(packageName: String) { val count = db.delete( Schema.Installed.name, @@ -668,6 +696,7 @@ object Database { } } + // Unnecessary with Room private fun transform(cursor: Cursor): InstalledItem { return InstalledItem( cursor.getString(cursor.getColumnIndex(Schema.Installed.ROW_PACKAGE_NAME)), @@ -679,6 +708,7 @@ object Database { } object LockAdapter { + // Done in insert (Lock object instead of pair) private fun put(lock: Pair, notify: Boolean) { db.insertOrReplace(true, Schema.Lock.name, ContentValues().apply { put(Schema.Lock.ROW_PACKAGE_NAME, lock.first) @@ -689,8 +719,10 @@ object Database { } } + // Done in insert (Lock object instead of pair) fun put(lock: Pair) = put(lock, true) + // Done in insert (Lock object instead of pair) fun putAll(locks: List>) { db.beginTransaction() try { @@ -702,12 +734,14 @@ object Database { } } + // Done fun delete(packageName: String) { db.delete(Schema.Lock.name, "${Schema.Lock.ROW_PACKAGE_NAME} = ?", arrayOf(packageName)) notifyChanged(Subject.Products) } } + // TODO add temporary tables object UpdaterAdapter { private val Table.temporaryName: String get() = "${name}_temporary" diff --git a/src/main/kotlin/com/looker/droidify/database/DatabaseX.kt b/src/main/kotlin/com/looker/droidify/database/DatabaseX.kt index 341667cb..2a8e02ea 100644 --- a/src/main/kotlin/com/looker/droidify/database/DatabaseX.kt +++ b/src/main/kotlin/com/looker/droidify/database/DatabaseX.kt @@ -17,7 +17,11 @@ import androidx.room.TypeConverters ) @TypeConverters(Converters::class) abstract class DatabaseX : RoomDatabase() { - // TODO add the DAOs for the tables + abstract val repositoryDao: RepositoryDao + abstract val productDao: ProductDao + abstract val categoryDao: CategoryDao + abstract val installedDao: InstalledDao + abstract val lockDao: LockDao companion object { @Volatile @@ -40,4 +44,19 @@ abstract class DatabaseX : RoomDatabase() { } } } + + fun cleanUp(pairs: Set>) { + val result = pairs.windowed(10, 10, true).map { + val ids = it.map { it.first }.toLongArray() + val productsCount = productDao.deleteById(*ids) + val categoriesCount = categoryDao.deleteById(*ids) + val deleteIds = it.filter { it.second }.map { it.first }.toLongArray() + repositoryDao.deleteById(*deleteIds) + productsCount != 0 || categoriesCount != 0 + } + // Use live objects and observers instead + /*if (result.any { it }) { + com.looker.droidify.database.Database.notifyChanged(com.looker.droidify.database.Database.Subject.Products) + }*/ + } } \ No newline at end of file diff --git a/src/main/kotlin/com/looker/droidify/database/Tables.kt b/src/main/kotlin/com/looker/droidify/database/Tables.kt index c8dde010..7c7da41f 100644 --- a/src/main/kotlin/com/looker/droidify/database/Tables.kt +++ b/src/main/kotlin/com/looker/droidify/database/Tables.kt @@ -21,6 +21,13 @@ class Repository { @ColumnInfo(typeAffinity = ColumnInfo.BLOB) var data: Repository? = null + + class IdAndDeleted { + @ColumnInfo(name = "_id") + var id = 0L + + var deleted = 0 + } } @Entity(primaryKeys = ["repository_id", "package_name"]) diff --git a/src/main/kotlin/com/looker/droidify/entity/InstalledItem.kt b/src/main/kotlin/com/looker/droidify/entity/InstalledItem.kt index c01bab14..a74d0668 100644 --- a/src/main/kotlin/com/looker/droidify/entity/InstalledItem.kt +++ b/src/main/kotlin/com/looker/droidify/entity/InstalledItem.kt @@ -1,5 +1,6 @@ package com.looker.droidify.entity +// Redundant to Room's Installed class InstalledItem( val packageName: String, val version: String,