diff --git a/src/main/kotlin/com/looker/droidify/database/DAOs.kt b/src/main/kotlin/com/looker/droidify/database/DAOs.kt index 0aeddba2..b6e54898 100644 --- a/src/main/kotlin/com/looker/droidify/database/DAOs.kt +++ b/src/main/kotlin/com/looker/droidify/database/DAOs.kt @@ -91,7 +91,7 @@ interface ProductDao : BaseDao { query: SupportSQLiteQuery ): Cursor - // TODO optimize and simplify + // TODO Remove @Transaction fun query( installed: Boolean, updates: Boolean, searchQuery: String, @@ -175,6 +175,92 @@ interface ProductDao : BaseDao { return query(SimpleSQLiteQuery(builder.build())) } + @RawQuery + fun queryObject(query: SupportSQLiteQuery): List + + @Transaction + fun queryObject( + installed: Boolean, updates: Boolean, searchQuery: String, + section: Section, order: Order, numberOfItems: Int = 0 + ): List { + 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]) fun queryList( query: SupportSQLiteQuery @@ -332,6 +418,9 @@ interface ProductTempDao : BaseDao { this.signatures = signatures compatible = if (it.compatible) 1 else 0 data = it + icon = it.icon + metadataIcon = it.metadataIcon + releases = it.releases } }) it.categories.forEach { category -> diff --git a/src/main/kotlin/com/looker/droidify/database/entity/Product.kt b/src/main/kotlin/com/looker/droidify/database/entity/Product.kt index 04628f74..5eca54b2 100644 --- a/src/main/kotlin/com/looker/droidify/database/entity/Product.kt +++ b/src/main/kotlin/com/looker/droidify/database/entity/Product.kt @@ -3,6 +3,7 @@ package com.looker.droidify.database.entity import androidx.room.ColumnInfo import androidx.room.Entity import com.looker.droidify.entity.ProductItem +import com.looker.droidify.entity.Release @Entity(tableName = "product", primaryKeys = ["repository_id", "package_name"]) open class Product { @@ -17,11 +18,39 @@ open class Product { var version_code = 0L var signatures = "" var compatible = 0 + var icon = "" + var metadataIcon = "" + var releases: List = emptyList() @ColumnInfo(typeAffinity = ColumnInfo.BLOB) var data: com.looker.droidify.entity.Product? = null - fun item(): ProductItem? = data?.item() + val selectedReleases: List + 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") diff --git a/src/main/kotlin/com/looker/droidify/entity/Release.kt b/src/main/kotlin/com/looker/droidify/entity/Release.kt index 1e487a70..f3d710e6 100644 --- a/src/main/kotlin/com/looker/droidify/entity/Release.kt +++ b/src/main/kotlin/com/looker/droidify/entity/Release.kt @@ -7,6 +7,7 @@ import com.fasterxml.jackson.core.JsonToken import com.looker.droidify.database.entity.Repository import com.looker.droidify.utility.extension.json.* +// TODO make a Room entity data class Release( val selected: Boolean, val version: String, diff --git a/src/main/kotlin/com/looker/droidify/service/SyncService.kt b/src/main/kotlin/com/looker/droidify/service/SyncService.kt index 9368a22e..cfdd0d27 100644 --- a/src/main/kotlin/com/looker/droidify/service/SyncService.kt +++ b/src/main/kotlin/com/looker/droidify/service/SyncService.kt @@ -24,11 +24,9 @@ import com.looker.droidify.ui.activities.MainActivityX import com.looker.droidify.utility.RxUtils import com.looker.droidify.utility.Utils 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.resources.getColorFromAttr 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.disposables.Disposable import io.reactivex.rxjava3.schedulers.Schedulers @@ -390,18 +388,13 @@ class SyncService : ConnectionService() { val disposable = RxUtils .querySingle { it -> db.productDao - .query( + .queryObject( installed = true, updates = true, searchQuery = "", section = Section.All, - order = Order.NAME, - signal = it - ) - .use { - it.asSequence().map { it.getProduct().item() } - .toList() - } + order = Order.NAME + ).mapNotNull { it.item } } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) diff --git a/src/main/kotlin/com/looker/droidify/ui/compose/ItemRecyclers.kt b/src/main/kotlin/com/looker/droidify/ui/compose/ItemRecyclers.kt index e4f24765..43239f57 100644 --- a/src/main/kotlin/com/looker/droidify/ui/compose/ItemRecyclers.kt +++ b/src/main/kotlin/com/looker/droidify/ui/compose/ItemRecyclers.kt @@ -15,7 +15,7 @@ fun ProductsVerticalRecycler(productsList: List) { verticalArrangement = spacedBy(2.dp) ) { items(productsList) { product: Product -> - product.item()?.let { item -> + product.item?.let { item -> ProductRow(item.name, item.version, item.summary, onUserClick = { Log.d(this.toString(), "You clicked $it") }) @@ -30,7 +30,7 @@ fun ProductsHorizontalRecycler(productsList: List) { horizontalArrangement = spacedBy(2.dp) ) { items(productsList) { product: Product -> - product.item()?.let { item -> + product.item?.let { item -> ProductColumn(item.name, item.version, onUserClick = { Log.d(this.toString(), "You clicked $it") }) diff --git a/src/main/kotlin/com/looker/droidify/ui/fragments/ExploreFragment.kt b/src/main/kotlin/com/looker/droidify/ui/fragments/ExploreFragment.kt index b5f318e6..aac0ae42 100644 --- a/src/main/kotlin/com/looker/droidify/ui/fragments/ExploreFragment.kt +++ b/src/main/kotlin/com/looker/droidify/ui/fragments/ExploreFragment.kt @@ -52,7 +52,7 @@ class ExploreFragment : MainNavFragmentX() { override fun setupAdapters() { appsItemAdapter = PagedModelAdapter(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?.setHasStableIds(true) diff --git a/src/main/kotlin/com/looker/droidify/ui/fragments/InstalledFragment.kt b/src/main/kotlin/com/looker/droidify/ui/fragments/InstalledFragment.kt index 37487e2f..22a96d12 100644 --- a/src/main/kotlin/com/looker/droidify/ui/fragments/InstalledFragment.kt +++ b/src/main/kotlin/com/looker/droidify/ui/fragments/InstalledFragment.kt @@ -55,11 +55,11 @@ class InstalledFragment : MainNavFragmentX() { override fun setupAdapters() { installedItemAdapter = PagedModelAdapter(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_ASYNC_DIFFER_CONFIG) { // 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?.setHasStableIds(true) diff --git a/src/main/kotlin/com/looker/droidify/ui/fragments/LatestFragment.kt b/src/main/kotlin/com/looker/droidify/ui/fragments/LatestFragment.kt index 841343f7..59fe7850 100644 --- a/src/main/kotlin/com/looker/droidify/ui/fragments/LatestFragment.kt +++ b/src/main/kotlin/com/looker/droidify/ui/fragments/LatestFragment.kt @@ -56,11 +56,11 @@ class LatestFragment : MainNavFragmentX() { override fun setupAdapters() { updatedItemAdapter = PagedModelAdapter(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_ASYNC_DIFFER_CONFIG) { // 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?.setHasStableIds(true) diff --git a/src/main/kotlin/com/looker/droidify/utility/Utils.kt b/src/main/kotlin/com/looker/droidify/utility/Utils.kt index f0710c5d..b300b0d8 100644 --- a/src/main/kotlin/com/looker/droidify/utility/Utils.kt +++ b/src/main/kotlin/com/looker/droidify/utility/Utils.kt @@ -184,6 +184,7 @@ object Utils { } +// TODO Remove fun Cursor.getProduct(): Product = getBlob(getColumnIndex(ROW_DATA)) .jsonParse { 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)) .jsonParse { 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)) .jsonParse { Repository.deserialize(it).apply { @@ -240,6 +242,6 @@ val PRODUCT_ASYNC_DIFFER_CONFIG oldItem: com.looker.droidify.database.entity.Product, newItem: com.looker.droidify.database.entity.Product ): Boolean { - return oldItem.item() == newItem.item() + return oldItem.item == newItem.item } }).build() \ No newline at end of file