Update: Migrate to Paged FastAdapter

This commit is contained in:
machiav3lli 2022-01-13 01:43:40 +01:00
parent f6ff3844ae
commit edc0235119
7 changed files with 113 additions and 37 deletions

View File

@ -151,6 +151,7 @@ dependencies {
implementation("com.mikepenz:fastadapter:5.6.0")
implementation("com.mikepenz:fastadapter-extensions-diff:5.6.0")
implementation("com.mikepenz:fastadapter-extensions-binding:5.6.0")
implementation("com.mikepenz:fastadapter-extensions-paged:5.6.0")
// Coil
implementation 'io.coil-kt:coil:1.4.0'

View File

@ -2,6 +2,7 @@ package com.looker.droidify.database
import android.database.Cursor
import android.os.CancellationSignal
import androidx.paging.DataSource
import androidx.room.*
import androidx.sqlite.db.SimpleSQLiteQuery
import androidx.sqlite.db.SupportSQLiteQuery
@ -175,14 +176,13 @@ interface ProductDao : BaseDao<Product> {
@RawQuery(observedEntities = [Product::class])
fun queryList(
query: SupportSQLiteQuery
): List<Product>
): DataSource.Factory<Int, Product>
// TODO optimize and simplify
@Transaction
fun queryList(
installed: Boolean, updates: Boolean, searchQuery: String,
section: ProductItem.Section, order: ProductItem.Order, numberOfItems: Int = 0
): List<Product> {
): DataSource.Factory<Int, Product> {
val builder = QueryBuilder()
val signatureMatches = """installed.${ROW_SIGNATURE} IS NOT NULL AND

View File

@ -11,9 +11,13 @@ import com.looker.droidify.database.Product
import com.looker.droidify.databinding.FragmentExploreXBinding
import com.looker.droidify.entity.Repository
import com.looker.droidify.ui.adapters.AppListAdapter
import com.looker.droidify.ui.items.VAppItem
import com.looker.droidify.ui.viewmodels.MainNavFragmentViewModelX
import com.looker.droidify.utility.PRODUCT_ASYNC_DIFFER_CONFIG
import com.looker.droidify.utility.RxUtils
import com.looker.droidify.utility.extension.resources.getDrawableCompat
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.paged.PagedModelAdapter
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import me.zhanghai.android.fastscroll.FastScrollerBuilder
@ -23,6 +27,8 @@ class ExploreFragment : MainNavFragmentX() {
override lateinit var viewModel: MainNavFragmentViewModelX
private lateinit var binding: FragmentExploreXBinding
private lateinit var appsItemAdapter: PagedModelAdapter<Product, VAppItem>
private var appsFastAdapter: FastAdapter<VAppItem>? = null
override val source = Source.AVAILABLE
@ -40,13 +46,19 @@ class ExploreFragment : MainNavFragmentX() {
viewModel = ViewModelProvider(this, viewModelFactory)
.get(MainNavFragmentViewModelX::class.java)
appsItemAdapter = PagedModelAdapter<Product, VAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) {
it.data_item?.let { item ->
VAppItem(item, repositories[it.repository_id])
}
}
appsFastAdapter = FastAdapter.with(appsItemAdapter)
appsFastAdapter?.setHasStableIds(true)
binding.recyclerView.apply {
layoutManager = LinearLayoutManager(context)
isMotionEventSplittingEnabled = false
isVerticalScrollBarEnabled = false
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
recycledViewPool.setMaxRecycledViews(AppListAdapter.ViewType.PRODUCT.ordinal, 30)
adapter = AppListAdapter { mainActivityX.navigateProduct(it.packageName) }
adapter = appsFastAdapter
FastScrollerBuilder(this)
.useMd2Style()
.setThumbDrawable(this.context.getDrawableCompat(R.drawable.scrollbar_thumb))

View File

@ -8,16 +8,18 @@ import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.looker.droidify.R
import com.looker.droidify.database.Product
import com.looker.droidify.databinding.FragmentInstalledXBinding
import com.looker.droidify.entity.ProductItem
import com.looker.droidify.entity.Repository
import com.looker.droidify.ui.adapters.AppListAdapter
import com.looker.droidify.ui.items.HAppItem
import com.looker.droidify.ui.items.VAppItem
import com.looker.droidify.ui.viewmodels.MainNavFragmentViewModelX
import com.looker.droidify.utility.PRODUCT_ASYNC_DIFFER_CONFIG
import com.looker.droidify.utility.RxUtils
import com.looker.droidify.utility.extension.resources.getDrawableCompat
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.adapters.ItemAdapter
import com.mikepenz.fastadapter.paged.PagedModelAdapter
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import me.zhanghai.android.fastscroll.FastScrollerBuilder
@ -27,9 +29,9 @@ class InstalledFragment : MainNavFragmentX() {
override lateinit var viewModel: MainNavFragmentViewModelX
private lateinit var binding: FragmentInstalledXBinding
private val installedItemAdapter = ItemAdapter<VAppItem>()
private lateinit var installedItemAdapter: PagedModelAdapter<Product, VAppItem>
private var installedFastAdapter: FastAdapter<VAppItem>? = null
private val updatedItemAdapter = ItemAdapter<HAppItem>()
private lateinit var updatedItemAdapter: PagedModelAdapter<Product, HAppItem>
private var updatedFastAdapter: FastAdapter<HAppItem>? = null
override val source = Source.INSTALLED
@ -48,12 +50,23 @@ class InstalledFragment : MainNavFragmentX() {
viewModel = ViewModelProvider(this, viewModelFactory)
.get(MainNavFragmentViewModelX::class.java)
installedItemAdapter = PagedModelAdapter<Product, VAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) {
it.data_item?.let { item ->
VAppItem(item, repositories[it.repository_id])
}
}
updatedItemAdapter = PagedModelAdapter<Product, HAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) {
it.data_item?.let { item ->
// TODO filter for only updated apps and add placeholder
HAppItem(item, repositories[it.repository_id])
}
}
installedFastAdapter = FastAdapter.with(installedItemAdapter)
installedFastAdapter?.setHasStableIds(true)
binding.installedRecycler.apply {
layoutManager = LinearLayoutManager(requireContext())
isMotionEventSplittingEnabled = false
isVerticalScrollBarEnabled = false
recycledViewPool.setMaxRecycledViews(AppListAdapter.ViewType.PRODUCT.ordinal, 30)
adapter = installedFastAdapter
FastScrollerBuilder(this)
.useMd2Style()
@ -64,13 +77,8 @@ class InstalledFragment : MainNavFragmentX() {
updatedFastAdapter?.setHasStableIds(true)
binding.updatedRecycler.apply {
layoutManager = LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false)
isMotionEventSplittingEnabled = false
isVerticalScrollBarEnabled = false
recycledViewPool.setMaxRecycledViews(AppListAdapter.ViewType.PRODUCT.ordinal, 30)
adapter = updatedFastAdapter
FastScrollerBuilder(this)
.useMd2Style()
.setThumbDrawable(this.context.getDrawableCompat(R.drawable.scrollbar_thumb))
.build()
}
return binding.root
}

View File

@ -8,16 +8,18 @@ import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.looker.droidify.R
import com.looker.droidify.database.Product
import com.looker.droidify.databinding.FragmentLatestXBinding
import com.looker.droidify.entity.ProductItem
import com.looker.droidify.entity.Repository
import com.looker.droidify.ui.adapters.AppListAdapter
import com.looker.droidify.ui.items.HAppItem
import com.looker.droidify.ui.items.VAppItem
import com.looker.droidify.ui.viewmodels.MainNavFragmentViewModelX
import com.looker.droidify.utility.PRODUCT_ASYNC_DIFFER_CONFIG
import com.looker.droidify.utility.RxUtils
import com.looker.droidify.utility.extension.resources.getDrawableCompat
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.adapters.ItemAdapter
import com.mikepenz.fastadapter.paged.PagedModelAdapter
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import me.zhanghai.android.fastscroll.FastScrollerBuilder
@ -27,11 +29,12 @@ class LatestFragment : MainNavFragmentX() {
override lateinit var viewModel: MainNavFragmentViewModelX
private lateinit var binding: FragmentLatestXBinding
private val updatedItemAdapter = ItemAdapter<VAppItem>()
private lateinit var updatedItemAdapter: PagedModelAdapter<Product, VAppItem>
private var updatedFastAdapter: FastAdapter<VAppItem>? = null
private val newItemAdapter = ItemAdapter<HAppItem>()
private lateinit var newItemAdapter: PagedModelAdapter<Product, HAppItem>
private var newFastAdapter: FastAdapter<HAppItem>? = null
// TODO replace the source with one that get a certain amount of updated apps
override val source = Source.AVAILABLE
private var repositories: Map<Long, Repository> = mapOf()
@ -48,12 +51,23 @@ class LatestFragment : MainNavFragmentX() {
viewModel = ViewModelProvider(this, viewModelFactory)
.get(MainNavFragmentViewModelX::class.java)
updatedItemAdapter = PagedModelAdapter<Product, VAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) {
it.data_item?.let { item ->
VAppItem(item, repositories[it.repository_id])
}
}
newItemAdapter = PagedModelAdapter<Product, HAppItem>(PRODUCT_ASYNC_DIFFER_CONFIG) {
it.data_item?.let { item ->
// TODO filter for only new apps and add placeholder
HAppItem(item, repositories[it.repository_id])
}
}
updatedFastAdapter = FastAdapter.with(updatedItemAdapter)
updatedFastAdapter?.setHasStableIds(true)
binding.updatedRecycler.apply {
layoutManager = LinearLayoutManager(requireContext())
isMotionEventSplittingEnabled = false
isVerticalScrollBarEnabled = false
recycledViewPool.setMaxRecycledViews(AppListAdapter.ViewType.PRODUCT.ordinal, 30)
adapter = updatedFastAdapter
FastScrollerBuilder(this)
.useMd2Style()
@ -64,13 +78,8 @@ class LatestFragment : MainNavFragmentX() {
newFastAdapter?.setHasStableIds(true)
binding.newRecycler.apply {
layoutManager = LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false)
isMotionEventSplittingEnabled = false
isVerticalScrollBarEnabled = false
recycledViewPool.setMaxRecycledViews(AppListAdapter.ViewType.PRODUCT.ordinal, 30)
adapter = newFastAdapter
FastScrollerBuilder(this)
.useMd2Style()
.setThumbDrawable(this.context.getDrawableCompat(R.drawable.scrollbar_thumb))
.build()
}
return binding.root
}

View File

@ -1,14 +1,18 @@
package com.looker.droidify.ui.viewmodels
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.paging.DataSource
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
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.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@ -68,15 +72,36 @@ class MainNavFragmentViewModelX(val db: DatabaseX, source: Source) : ViewModel()
}
}
var productsList = MediatorLiveData<MutableList<Product>>()
var productsList: LiveData<PagedList<Product>>
init {
val pagedListConfig = PagedList.Config.Builder()
.setPageSize(30)
.setPrefetchDistance(30)
.setEnablePlaceholders(false)
.build()
val request = request(source)
productsList = LivePagedListBuilder(
db.productDao.queryList(
installed = request.installed,
updates = request.updates,
searchQuery = request.searchQuery,
section = request.section,
order = request.order,
numberOfItems = request.numberOfItems
), pagedListConfig
).build()
}
fun fillList(source: Source) {
viewModelScope.launch {
productsList.value = query(request(source))?.toMutableList()
CoroutineScope(Dispatchers.Default).launch {
// productsList = query(request(source))
}
}
private suspend fun query(request: Request): List<Product>? {
private suspend fun query(request: Request): DataSource.Factory<Int, Product> {
return withContext(Dispatchers.Default) {
db.productDao
.queryList(

View File

@ -10,6 +10,8 @@ import android.content.res.Configuration
import android.database.Cursor
import android.graphics.drawable.Drawable
import android.os.Build
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.looker.droidify.*
@ -234,4 +236,23 @@ fun jsonGenerate(callback: (JsonGenerator) -> Unit): ByteArray {
val outputStream = ByteArrayOutputStream()
Json.factory.createGenerator(outputStream).use { it.writeDictionary(callback) }
return outputStream.toByteArray()
}
}
val PRODUCT_ASYNC_DIFFER_CONFIG
get() = AsyncDifferConfig.Builder(object :
DiffUtil.ItemCallback<com.looker.droidify.database.Product>() {
override fun areItemsTheSame(
oldItem: com.looker.droidify.database.Product,
newItem: com.looker.droidify.database.Product
): Boolean {
return oldItem.repository_id == newItem.repository_id
&& oldItem.package_name == newItem.package_name
}
override fun areContentsTheSame(
oldItem: com.looker.droidify.database.Product,
newItem: com.looker.droidify.database.Product
): Boolean {
return oldItem.data_item == newItem.data_item
}
}).build()