Fix crash when revoke root permission

This commit is contained in:
topjohnwu 2023-04-06 00:40:26 -07:00
parent 69b66ef637
commit 7be958e35d
7 changed files with 78 additions and 60 deletions

View File

@ -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<T : DiffItem<*>>
: AbstractList<T>(), ObservableList<T>, ListUpdateCallback {
// Only expose the immutable List types
interface DiffList<T : DiffItem<*>> : List<T> {
fun calculateDiff(newItems: List<T>): DiffUtil.DiffResult
@MainThread
fun update(newItems: List<T>, diffResult: DiffUtil.DiffResult)
@WorkerThread
suspend fun update(newItems: List<T>)
}
interface FilterList<T : DiffItem<*>> : DiffList<T> {
fun filter(filter: (T) -> Boolean)
}
fun <T : DiffItem<*>> diffList(): DiffList<T> = DiffObservableList()
fun <T : DiffItem<*>> filterList(scope: CoroutineScope): FilterList<T> =
FilterableDiffObservableList(scope)
private open class DiffObservableList<T : DiffItem<*>>
: AbstractList<T>(), ObservableList<T>, DiffList<T>, ListUpdateCallback {
protected var list: List<T> = emptyList()
private set
@ -21,7 +44,7 @@ open class DiffObservableList<T : DiffItem<*>>
override fun get(index: Int) = list[index]
fun calculateDiff(newItems: List<T>): DiffUtil.DiffResult {
override fun calculateDiff(newItems: List<T>): DiffUtil.DiffResult {
return doCalculateDiff(list, newItems)
}
@ -48,13 +71,13 @@ open class DiffObservableList<T : DiffItem<*>>
}
@MainThread
fun update(newItems: List<T>, diffResult: DiffUtil.DiffResult) {
override fun update(newItems: List<T>, diffResult: DiffUtil.DiffResult) {
list = ArrayList(newItems)
diffResult.dispatchUpdatesTo(this)
}
@WorkerThread
suspend fun update(newItems: List<T>) {
override suspend fun update(newItems: List<T>) {
val diffResult = calculateDiff(newItems)
withContext(Dispatchers.Main) {
update(newItems, diffResult)
@ -87,3 +110,35 @@ open class DiffObservableList<T : DiffItem<*>>
listeners.notifyRemoved(this, position, count)
}
}
private class FilterableDiffObservableList<T : DiffItem<*>>(
private val scope: CoroutineScope
) : DiffObservableList<T>(), FilterList<T> {
private var sublist: List<T> = 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
}

View File

@ -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<T : DiffItem<*>>(
private val scope: CoroutineScope
) : DiffObservableList<T>() {
private var sublist: List<T> = 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
}

View File

@ -46,11 +46,11 @@ class MergeObservableList<T> : AbstractList<T>(), ObservableList<T> {
return this
}
fun insertList(list: ObservableList<out T>): MergeObservableList<T> {
fun insertList(list: List<T>): MergeObservableList<T> {
val idx = size
lists.add(list)
++modCount
(list as ObservableList<T>).addOnListChangedCallback(callback)
(list as? ObservableList<T>)?.addOnListChangedCallback(callback)
if (list.isNotEmpty())
listeners.notifyInserted(this, idx, list.size)
return this
@ -72,11 +72,11 @@ class MergeObservableList<T> : AbstractList<T>(), ObservableList<T> {
return false
}
fun removeList(listToRemove: ObservableList<out T>): Boolean {
fun removeList(listToRemove: List<T>): Boolean {
var idx = 0
for ((i, list) in lists.withIndex()) {
if (listToRemove === list) {
(list as ObservableList<T>).removeOnListChangedCallback(callback)
(list as? ObservableList<T>)?.removeOnListChangedCallback(callback)
lists.removeAt(i)
++modCount
listeners.notifyRemoved(this, idx, list.size)
@ -90,8 +90,8 @@ class MergeObservableList<T> : AbstractList<T>(), ObservableList<T> {
override fun clear() {
val sz = size
for (list in lists) {
if (list is ObservableList<*>) {
(list as ObservableList<T>).removeOnListChangedCallback(callback)
if (list is ObservableList) {
list.removeOnListChangedCallback(callback)
}
}
++modCount

View File

@ -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<DenyListRvItem>(viewModelScope)
val items = filterList<DenyListRvItem>(viewModelScope)
val extraBindings = bindExtra {
it.put(BR.viewModel, this)
}

View File

@ -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<SuLogRvItem>()
val items = diffList<SuLogRvItem>()
val extraBindings = bindExtra {
it.put(BR.viewModel, this)
}
// --- magisk log
val logs = DiffObservableList<LogRvItem>()
val logs = diffList<LogRvItem>()
var magiskLogRaw = " "
override suspend fun doLoadWork() {

View File

@ -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<LocalModuleRvItem>()
private val itemsInstalled = diffList<LocalModuleRvItem>()
val items = MergeObservableList<RvItem>()
val extraBindings = bindExtra {

View File

@ -34,7 +34,7 @@ class SuperuserViewModel(
private val itemNoData = TextItem(R.string.superuser_policy_none)
private val itemsHelpers = ObservableArrayList<TextItem>()
private val itemsPolicies = DiffObservableList<PolicyRvItem>()
private val itemsPolicies = diffList<PolicyRvItem>()
val items = MergeObservableList<RvItem>()
.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)
}
}