Update: Migrate some of entity Product's fields to database.entity (and add some TODOs)

This commit is contained in:
machiav3lli 2022-02-02 01:44:20 +01:00
parent af611cb49a
commit 7694f3cade
9 changed files with 135 additions and 21 deletions

View File

@ -91,7 +91,7 @@ interface ProductDao : BaseDao<Product> {
query: SupportSQLiteQuery query: SupportSQLiteQuery
): Cursor ): Cursor
// TODO optimize and simplify // TODO Remove
@Transaction @Transaction
fun query( fun query(
installed: Boolean, updates: Boolean, searchQuery: String, installed: Boolean, updates: Boolean, searchQuery: String,
@ -175,6 +175,92 @@ interface ProductDao : BaseDao<Product> {
return query(SimpleSQLiteQuery(builder.build())) return query(SimpleSQLiteQuery(builder.build()))
} }
@RawQuery
fun queryObject(query: SupportSQLiteQuery): List<Product>
@Transaction
fun queryObject(
installed: Boolean, updates: Boolean, searchQuery: String,
section: Section, order: Order, numberOfItems: Int = 0
): List<Product> {
val builder = QueryBuilder()
val signatureMatches = """installed.${ROW_SIGNATURE} IS NOT NULL AND
product.${ROW_SIGNATURES} LIKE ('%.' || installed.${ROW_SIGNATURE} || '.%') AND
product.${ROW_SIGNATURES} != ''"""
builder += """SELECT product.rowid AS _id, product.${ROW_REPOSITORY_ID},
product.${ROW_PACKAGE_NAME}, product.${ROW_NAME},
product.${ROW_SUMMARY}, installed.${ROW_VERSION},
(COALESCE(lock.${ROW_VERSION_CODE}, -1) NOT IN (0, product.${ROW_VERSION_CODE}) AND
product.${ROW_COMPATIBLE} != 0 AND product.${ROW_VERSION_CODE} >
COALESCE(installed.${ROW_VERSION_CODE}, 0xffffffff) AND $signatureMatches)
AS ${ROW_CAN_UPDATE}, product.${ROW_COMPATIBLE},"""
if (searchQuery.isNotEmpty()) {
builder += """(((product.${ROW_NAME} LIKE ? OR
product.${ROW_SUMMARY} LIKE ?) * 7) |
((product.${ROW_PACKAGE_NAME} LIKE ?) * 3) |
(product.${ROW_DESCRIPTION} LIKE ?)) AS ${ROW_MATCH_RANK},"""
builder %= List(4) { "%$searchQuery%" }
} else {
builder += "0 AS ${ROW_MATCH_RANK},"
}
builder += """MAX((product.${ROW_COMPATIBLE} AND
(installed.${ROW_SIGNATURE} IS NULL OR $signatureMatches)) ||
PRINTF('%016X', product.${ROW_VERSION_CODE})) FROM $ROW_PRODUCT_NAME AS product"""
builder += """JOIN $ROW_REPOSITORY_NAME AS repository
ON product.${ROW_REPOSITORY_ID} = repository.${ROW_ID}"""
builder += """LEFT JOIN $ROW_LOCK_NAME AS lock
ON product.${ROW_PACKAGE_NAME} = lock.${ROW_PACKAGE_NAME}"""
if (!installed && !updates) {
builder += "LEFT"
}
builder += """JOIN $ROW_INSTALLED_NAME AS installed
ON product.${ROW_PACKAGE_NAME} = installed.${ROW_PACKAGE_NAME}"""
if (section is Section.Category) {
builder += """JOIN $ROW_CATEGORY_NAME AS category
ON product.${ROW_PACKAGE_NAME} = category.${ROW_PACKAGE_NAME}"""
}
builder += """WHERE repository.${ROW_ENABLED} != 0"""
if (section is Section.Category) {
builder += "AND category.${ROW_NAME} = ?"
builder %= section.name
} else if (section is Section.Repository) {
builder += "AND product.${ROW_REPOSITORY_ID} = ?"
builder %= section.id.toString()
}
if (searchQuery.isNotEmpty()) {
builder += """AND $ROW_MATCH_RANK > 0"""
}
builder += "GROUP BY product.${ROW_PACKAGE_NAME} HAVING 1"
if (updates) {
builder += "AND $ROW_CAN_UPDATE"
}
builder += "ORDER BY"
if (searchQuery.isNotEmpty()) {
builder += """$ROW_MATCH_RANK DESC,"""
}
when (order) {
Order.NAME -> Unit
Order.DATE_ADDED -> builder += "product.${ROW_ADDED} DESC,"
Order.LAST_UPDATE -> builder += "product.${ROW_UPDATED} DESC,"
}::class
builder += "product.${ROW_NAME} COLLATE LOCALIZED ASC${if (numberOfItems > 0) " LIMIT $numberOfItems" else ""}"
return queryObject(SimpleSQLiteQuery(builder.build()))
}
@RawQuery(observedEntities = [Product::class]) @RawQuery(observedEntities = [Product::class])
fun queryList( fun queryList(
query: SupportSQLiteQuery query: SupportSQLiteQuery
@ -332,6 +418,9 @@ interface ProductTempDao : BaseDao<ProductTemp> {
this.signatures = signatures this.signatures = signatures
compatible = if (it.compatible) 1 else 0 compatible = if (it.compatible) 1 else 0
data = it data = it
icon = it.icon
metadataIcon = it.metadataIcon
releases = it.releases
} }
}) })
it.categories.forEach { category -> it.categories.forEach { category ->

View File

@ -3,6 +3,7 @@ package com.looker.droidify.database.entity
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import com.looker.droidify.entity.ProductItem import com.looker.droidify.entity.ProductItem
import com.looker.droidify.entity.Release
@Entity(tableName = "product", primaryKeys = ["repository_id", "package_name"]) @Entity(tableName = "product", primaryKeys = ["repository_id", "package_name"])
open class Product { open class Product {
@ -17,11 +18,39 @@ open class Product {
var version_code = 0L var version_code = 0L
var signatures = "" var signatures = ""
var compatible = 0 var compatible = 0
var icon = ""
var metadataIcon = ""
var releases: List<Release> = emptyList()
@ColumnInfo(typeAffinity = ColumnInfo.BLOB) @ColumnInfo(typeAffinity = ColumnInfo.BLOB)
var data: com.looker.droidify.entity.Product? = null var data: com.looker.droidify.entity.Product? = null
fun item(): ProductItem? = data?.item() val selectedReleases: List<Release>
get() = releases.filter { it.selected }
val displayRelease: Release?
get() = selectedReleases.firstOrNull() ?: releases.firstOrNull()
val version: String
get() = displayRelease?.version.orEmpty()
val versionCode: Long
get() = selectedReleases.firstOrNull()?.versionCode ?: 0L
val item: ProductItem
get() = ProductItem(
repository_id,
package_name,
name,
summary,
icon,
metadataIcon,
version,
"",
compatible != 0,
false,
0
)
} }
@Entity(tableName = "temporary_product") @Entity(tableName = "temporary_product")

View File

@ -7,6 +7,7 @@ import com.fasterxml.jackson.core.JsonToken
import com.looker.droidify.database.entity.Repository import com.looker.droidify.database.entity.Repository
import com.looker.droidify.utility.extension.json.* import com.looker.droidify.utility.extension.json.*
// TODO make a Room entity
data class Release( data class Release(
val selected: Boolean, val selected: Boolean,
val version: String, val version: String,

View File

@ -24,11 +24,9 @@ import com.looker.droidify.ui.activities.MainActivityX
import com.looker.droidify.utility.RxUtils import com.looker.droidify.utility.RxUtils
import com.looker.droidify.utility.Utils import com.looker.droidify.utility.Utils
import com.looker.droidify.utility.extension.android.Android import com.looker.droidify.utility.extension.android.Android
import com.looker.droidify.utility.extension.android.asSequence
import com.looker.droidify.utility.extension.android.notificationManager import com.looker.droidify.utility.extension.android.notificationManager
import com.looker.droidify.utility.extension.resources.getColorFromAttr import com.looker.droidify.utility.extension.resources.getColorFromAttr
import com.looker.droidify.utility.extension.text.formatSize import com.looker.droidify.utility.extension.text.formatSize
import com.looker.droidify.utility.getProduct
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
@ -390,18 +388,13 @@ class SyncService : ConnectionService<SyncService.Binder>() {
val disposable = RxUtils val disposable = RxUtils
.querySingle { it -> .querySingle { it ->
db.productDao db.productDao
.query( .queryObject(
installed = true, installed = true,
updates = true, updates = true,
searchQuery = "", searchQuery = "",
section = Section.All, section = Section.All,
order = Order.NAME, order = Order.NAME
signal = it ).mapNotNull { it.item }
)
.use {
it.asSequence().map { it.getProduct().item() }
.toList()
}
} }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View File

@ -15,7 +15,7 @@ fun ProductsVerticalRecycler(productsList: List<Product>) {
verticalArrangement = spacedBy(2.dp) verticalArrangement = spacedBy(2.dp)
) { ) {
items(productsList) { product: Product -> items(productsList) { product: Product ->
product.item()?.let { item -> product.item?.let { item ->
ProductRow(item.name, item.version, item.summary, onUserClick = { ProductRow(item.name, item.version, item.summary, onUserClick = {
Log.d(this.toString(), "You clicked $it") Log.d(this.toString(), "You clicked $it")
}) })
@ -30,7 +30,7 @@ fun ProductsHorizontalRecycler(productsList: List<Product>) {
horizontalArrangement = spacedBy(2.dp) horizontalArrangement = spacedBy(2.dp)
) { ) {
items(productsList) { product: Product -> items(productsList) { product: Product ->
product.item()?.let { item -> product.item?.let { item ->
ProductColumn(item.name, item.version, onUserClick = { ProductColumn(item.name, item.version, onUserClick = {
Log.d(this.toString(), "You clicked $it") Log.d(this.toString(), "You clicked $it")
}) })

View File

@ -52,7 +52,7 @@ class ExploreFragment : MainNavFragmentX() {
override fun setupAdapters() { override fun setupAdapters() {
appsItemAdapter = PagedModelAdapter<Product, VAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) { appsItemAdapter = PagedModelAdapter<Product, VAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) {
it.item()?.let { item -> VAppItem(item, repositories[it.repository_id]) } it.item?.let { item -> VAppItem(item, repositories[it.repository_id]) }
} }
appsFastAdapter = FastAdapter.with(appsItemAdapter) appsFastAdapter = FastAdapter.with(appsItemAdapter)
appsFastAdapter?.setHasStableIds(true) appsFastAdapter?.setHasStableIds(true)

View File

@ -55,11 +55,11 @@ class InstalledFragment : MainNavFragmentX() {
override fun setupAdapters() { override fun setupAdapters() {
installedItemAdapter = PagedModelAdapter<Product, VAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) { installedItemAdapter = PagedModelAdapter<Product, VAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) {
it.item()?.let { item -> VAppItem(item, repositories[it.repository_id]) } it.item?.let { item -> VAppItem(item, repositories[it.repository_id]) }
} }
updatedItemAdapter = PagedModelAdapter<Product, HAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) { updatedItemAdapter = PagedModelAdapter<Product, HAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) {
// TODO filter for only updated apps and add placeholder // TODO filter for only updated apps and add placeholder
it.item()?.let { item -> HAppItem(item, repositories[it.repository_id]) } it.item?.let { item -> HAppItem(item, repositories[it.repository_id]) }
} }
installedFastAdapter = FastAdapter.with(installedItemAdapter) installedFastAdapter = FastAdapter.with(installedItemAdapter)
installedFastAdapter?.setHasStableIds(true) installedFastAdapter?.setHasStableIds(true)

View File

@ -56,11 +56,11 @@ class LatestFragment : MainNavFragmentX() {
override fun setupAdapters() { override fun setupAdapters() {
updatedItemAdapter = PagedModelAdapter<Product, VAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) { updatedItemAdapter = PagedModelAdapter<Product, VAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) {
it.item()?.let { item -> VAppItem(item, repositories[it.repository_id]) } it.item?.let { item -> VAppItem(item, repositories[it.repository_id]) }
} }
newItemAdapter = PagedModelAdapter<Product, HAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) { newItemAdapter = PagedModelAdapter<Product, HAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) {
// TODO filter for only new apps and add placeholder // TODO filter for only new apps and add placeholder
it.item()?.let { item -> HAppItem(item, repositories[it.repository_id]) } it.item?.let { item -> HAppItem(item, repositories[it.repository_id]) }
} }
updatedFastAdapter = FastAdapter.with(updatedItemAdapter) updatedFastAdapter = FastAdapter.with(updatedItemAdapter)
updatedFastAdapter?.setHasStableIds(true) updatedFastAdapter?.setHasStableIds(true)

View File

@ -184,6 +184,7 @@ object Utils {
} }
// TODO Remove
fun Cursor.getProduct(): Product = getBlob(getColumnIndex(ROW_DATA)) fun Cursor.getProduct(): Product = getBlob(getColumnIndex(ROW_DATA))
.jsonParse { .jsonParse {
Product.deserialize(it).apply { Product.deserialize(it).apply {
@ -192,7 +193,7 @@ fun Cursor.getProduct(): Product = getBlob(getColumnIndex(ROW_DATA))
} }
} }
// TODO Remove
fun Cursor.getProductItem(): ProductItem = getBlob(getColumnIndex(ROW_DATA_ITEM)) fun Cursor.getProductItem(): ProductItem = getBlob(getColumnIndex(ROW_DATA_ITEM))
.jsonParse { .jsonParse {
ProductItem.deserialize(it).apply { ProductItem.deserialize(it).apply {
@ -208,6 +209,7 @@ fun Cursor.getProductItem(): ProductItem = getBlob(getColumnIndex(ROW_DATA_ITEM)
} }
} }
// TODO Remove
fun Cursor.getRepository(): Repository = getBlob(getColumnIndex(ROW_DATA)) fun Cursor.getRepository(): Repository = getBlob(getColumnIndex(ROW_DATA))
.jsonParse { .jsonParse {
Repository.deserialize(it).apply { Repository.deserialize(it).apply {
@ -240,6 +242,6 @@ val PRODUCT_ASYNC_DIFFER_CONFIG
oldItem: com.looker.droidify.database.entity.Product, oldItem: com.looker.droidify.database.entity.Product,
newItem: com.looker.droidify.database.entity.Product newItem: com.looker.droidify.database.entity.Product
): Boolean { ): Boolean {
return oldItem.item() == newItem.item() return oldItem.item == newItem.item
} }
}).build() }).build()