This commit is contained in:
Felitendo
2025-05-20 15:28:05 +02:00
parent ddff25a7c4
commit a081d24035
289 changed files with 20897 additions and 1468 deletions

View File

@@ -9,18 +9,18 @@ import android.os.CancellationSignal
import androidx.core.database.sqlite.transaction
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.looker.core.common.extension.Json
import com.looker.core.common.extension.asSequence
import com.looker.core.common.extension.firstOrNull
import com.looker.core.common.extension.parseDictionary
import com.looker.core.common.extension.writeDictionary
import com.looker.core.common.log
import com.looker.core.datastore.model.SortOrder
import com.looker.droidify.BuildConfig
import com.looker.droidify.datastore.model.SortOrder
import com.looker.droidify.model.InstalledItem
import com.looker.droidify.model.Product
import com.looker.droidify.model.ProductItem
import com.looker.droidify.model.Repository
import com.looker.droidify.BuildConfig
import com.looker.droidify.utility.common.extension.Json
import com.looker.droidify.utility.common.extension.asSequence
import com.looker.droidify.utility.common.extension.firstOrNull
import com.looker.droidify.utility.common.extension.parseDictionary
import com.looker.droidify.utility.common.extension.writeDictionary
import com.looker.droidify.utility.common.log
import com.looker.droidify.utility.serialization.product
import com.looker.droidify.utility.serialization.productItem
import com.looker.droidify.utility.serialization.repository
@@ -71,14 +71,20 @@ object Database {
get() = "$databasePrefix$innerName"
fun formatCreateTable(name: String): String {
return "CREATE TABLE $name (${QueryBuilder.trimQuery(createTable)})"
return buildString(128) {
append("CREATE TABLE ")
append(name)
append(" (")
trimAndJoin(createTable)
append(")")
}
}
val createIndexPairFormatted: Pair<String, String>?
get() = createIndex?.let {
Pair(
"CREATE INDEX ${innerName}_index ON $innerName ($it)",
"CREATE INDEX ${name}_index ON $innerName ($it)"
"CREATE INDEX ${name}_index ON $innerName ($it)",
)
}
}
@@ -184,7 +190,7 @@ object Database {
}
}
private class Helper(context: Context) : SQLiteOpenHelper(context, "droidify", null, 4) {
private class Helper(context: Context) : SQLiteOpenHelper(context, "droidify", null, 5) {
var created = false
private set
var updated = false
@@ -214,7 +220,7 @@ object Database {
Schema.Product,
Schema.Category,
Schema.Installed,
Schema.Lock
Schema.Lock,
)
dropOldTables(db, Schema.Repository, Schema.Product, Schema.Category)
this.created = this.created || create
@@ -227,7 +233,7 @@ object Database {
val sql = db.query(
"${table.databasePrefix}sqlite_master",
columns = arrayOf("sql"),
selection = Pair("type = ? AND name = ?", arrayOf("table", table.innerName))
selection = Pair("type = ? AND name = ?", arrayOf("table", table.innerName)),
).use { it.firstOrNull()?.getString(0) }.orEmpty()
table.formatCreateTable(table.innerName) != sql
}
@@ -261,7 +267,7 @@ object Database {
val sqls = db.query(
"${table.databasePrefix}sqlite_master",
columns = arrayOf("name", "sql"),
selection = Pair("type = ? AND tbl_name = ?", arrayOf("index", table.innerName))
selection = Pair("type = ? AND tbl_name = ?", arrayOf("index", table.innerName)),
)
.use { cursor ->
cursor.asSequence()
@@ -289,7 +295,7 @@ object Database {
val tables = db.query(
"sqlite_master",
columns = arrayOf("name"),
selection = Pair("type = ?", arrayOf("table"))
selection = Pair("type = ?", arrayOf("table")),
)
.use { cursor -> cursor.asSequence().mapNotNull { it.getString(0) }.toList() }
.filter { !it.startsWith("sqlite_") && !it.startsWith("android_") }
@@ -345,7 +351,7 @@ object Database {
private fun SQLiteDatabase.insertOrReplace(
replace: Boolean,
table: String,
contentValues: ContentValues
contentValues: ContentValues,
): Long {
return if (replace) {
replace(table, null, contentValues)
@@ -353,7 +359,7 @@ object Database {
insert(
table,
null,
contentValues
contentValues,
)
}
}
@@ -363,7 +369,7 @@ object Database {
columns: Array<String>? = null,
selection: Pair<String, Array<String>>? = null,
orderBy: String? = null,
signal: CancellationSignal? = null
signal: CancellationSignal? = null,
): Cursor {
return query(
false,
@@ -375,7 +381,7 @@ object Database {
null,
orderBy,
null,
signal
signal,
)
}
@@ -397,7 +403,7 @@ object Database {
internal fun putWithoutNotification(
repository: Repository,
shouldReplace: Boolean,
database: SQLiteDatabase
database: SQLiteDatabase,
): Long {
return database.insertOrReplace(
shouldReplace,
@@ -409,7 +415,7 @@ object Database {
put(Schema.Repository.ROW_ENABLED, if (repository.enabled) 1 else 0)
put(Schema.Repository.ROW_DELETED, 0)
put(Schema.Repository.ROW_DATA, jsonGenerate(repository::serialize))
}
},
)
}
@@ -442,8 +448,8 @@ object Database {
Schema.Repository.name,
selection = Pair(
"${Schema.Repository.ROW_ID} = ? AND ${Schema.Repository.ROW_DELETED} == 0",
arrayOf(id.toString())
)
arrayOf(id.toString()),
),
).use { it.firstOrNull()?.let(::transform) }
}
@@ -463,9 +469,9 @@ object Database {
selection = Pair(
"${Schema.Repository.ROW_ENABLED} != 0 AND " +
"${Schema.Repository.ROW_DELETED} == 0",
emptyArray()
emptyArray(),
),
signal = null
signal = null,
).use { it.asSequence().map(::transform).toList() }
}
@@ -473,7 +479,7 @@ object Database {
return db.query(
Schema.Repository.name,
selection = Pair("${Schema.Repository.ROW_DELETED} == 0", emptyArray()),
signal = null
signal = null,
).use { it.asSequence().map(::transform).toList() }
}
@@ -489,9 +495,9 @@ object Database {
selection = Pair(
"${Schema.Repository.ROW_ENABLED} == 0 OR " +
"${Schema.Repository.ROW_DELETED} != 0",
emptyArray()
emptyArray(),
),
signal = null
signal = null,
).use { parentCursor ->
parentCursor.asSequence().associate {
val idIndex = it.getColumnIndexOrThrow(Schema.Repository.ROW_ID)
@@ -508,7 +514,7 @@ object Database {
put(Schema.Repository.ROW_DELETED, 1)
},
"${Schema.Repository.ROW_ID} = ?",
arrayOf(id.toString())
arrayOf(id.toString()),
)
notifyChanged(Subject.Repositories, Subject.Repository(id), Subject.Products)
}
@@ -519,18 +525,18 @@ object Database {
val productsCount = db.delete(
Schema.Product.name,
"${Schema.Product.ROW_REPOSITORY_ID} IN ($idsString)",
null
null,
)
val categoriesCount = db.delete(
Schema.Category.name,
"${Schema.Category.ROW_REPOSITORY_ID} IN ($idsString)",
null
null,
)
if (isDeleted) {
db.delete(
Schema.Repository.name,
"${Schema.Repository.ROW_ID} IN ($id)",
null
null,
)
}
productsCount != 0 || categoriesCount != 0
@@ -555,7 +561,7 @@ object Database {
Schema.Repository.name,
selection = Pair("${Schema.Repository.ROW_DELETED} == 0", emptyArray()),
orderBy = "${Schema.Repository.ROW_ENABLED} DESC",
signal = signal
signal = signal,
).observable(Subject.Repositories)
}
@@ -577,26 +583,28 @@ object Database {
.map { get(packageName, null) }
.flowOn(Dispatchers.IO)
suspend fun getUpdates(): List<ProductItem> = withContext(Dispatchers.IO) {
query(
installed = true,
updates = true,
searchQuery = "",
section = ProductItem.Section.All,
order = SortOrder.NAME,
signal = null
).use {
it.asSequence()
.map(ProductAdapter::transformItem)
.toList()
suspend fun getUpdates(skipSignatureCheck: Boolean): List<ProductItem> =
withContext(Dispatchers.IO) {
query(
installed = true,
updates = true,
searchQuery = "",
skipSignatureCheck = skipSignatureCheck,
section = ProductItem.Section.All,
order = SortOrder.NAME,
signal = null,
).use {
it.asSequence()
.map(ProductAdapter::transformItem)
.toList()
}
}
}
fun getUpdatesStream(): Flow<List<ProductItem>> = flowOf(Unit)
fun getUpdatesStream(skipSignatureCheck: Boolean): Flow<List<ProductItem>> = flowOf(Unit)
.onCompletion { if (it == null) emitAll(flowCollection(Subject.Products)) }
// Crashes due to immediate retrieval of data?
.onEach { delay(50) }
.map { getUpdates() }
.map { getUpdates(skipSignatureCheck) }
.flowOn(Dispatchers.IO)
fun get(packageName: String, signal: CancellationSignal?): List<Product> {
@@ -605,10 +613,10 @@ object Database {
columns = arrayOf(
Schema.Product.ROW_REPOSITORY_ID,
Schema.Product.ROW_DESCRIPTION,
Schema.Product.ROW_DATA
Schema.Product.ROW_DATA,
),
selection = Pair("${Schema.Product.ROW_PACKAGE_NAME} = ?", arrayOf(packageName)),
signal = signal
signal = signal,
).use { it.asSequence().map(::transform).toList() }
}
@@ -623,24 +631,26 @@ object Database {
columns = arrayOf("COUNT (*)"),
selection = Pair(
"${Schema.Product.ROW_REPOSITORY_ID} = ?",
arrayOf(repositoryId.toString())
)
arrayOf(repositoryId.toString()),
),
).use { it.firstOrNull()?.getInt(0) ?: 0 }
}
fun query(
installed: Boolean,
updates: Boolean,
skipSignatureCheck: Boolean = false,
searchQuery: String,
section: ProductItem.Section,
order: SortOrder,
signal: CancellationSignal?
signal: CancellationSignal?,
): Cursor {
val builder = QueryBuilder()
val signatureMatches = """installed.${Schema.Installed.ROW_SIGNATURE} IS NOT NULL AND
product.${Schema.Product.ROW_SIGNATURES} LIKE ('%.' || installed.${Schema.Installed.ROW_SIGNATURE} || '.%') AND
product.${Schema.Product.ROW_SIGNATURES} != ''"""
val signatureMatches = if (skipSignatureCheck) "1"
else """installed.${Schema.Installed.ROW_SIGNATURE} IS NOT NULL AND
product.${Schema.Product.ROW_SIGNATURES} LIKE ('%.' || installed.${Schema.Installed.ROW_SIGNATURE} || '.%') AND
product.${Schema.Product.ROW_SIGNATURES} != ''"""
builder += """SELECT product.rowid AS _id, product.${Schema.Product.ROW_REPOSITORY_ID},
product.${Schema.Product.ROW_PACKAGE_NAME}, product.${Schema.Product.ROW_NAME},
@@ -728,6 +738,10 @@ object Database {
}
}
fun transformPackageName(cursor: Cursor): String {
return cursor.getString(cursor.getColumnIndexOrThrow(Schema.Product.ROW_PACKAGE_NAME))
}
fun transformItem(cursor: Cursor): ProductItem {
return cursor.getBlob(cursor.getColumnIndexOrThrow(Schema.Product.ROW_DATA_ITEM))
.jsonParse {
@@ -793,10 +807,10 @@ object Database {
Schema.Installed.ROW_PACKAGE_NAME,
Schema.Installed.ROW_VERSION,
Schema.Installed.ROW_VERSION_CODE,
Schema.Installed.ROW_SIGNATURE
Schema.Installed.ROW_SIGNATURE,
),
selection = Pair("${Schema.Installed.ROW_PACKAGE_NAME} = ?", arrayOf(packageName)),
signal = signal
signal = signal,
).use { it.firstOrNull()?.let(::transform) }
}
@@ -809,7 +823,7 @@ object Database {
put(Schema.Installed.ROW_VERSION, installedItem.version)
put(Schema.Installed.ROW_VERSION_CODE, installedItem.versionCode)
put(Schema.Installed.ROW_SIGNATURE, installedItem.signature)
}
},
)
if (notify) {
notifyChanged(Subject.Products)
@@ -829,7 +843,7 @@ object Database {
val count = db.delete(
Schema.Installed.name,
"${Schema.Installed.ROW_PACKAGE_NAME} = ?",
arrayOf(packageName)
arrayOf(packageName),
)
if (count > 0) {
notifyChanged(Subject.Products)
@@ -841,7 +855,7 @@ object Database {
cursor.getString(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_PACKAGE_NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_VERSION)),
cursor.getLong(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_VERSION_CODE)),
cursor.getString(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_SIGNATURE))
cursor.getString(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_SIGNATURE)),
)
}
}
@@ -854,7 +868,7 @@ object Database {
ContentValues().apply {
put(Schema.Lock.ROW_PACKAGE_NAME, lock.first)
put(Schema.Lock.ROW_VERSION_CODE, lock.second)
}
},
)
if (notify) {
notifyChanged(Subject.Products)
@@ -910,9 +924,9 @@ object Database {
put(Schema.Product.ROW_DATA, jsonGenerate(product::serialize))
put(
Schema.Product.ROW_DATA_ITEM,
jsonGenerate(product.item()::serialize)
jsonGenerate(product.item()::serialize),
)
}
},
)
for (category in product.categories) {
db.insertOrReplace(
@@ -922,7 +936,7 @@ object Database {
put(Schema.Category.ROW_REPOSITORY_ID, product.repositoryId)
put(Schema.Category.ROW_PACKAGE_NAME, product.packageName)
put(Schema.Category.ROW_NAME, category)
}
},
)
}
}
@@ -935,20 +949,20 @@ object Database {
db.delete(
Schema.Product.name,
"${Schema.Product.ROW_REPOSITORY_ID} = ?",
arrayOf(repository.id.toString())
arrayOf(repository.id.toString()),
)
db.delete(
Schema.Category.name,
"${Schema.Category.ROW_REPOSITORY_ID} = ?",
arrayOf(repository.id.toString())
arrayOf(repository.id.toString()),
)
db.execSQL(
"INSERT INTO ${Schema.Product.name} SELECT * " +
"FROM ${Schema.Product.temporaryName}"
"FROM ${Schema.Product.temporaryName}",
)
db.execSQL(
"INSERT INTO ${Schema.Category.name} SELECT * " +
"FROM ${Schema.Category.temporaryName}"
"FROM ${Schema.Category.temporaryName}",
)
RepositoryAdapter.putWithoutNotification(repository, true, db)
db.execSQL("DROP TABLE IF EXISTS ${Schema.Product.temporaryName}")
@@ -957,7 +971,7 @@ object Database {
notifyChanged(
Subject.Repositories,
Subject.Repository(repository.id),
Subject.Products
Subject.Products,
)
} else {
db.execSQL("DROP TABLE IF EXISTS ${Schema.Product.temporaryName}")