v0.6.5
This commit is contained in:
@@ -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}")
|
||||
|
||||
Reference in New Issue
Block a user