Move file reads in RV adapters to a background dispatcher
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
parent
cdf8e76ac6
commit
285bf9d929
5 changed files with 24 additions and 10 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue