diff --git a/app/src/main/java/com/topjohnwu/magisk/databinding/DiffObservableList.kt b/app/src/main/java/com/topjohnwu/magisk/databinding/DiffObservableList.kt index 441e0f5c5..01493d7a1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/databinding/DiffObservableList.kt +++ b/app/src/main/java/com/topjohnwu/magisk/databinding/DiffObservableList.kt @@ -6,12 +6,35 @@ import androidx.databinding.ListChangeRegistry import androidx.databinding.ObservableList import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListUpdateCallback +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.util.AbstractList -open class DiffObservableList> - : AbstractList(), ObservableList, ListUpdateCallback { +// Only expose the immutable List types +interface DiffList> : List { + fun calculateDiff(newItems: List): DiffUtil.DiffResult + + @MainThread + fun update(newItems: List, diffResult: DiffUtil.DiffResult) + + @WorkerThread + suspend fun update(newItems: List) +} + +interface FilterList> : DiffList { + fun filter(filter: (T) -> Boolean) +} + +fun > diffList(): DiffList = DiffObservableList() + +fun > filterList(scope: CoroutineScope): FilterList = + FilterableDiffObservableList(scope) + +private open class DiffObservableList> + : AbstractList(), ObservableList, DiffList, ListUpdateCallback { protected var list: List = emptyList() private set @@ -21,7 +44,7 @@ open class DiffObservableList> override fun get(index: Int) = list[index] - fun calculateDiff(newItems: List): DiffUtil.DiffResult { + override fun calculateDiff(newItems: List): DiffUtil.DiffResult { return doCalculateDiff(list, newItems) } @@ -48,13 +71,13 @@ open class DiffObservableList> } @MainThread - fun update(newItems: List, diffResult: DiffUtil.DiffResult) { + override fun update(newItems: List, diffResult: DiffUtil.DiffResult) { list = ArrayList(newItems) diffResult.dispatchUpdatesTo(this) } @WorkerThread - suspend fun update(newItems: List) { + override suspend fun update(newItems: List) { val diffResult = calculateDiff(newItems) withContext(Dispatchers.Main) { update(newItems, diffResult) @@ -87,3 +110,35 @@ open class DiffObservableList> listeners.notifyRemoved(this, position, count) } } + +private class FilterableDiffObservableList>( + private val scope: CoroutineScope +) : DiffObservableList(), FilterList { + + private var sublist: List = emptyList() + private var job: Job? = null + + // --- + + override fun filter(filter: (T) -> Boolean) { + job?.cancel() + job = scope.launch(Dispatchers.Default) { + val oldList = sublist + val newList = list.filter(filter) + val diff = doCalculateDiff(oldList, newList) + withContext(Dispatchers.Main) { + sublist = newList + diff.dispatchUpdatesTo(this@FilterableDiffObservableList) + } + } + } + + // --- + + override fun get(index: Int): T { + return sublist[index] + } + + override val size: Int + get() = sublist.size +} diff --git a/app/src/main/java/com/topjohnwu/magisk/databinding/FilterableDiffObservableList.kt b/app/src/main/java/com/topjohnwu/magisk/databinding/FilterableDiffObservableList.kt deleted file mode 100644 index 786769bda..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/databinding/FilterableDiffObservableList.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.topjohnwu.magisk.databinding - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -open class FilterableDiffObservableList>( - private val scope: CoroutineScope -) : DiffObservableList() { - - private var sublist: List = emptyList() - private var job: Job? = null - - // --- - - fun filter(filter: (T) -> Boolean) { - job?.cancel() - job = scope.launch(Dispatchers.Default) { - val oldList = sublist - val newList = list.filter(filter) - val diff = doCalculateDiff(oldList, newList) - withContext(Dispatchers.Main) { - sublist = newList - diff.dispatchUpdatesTo(this@FilterableDiffObservableList) - } - } - } - - // --- - - override fun get(index: Int): T { - return sublist[index] - } - - override val size: Int - get() = sublist.size -} diff --git a/app/src/main/java/com/topjohnwu/magisk/databinding/MergeObservableList.kt b/app/src/main/java/com/topjohnwu/magisk/databinding/MergeObservableList.kt index 582a06235..75543be9d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/databinding/MergeObservableList.kt +++ b/app/src/main/java/com/topjohnwu/magisk/databinding/MergeObservableList.kt @@ -46,11 +46,11 @@ class MergeObservableList : AbstractList(), ObservableList { return this } - fun insertList(list: ObservableList): MergeObservableList { + fun insertList(list: List): MergeObservableList { val idx = size lists.add(list) ++modCount - (list as ObservableList).addOnListChangedCallback(callback) + (list as? ObservableList)?.addOnListChangedCallback(callback) if (list.isNotEmpty()) listeners.notifyInserted(this, idx, list.size) return this @@ -72,11 +72,11 @@ class MergeObservableList : AbstractList(), ObservableList { return false } - fun removeList(listToRemove: ObservableList): Boolean { + fun removeList(listToRemove: List): Boolean { var idx = 0 for ((i, list) in lists.withIndex()) { if (listToRemove === list) { - (list as ObservableList).removeOnListChangedCallback(callback) + (list as? ObservableList)?.removeOnListChangedCallback(callback) lists.removeAt(i) ++modCount listeners.notifyRemoved(this, idx, list.size) @@ -90,8 +90,8 @@ class MergeObservableList : AbstractList(), ObservableList { override fun clear() { val sz = size for (list in lists) { - if (list is ObservableList<*>) { - (list as ObservableList).removeOnListChangedCallback(callback) + if (list is ObservableList) { + list.removeOnListChangedCallback(callback) } } ++modCount diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/deny/DenyListViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/deny/DenyListViewModel.kt index 6e73ca267..127769c5d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/deny/DenyListViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/deny/DenyListViewModel.kt @@ -8,8 +8,8 @@ import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.arch.AsyncLoadViewModel import com.topjohnwu.magisk.core.di.AppContext import com.topjohnwu.magisk.core.ktx.concurrentMap -import com.topjohnwu.magisk.databinding.FilterableDiffObservableList import com.topjohnwu.magisk.databinding.bindExtra +import com.topjohnwu.magisk.databinding.filterList import com.topjohnwu.magisk.databinding.set import com.topjohnwu.superuser.Shell import kotlinx.coroutines.Dispatchers @@ -38,7 +38,7 @@ class DenyListViewModel : AsyncLoadViewModel() { query() } - val items = FilterableDiffObservableList(viewModelScope) + val items = filterList(viewModelScope) val extraBindings = bindExtra { it.put(BR.viewModel, this) } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt index 047496c29..0085a2ed1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt @@ -13,8 +13,8 @@ import com.topjohnwu.magisk.core.ktx.toTime import com.topjohnwu.magisk.core.repository.LogRepository import com.topjohnwu.magisk.core.utils.MediaStoreUtils import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream -import com.topjohnwu.magisk.databinding.DiffObservableList import com.topjohnwu.magisk.databinding.bindExtra +import com.topjohnwu.magisk.databinding.diffList import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.events.SnackbarEvent import com.topjohnwu.magisk.view.TextItem @@ -37,13 +37,13 @@ class LogViewModel( // --- su log - val items = DiffObservableList() + val items = diffList() val extraBindings = bindExtra { it.put(BR.viewModel, this) } // --- magisk log - val logs = DiffObservableList() + val logs = diffList() var magiskLogRaw = " " override suspend fun doLoadWork() { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt index 65ba2b459..ec9cbb6b6 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt @@ -10,10 +10,10 @@ import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.base.ContentResultCallback import com.topjohnwu.magisk.core.model.module.LocalModule import com.topjohnwu.magisk.core.model.module.OnlineModule -import com.topjohnwu.magisk.databinding.DiffObservableList import com.topjohnwu.magisk.databinding.MergeObservableList import com.topjohnwu.magisk.databinding.RvItem import com.topjohnwu.magisk.databinding.bindExtra +import com.topjohnwu.magisk.databinding.diffList import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.dialog.LocalModuleInstallDialog import com.topjohnwu.magisk.dialog.OnlineModuleInstallDialog @@ -27,7 +27,7 @@ class ModuleViewModel : AsyncLoadViewModel() { val bottomBarBarrierIds = intArrayOf(R.id.module_update, R.id.module_remove) - private val itemsInstalled = DiffObservableList() + private val itemsInstalled = diffList() val items = MergeObservableList() val extraBindings = bindExtra { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt index 68697e26c..06c91af8c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt @@ -34,7 +34,7 @@ class SuperuserViewModel( private val itemNoData = TextItem(R.string.superuser_policy_none) private val itemsHelpers = ObservableArrayList() - private val itemsPolicies = DiffObservableList() + private val itemsPolicies = diffList() val items = MergeObservableList() .insertList(itemsHelpers) @@ -105,8 +105,10 @@ class SuperuserViewModel( fun deletePressed(item: PolicyRvItem) { fun updateState() = viewModelScope.launch { db.delete(item.item.uid) - itemsPolicies.removeAll { it.itemSameAs(item) } - if (itemsPolicies.isEmpty() && itemsHelpers.isEmpty()) { + val list = ArrayList(itemsPolicies) + list.removeAll { it.itemSameAs(item) } + itemsPolicies.update(list) + if (list.isEmpty() && itemsHelpers.isEmpty()) { itemsHelpers.add(itemNoData) } }