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

View File

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

View File

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

View File

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