Cleanup filterable list implementation

This commit is contained in:
topjohnwu 2023-03-09 17:45:00 -08:00
parent d130aa02a1
commit 382568bd3c
4 changed files with 31 additions and 44 deletions

View File

@ -1,55 +1,38 @@
package com.topjohnwu.magisk.databinding
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import java.util.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.Collections
class FilterableDiffObservableList<T>(
callback: Callback<T>
callback: Callback<T>,
private val scope: CoroutineScope
) : DiffObservableList<T>(callback) {
var filter: ((T) -> Boolean)? = null
set(value) {
field = value
queueUpdate()
}
@Volatile
private var sublist: MutableList<T> = super.list
private var sublist: MutableList<T> = list
private var job: Job? = null
// ---
private val ui by lazy { Handler(Looper.getMainLooper()) }
private val handler = Handler(HandlerThread("List${hashCode()}").apply { start() }.looper)
private val updater = Runnable {
val filter = filter ?: { true }
val newList = super.list.filter(filter)
val diff = synchronized(this) { doCalculateDiff(sublist, newList) }
ui.post {
sublist = Collections.synchronizedList(newList)
diff.dispatchUpdatesTo(listCallback)
fun filter(filter: (T) -> Boolean) {
job?.cancel()
job = scope.launch(Dispatchers.Default) {
val newList = list.filter(filter)
val diff = synchronized(this) { doCalculateDiff(sublist, newList) }
withContext(Dispatchers.Main) {
sublist = Collections.synchronizedList(newList)
diff.dispatchUpdatesTo(listCallback)
}
}
}
private fun queueUpdate() {
handler.removeCallbacks(updater)
handler.post(updater)
}
fun hasFilter() = filter != null
fun filter(switch: (T) -> Boolean) {
filter = switch
}
fun reset() {
filter = null
}
// ---
override fun get(index: Int): T {
return sublist.get(index)
return sublist[index]
}
override fun add(element: T): Boolean {

View File

@ -1,7 +1,9 @@
package com.topjohnwu.magisk.databinding
import kotlinx.coroutines.CoroutineScope
fun <T : AnyDiffRvItem> diffListOf() =
DiffObservableList(DiffRvItem.callback<T>())
fun <T : AnyDiffRvItem> filterableListOf() =
FilterableDiffObservableList(DiffRvItem.callback<T>())
fun <T : AnyDiffRvItem> filterableListOf(scope: CoroutineScope) =
FilterableDiffObservableList(DiffRvItem.callback<T>(), scope)

View File

@ -15,8 +15,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.topjohnwu.magisk.BR
class RvItemAdapter<T: RvItem>(
internal val items: List<T>,
private val extraBindings: SparseArray<*>?
val items: List<T>,
val extraBindings: SparseArray<*>?
) : RecyclerView.Adapter<RvItemAdapter.ViewHolder>() {
private var lifecycleOwner: LifecycleOwner? = null
@ -113,7 +113,8 @@ inline fun bindExtra(body: (SparseArray<Any?>) -> Unit) = SparseArray<Any?>().al
@BindingAdapter("items", "extraBindings", requireAll = false)
fun <T: RvItem> RecyclerView.setAdapter(items: List<T>?, extraBindings: SparseArray<*>?) {
if (items != null) {
if ((adapter as? RvItemAdapter<T>)?.items !== items) {
val rva = (adapter as? RvItemAdapter<*>)
if (rva == null || rva.items !== items || rva.extraBindings !== extraBindings) {
adapter = RvItemAdapter(items, extraBindings)
}
}

View File

@ -3,6 +3,7 @@ package com.topjohnwu.magisk.ui.deny
import android.annotation.SuppressLint
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
import androidx.databinding.Bindable
import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.arch.AsyncLoadViewModel
import com.topjohnwu.magisk.core.di.AppContext
@ -37,7 +38,7 @@ class DenyListViewModel : AsyncLoadViewModel() {
query()
}
val items = filterableListOf<DenyListRvItem>()
val items = filterableListOf<DenyListRvItem>(viewModelScope)
val extraBindings = bindExtra {
it.put(BR.viewModel, this)
}
@ -68,7 +69,7 @@ class DenyListViewModel : AsyncLoadViewModel() {
query()
}
fun query() {
private fun query() {
items.filter {
fun filterSystem() = isShowSystem || !it.info.isSystemApp()