Move file reads in RV adapters to a background dispatcher

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Harsh Shandilya 2021-05-15 12:53:31 +05:30
parent cdf8e76ac6
commit 285bf9d929
No known key found for this signature in database
GPG key ID: 366D7BBAD1031E80
5 changed files with 24 additions and 10 deletions

View file

@ -17,12 +17,16 @@ import dev.msfjarvis.aps.R
import dev.msfjarvis.aps.data.password.PasswordItem import dev.msfjarvis.aps.data.password.PasswordItem
import dev.msfjarvis.aps.util.viewmodel.SearchableRepositoryAdapter import dev.msfjarvis.aps.util.viewmodel.SearchableRepositoryAdapter
import dev.msfjarvis.aps.util.viewmodel.stableId import dev.msfjarvis.aps.util.viewmodel.stableId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
open class PasswordItemRecyclerAdapter : open class PasswordItemRecyclerAdapter(coroutineScope: CoroutineScope) :
SearchableRepositoryAdapter<PasswordItemRecyclerAdapter.PasswordItemViewHolder>( SearchableRepositoryAdapter<PasswordItemRecyclerAdapter.PasswordItemViewHolder>(
R.layout.password_row_layout, R.layout.password_row_layout,
::PasswordItemViewHolder, ::PasswordItemViewHolder,
PasswordItemViewHolder::bind coroutineScope,
PasswordItemViewHolder::bind,
) { ) {
fun makeSelectable(recyclerView: RecyclerView) { fun makeSelectable(recyclerView: RecyclerView) {
@ -48,7 +52,7 @@ open class PasswordItemRecyclerAdapter :
private val folderIndicator: AppCompatImageView = itemView.findViewById(R.id.folder_indicator) private val folderIndicator: AppCompatImageView = itemView.findViewById(R.id.folder_indicator)
lateinit var itemDetails: ItemDetailsLookup.ItemDetails<String> lateinit var itemDetails: ItemDetailsLookup.ItemDetails<String>
fun bind(item: PasswordItem) { suspend fun bind(item: PasswordItem) {
val parentPath = item.fullPathToParent.replace("(^/)|(/$)".toRegex(), "") val parentPath = item.fullPathToParent.replace("(^/)|(/$)".toRegex(), "")
val source = val source =
if (parentPath.isNotEmpty()) { if (parentPath.isNotEmpty()) {
@ -62,7 +66,9 @@ open class PasswordItemRecyclerAdapter :
if (item.type == PasswordItem.TYPE_CATEGORY) { if (item.type == PasswordItem.TYPE_CATEGORY) {
folderIndicator.visibility = View.VISIBLE folderIndicator.visibility = View.VISIBLE
val count = val count =
item.file.listFiles { path -> path.isDirectory || path.extension == "gpg" }?.size ?: 0 withContext(Dispatchers.IO) {
item.file.listFiles { path -> path.isDirectory || path.extension == "gpg" }?.size ?: 0
}
childCount.visibility = if (count > 0) View.VISIBLE else View.GONE childCount.visibility = if (count > 0) View.VISIBLE else View.GONE
childCount.text = "$count" childCount.text = "$count"
} else { } else {

View file

@ -22,6 +22,7 @@ import androidx.core.text.buildSpannedString
import androidx.core.text.underline import androidx.core.text.underline
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.ajalt.timberkt.e import com.github.ajalt.timberkt.e
import com.github.androidpasswordstore.autofillparser.FormOrigin import com.github.androidpasswordstore.autofillparser.FormOrigin
@ -132,8 +133,11 @@ class AutofillFilterView : AppCompatActivity() {
with(binding) { with(binding) {
rvPassword.apply { rvPassword.apply {
adapter = adapter =
SearchableRepositoryAdapter(R.layout.oreo_autofill_filter_row, ::PasswordViewHolder) { SearchableRepositoryAdapter(
item -> R.layout.oreo_autofill_filter_row,
::PasswordViewHolder,
lifecycleScope,
) { item ->
val file = item.file.relativeTo(item.rootDir) val file = item.file.relativeTo(item.rootDir)
val pathToIdentifier = directoryStructure.getPathToIdentifierFor(file) val pathToIdentifier = directoryStructure.getPathToIdentifierFor(file)
val identifier = directoryStructure.getIdentifierFor(file) val identifier = directoryStructure.getIdentifierFor(file)

View file

@ -10,6 +10,7 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.michaelbull.result.onFailure import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.runCatching import com.github.michaelbull.result.runCatching
@ -36,7 +37,7 @@ class SelectFolderFragment : Fragment(R.layout.password_recycler_view) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.fab.hide() binding.fab.hide()
recyclerAdapter = recyclerAdapter =
PasswordItemRecyclerAdapter().onItemClicked { _, item -> PasswordItemRecyclerAdapter(lifecycleScope).onItemClicked { _, item ->
listener.onFragmentInteraction(item) listener.onFragmentInteraction(item)
} }
binding.passRecycler.apply { binding.passRecycler.apply {

View file

@ -133,7 +133,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
} }
recyclerAdapter = recyclerAdapter =
PasswordItemRecyclerAdapter() PasswordItemRecyclerAdapter(lifecycleScope)
.onItemClicked { _, item -> listener.onFragmentInteraction(item) } .onItemClicked { _, item -> listener.onFragmentInteraction(item) }
.onSelectionChanged { selection -> .onSelectionChanged { selection ->
// In order to not interfere with drag selection, we disable the // In order to not interfere with drag selection, we disable the

View file

@ -36,6 +36,7 @@ import java.io.File
import java.text.Collator import java.text.Collator
import java.util.Locale import java.util.Locale
import java.util.Stack import java.util.Stack
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
@ -48,6 +49,7 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield import kotlinx.coroutines.yield
import me.zhanghai.android.fastscroll.PopupTextProvider import me.zhanghai.android.fastscroll.PopupTextProvider
@ -376,7 +378,8 @@ private object PasswordItemDiffCallback : DiffUtil.ItemCallback<PasswordItem>()
open class SearchableRepositoryAdapter<T : RecyclerView.ViewHolder>( open class SearchableRepositoryAdapter<T : RecyclerView.ViewHolder>(
private val layoutRes: Int, private val layoutRes: Int,
private val viewHolderCreator: (view: View) -> T, private val viewHolderCreator: (view: View) -> T,
private val viewHolderBinder: T.(item: PasswordItem) -> Unit private val coroutineScope: CoroutineScope,
private val viewHolderBinder: suspend T.(item: PasswordItem) -> Unit,
) : ListAdapter<PasswordItem, T>(PasswordItemDiffCallback), PopupTextProvider { ) : ListAdapter<PasswordItem, T>(PasswordItemDiffCallback), PopupTextProvider {
fun <T : ItemDetailsLookup<String>> makeSelectable( fun <T : ItemDetailsLookup<String>> makeSelectable(
@ -454,7 +457,7 @@ open class SearchableRepositoryAdapter<T : RecyclerView.ViewHolder>(
final override fun onBindViewHolder(holder: T, position: Int) { final override fun onBindViewHolder(holder: T, position: Int) {
val item = getItem(position) val item = getItem(position)
holder.apply { holder.apply {
viewHolderBinder.invoke(this, item) coroutineScope.launch(Dispatchers.Main.immediate) { viewHolderBinder.invoke(holder, item) }
selectionTracker?.let { itemView.isSelected = it.isSelected(item.stableId) } selectionTracker?.let { itemView.isSelected = it.isSelected(item.stableId) }
itemView.setOnClickListener { itemView.setOnClickListener {
// Do not emit custom click events while the user is selecting items. // Do not emit custom click events while the user is selecting items.