mirror of
https://github.com/Aviortheking/Neo-Store.git
synced 2025-04-23 19:32:16 +00:00
Update: Replace CursorOwner's de-/attaching with VM's fillList
This commit is contained in:
parent
bedf5cad3f
commit
3082e7153d
@ -171,6 +171,97 @@ interface ProductDao : BaseDao<Product> {
|
||||
|
||||
return query(SimpleSQLiteQuery(builder.build()))
|
||||
}
|
||||
|
||||
@RawQuery
|
||||
fun queryList(
|
||||
query: SupportSQLiteQuery
|
||||
): List<Product>
|
||||
|
||||
// TODO optimize and simplify
|
||||
@Transaction
|
||||
fun queryList(
|
||||
installed: Boolean, updates: Boolean, searchQuery: String,
|
||||
section: ProductItem.Section, order: ProductItem.Order
|
||||
): 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},
|
||||
product.${ROW_DATA_ITEM},"""
|
||||
|
||||
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 ProductItem.Section.Category) {
|
||||
builder += """JOIN $ROW_CATEGORY_NAME AS category
|
||||
ON product.${ROW_PACKAGE_NAME} = category.${ROW_PACKAGE_NAME}"""
|
||||
}
|
||||
|
||||
builder += """WHERE repository.${ROW_ENABLED} != 0 AND
|
||||
repository.${ROW_DELETED} == 0"""
|
||||
|
||||
if (section is ProductItem.Section.Category) {
|
||||
builder += "AND category.${ROW_NAME} = ?"
|
||||
builder %= section.name
|
||||
} else if (section is ProductItem.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) {
|
||||
ProductItem.Order.NAME -> Unit
|
||||
ProductItem.Order.DATE_ADDED -> builder += "product.${ROW_ADDED} DESC,"
|
||||
ProductItem.Order.LAST_UPDATE -> builder += "product.${ROW_UPDATED} DESC,"
|
||||
}::class
|
||||
builder += "product.${ROW_NAME} COLLATE LOCALIZED ASC"
|
||||
|
||||
return queryList(SimpleSQLiteQuery(builder.build()))
|
||||
}
|
||||
}
|
||||
|
||||
@Dao
|
||||
|
@ -234,35 +234,6 @@ class MainActivityX : AppCompatActivity(), LoaderManager.LoaderCallbacks<Cursor>
|
||||
syncConnection.binder?.setUpdateNotificationBlocker(blockerFragment)
|
||||
}
|
||||
|
||||
fun attachCursorOwner(callback: CursorOwner.Callback, request: CursorOwner.Request) {
|
||||
val oldActiveRequest = viewModel.activeRequests[request.id]
|
||||
if (oldActiveRequest?.callback != null &&
|
||||
oldActiveRequest.callback != callback && oldActiveRequest.cursor != null
|
||||
) {
|
||||
oldActiveRequest.callback.onCursorData(oldActiveRequest.request, null)
|
||||
}
|
||||
val cursor = if (oldActiveRequest?.request == request && oldActiveRequest.cursor != null) {
|
||||
callback.onCursorData(request, oldActiveRequest.cursor)
|
||||
oldActiveRequest.cursor
|
||||
} else {
|
||||
null
|
||||
}
|
||||
viewModel.activeRequests[request.id] = CursorOwner.ActiveRequest(request, callback, cursor)
|
||||
if (cursor == null) {
|
||||
LoaderManager.getInstance(this).restartLoader(request.id, null, this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun detachCursorOwner(callback: CursorOwner.Callback) {
|
||||
for (id in viewModel.activeRequests.keys) {
|
||||
val activeRequest = viewModel.activeRequests[id]!!
|
||||
if (activeRequest.callback == callback) {
|
||||
viewModel.activeRequests[id] = activeRequest.copy(callback = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
|
||||
val request = viewModel.activeRequests[id]!!.request
|
||||
return QueryLoader(this) {
|
||||
|
@ -59,7 +59,7 @@ class ExploreFragment : MainNavFragmentX(), CursorOwner.Callback {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mainActivityX.attachCursorOwner(this, viewModel.request(source))
|
||||
viewModel.fillList(source)
|
||||
viewModel.db.repositoryDao.allFlowable
|
||||
.observeOn(Schedulers.io())
|
||||
.flatMapSingle { list -> RxUtils.querySingle { list.mapNotNull { it.trueData } } }
|
||||
@ -68,12 +68,6 @@ class ExploreFragment : MainNavFragmentX(), CursorOwner.Callback {
|
||||
.subscribe { repositories = it }
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
|
||||
mainActivityX.detachCursorOwner(this)
|
||||
}
|
||||
|
||||
override fun onCursorData(request: CursorOwner.Request, cursor: Cursor?) {
|
||||
(binding.recyclerView.adapter as? AppListAdapter)?.apply {
|
||||
this.cursor = cursor
|
||||
|
@ -72,7 +72,7 @@ class InstalledFragment : MainNavFragmentX(), CursorOwner.Callback {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mainActivityX.attachCursorOwner(this, viewModel.request(source))
|
||||
viewModel.fillList(source)
|
||||
viewModel.db.repositoryDao.allFlowable
|
||||
.observeOn(Schedulers.io())
|
||||
.flatMapSingle { list -> RxUtils.querySingle { list.mapNotNull { it.trueData } } }
|
||||
@ -81,12 +81,6 @@ class InstalledFragment : MainNavFragmentX(), CursorOwner.Callback {
|
||||
.subscribe { repositories = it }
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
|
||||
mainActivityX.detachCursorOwner(this)
|
||||
}
|
||||
|
||||
override fun onCursorData(request: CursorOwner.Request, cursor: Cursor?) {
|
||||
// TODO get a list instead of the cursor
|
||||
// TODO use LiveData and observers instead of listeners
|
||||
|
@ -72,7 +72,7 @@ class LatestFragment : MainNavFragmentX(), CursorOwner.Callback {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mainActivityX.attachCursorOwner(this, viewModel.request(source))
|
||||
viewModel.fillList(source)
|
||||
viewModel.db.repositoryDao.allFlowable
|
||||
.observeOn(Schedulers.io())
|
||||
.flatMapSingle { list -> RxUtils.querySingle { list.mapNotNull { it.trueData } } }
|
||||
@ -81,12 +81,6 @@ class LatestFragment : MainNavFragmentX(), CursorOwner.Callback {
|
||||
.subscribe { repositories = it }
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
|
||||
mainActivityX.detachCursorOwner(this)
|
||||
}
|
||||
|
||||
override fun onCursorData(request: CursorOwner.Request, cursor: Cursor?) {
|
||||
// TODO get a list instead of the cursor
|
||||
// TODO use LiveData and observers instead of listeners
|
||||
|
@ -18,7 +18,7 @@ abstract class MainNavFragmentX : Fragment(), CursorOwner.Callback {
|
||||
internal fun setSearchQuery(searchQuery: String) {
|
||||
viewModel.setSearchQuery(searchQuery) {
|
||||
if (view != null) {
|
||||
mainActivityX.attachCursorOwner(this, viewModel.request(source))
|
||||
viewModel.fillList(source)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -26,7 +26,7 @@ abstract class MainNavFragmentX : Fragment(), CursorOwner.Callback {
|
||||
internal fun setSection(section: ProductItem.Section) {
|
||||
viewModel.setSection(section) {
|
||||
if (view != null) {
|
||||
mainActivityX.attachCursorOwner(this, viewModel.request(source))
|
||||
viewModel.fillList(source)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -34,7 +34,7 @@ abstract class MainNavFragmentX : Fragment(), CursorOwner.Callback {
|
||||
internal fun setOrder(order: ProductItem.Order) {
|
||||
viewModel.setOrder(order) {
|
||||
if (view != null) {
|
||||
mainActivityX.attachCursorOwner(this, viewModel.request(source))
|
||||
viewModel.fillList(source)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -44,4 +44,37 @@ enum class Source(val titleResId: Int, val sections: Boolean, val order: Boolean
|
||||
AVAILABLE(R.string.available, true, true),
|
||||
INSTALLED(R.string.installed, false, true),
|
||||
UPDATES(R.string.updates, false, false)
|
||||
}
|
||||
|
||||
sealed class Request {
|
||||
internal abstract val id: Int
|
||||
|
||||
data class ProductsAvailable(
|
||||
val searchQuery: String, val section: ProductItem.Section,
|
||||
val order: ProductItem.Order,
|
||||
) : Request() {
|
||||
override val id: Int
|
||||
get() = 1
|
||||
}
|
||||
|
||||
data class ProductsInstalled(
|
||||
val searchQuery: String, val section: ProductItem.Section,
|
||||
val order: ProductItem.Order,
|
||||
) : Request() {
|
||||
override val id: Int
|
||||
get() = 2
|
||||
}
|
||||
|
||||
data class ProductsUpdates(
|
||||
val searchQuery: String, val section: ProductItem.Section,
|
||||
val order: ProductItem.Order,
|
||||
) : Request() {
|
||||
override val id: Int
|
||||
get() = 3
|
||||
}
|
||||
|
||||
object Repositories : Request() {
|
||||
override val id: Int
|
||||
get() = 4
|
||||
}
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
package com.looker.droidify.ui.viewmodels
|
||||
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.looker.droidify.database.CursorOwner
|
||||
import com.looker.droidify.database.DatabaseX
|
||||
import com.looker.droidify.database.Product
|
||||
import com.looker.droidify.entity.ProductItem
|
||||
import com.looker.droidify.ui.fragments.Request
|
||||
import com.looker.droidify.ui.fragments.Source
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@ -13,6 +15,7 @@ import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class MainNavFragmentViewModelX(val db: DatabaseX) : ViewModel() {
|
||||
|
||||
@ -37,7 +40,7 @@ class MainNavFragmentViewModelX(val db: DatabaseX) : ViewModel() {
|
||||
started = SharingStarted.WhileSubscribed(5000)
|
||||
)
|
||||
|
||||
fun request(source: Source): CursorOwner.Request {
|
||||
fun request(source: Source): Request {
|
||||
var mSearchQuery = ""
|
||||
var mSections: ProductItem.Section = ProductItem.Section.All
|
||||
var mOrder: ProductItem.Order = ProductItem.Order.NAME
|
||||
@ -47,17 +50,17 @@ class MainNavFragmentViewModelX(val db: DatabaseX) : ViewModel() {
|
||||
launch { order.collect { if (source.order) mOrder = it } }
|
||||
}
|
||||
return when (source) {
|
||||
Source.AVAILABLE -> CursorOwner.Request.ProductsAvailable(
|
||||
Source.AVAILABLE -> Request.ProductsAvailable(
|
||||
mSearchQuery,
|
||||
mSections,
|
||||
mOrder
|
||||
)
|
||||
Source.INSTALLED -> CursorOwner.Request.ProductsInstalled(
|
||||
Source.INSTALLED -> Request.ProductsInstalled(
|
||||
mSearchQuery,
|
||||
mSections,
|
||||
mOrder
|
||||
)
|
||||
Source.UPDATES -> CursorOwner.Request.ProductsUpdates(
|
||||
Source.UPDATES -> Request.ProductsUpdates(
|
||||
mSearchQuery,
|
||||
mSections,
|
||||
mOrder
|
||||
@ -65,6 +68,46 @@ class MainNavFragmentViewModelX(val db: DatabaseX) : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
var productsList = MediatorLiveData<MutableList<Product>>()
|
||||
|
||||
fun fillList(source: Source) {
|
||||
viewModelScope.launch {
|
||||
productsList.value = query(request(source))?.toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun query(request: Request): List<Product>? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
when (request) {
|
||||
is Request.ProductsAvailable -> db.productDao
|
||||
.queryList(
|
||||
installed = false,
|
||||
updates = false,
|
||||
searchQuery = request.searchQuery,
|
||||
section = request.section,
|
||||
order = request.order
|
||||
)
|
||||
is Request.ProductsInstalled -> db.productDao
|
||||
.queryList(
|
||||
installed = true,
|
||||
updates = false,
|
||||
searchQuery = request.searchQuery,
|
||||
section = request.section,
|
||||
order = request.order
|
||||
)
|
||||
is Request.ProductsUpdates -> db.productDao
|
||||
.queryList(
|
||||
installed = true,
|
||||
updates = true,
|
||||
searchQuery = request.searchQuery,
|
||||
section = request.section,
|
||||
order = request.order
|
||||
)
|
||||
else -> listOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setSection(newSection: ProductItem.Section, perform: () -> Unit) {
|
||||
viewModelScope.launch {
|
||||
if (newSection != sections.value) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user