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.databinding.ObservableList
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListUpdateCallback import androidx.recyclerview.widget.ListUpdateCallback
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.AbstractList import java.util.AbstractList
open class DiffObservableList<T : DiffItem<*>> // Only expose the immutable List types
: AbstractList<T>(), ObservableList<T>, ListUpdateCallback { 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() protected var list: List<T> = emptyList()
private set private set
@ -21,7 +44,7 @@ open class DiffObservableList<T : DiffItem<*>>
override fun get(index: Int) = list[index] 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) return doCalculateDiff(list, newItems)
} }
@ -48,13 +71,13 @@ open class DiffObservableList<T : DiffItem<*>>
} }
@MainThread @MainThread
fun update(newItems: List<T>, diffResult: DiffUtil.DiffResult) { override fun update(newItems: List<T>, diffResult: DiffUtil.DiffResult) {
list = ArrayList(newItems) list = ArrayList(newItems)
diffResult.dispatchUpdatesTo(this) diffResult.dispatchUpdatesTo(this)
} }
@WorkerThread @WorkerThread
suspend fun update(newItems: List<T>) { override suspend fun update(newItems: List<T>) {
val diffResult = calculateDiff(newItems) val diffResult = calculateDiff(newItems)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
update(newItems, diffResult) update(newItems, diffResult)
@ -87,3 +110,35 @@ open class DiffObservableList<T : DiffItem<*>>
listeners.notifyRemoved(this, position, count) 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 return this
} }
fun insertList(list: ObservableList<out T>): MergeObservableList<T> { fun insertList(list: List<T>): MergeObservableList<T> {
val idx = size val idx = size
lists.add(list) lists.add(list)
++modCount ++modCount
(list as ObservableList<T>).addOnListChangedCallback(callback) (list as? ObservableList<T>)?.addOnListChangedCallback(callback)
if (list.isNotEmpty()) if (list.isNotEmpty())
listeners.notifyInserted(this, idx, list.size) listeners.notifyInserted(this, idx, list.size)
return this return this
@ -72,11 +72,11 @@ class MergeObservableList<T> : AbstractList<T>(), ObservableList<T> {
return false return false
} }
fun removeList(listToRemove: ObservableList<out T>): Boolean { fun removeList(listToRemove: List<T>): Boolean {
var idx = 0 var idx = 0
for ((i, list) in lists.withIndex()) { for ((i, list) in lists.withIndex()) {
if (listToRemove === list) { if (listToRemove === list) {
(list as ObservableList<T>).removeOnListChangedCallback(callback) (list as? ObservableList<T>)?.removeOnListChangedCallback(callback)
lists.removeAt(i) lists.removeAt(i)
++modCount ++modCount
listeners.notifyRemoved(this, idx, list.size) listeners.notifyRemoved(this, idx, list.size)
@ -90,8 +90,8 @@ class MergeObservableList<T> : AbstractList<T>(), ObservableList<T> {
override fun clear() { override fun clear() {
val sz = size val sz = size
for (list in lists) { for (list in lists) {
if (list is ObservableList<*>) { if (list is ObservableList) {
(list as ObservableList<T>).removeOnListChangedCallback(callback) list.removeOnListChangedCallback(callback)
} }
} }
++modCount ++modCount

View File

@ -8,8 +8,8 @@ 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
import com.topjohnwu.magisk.core.ktx.concurrentMap import com.topjohnwu.magisk.core.ktx.concurrentMap
import com.topjohnwu.magisk.databinding.FilterableDiffObservableList
import com.topjohnwu.magisk.databinding.bindExtra import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.databinding.filterList
import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -38,7 +38,7 @@ class DenyListViewModel : AsyncLoadViewModel() {
query() query()
} }
val items = FilterableDiffObservableList<DenyListRvItem>(viewModelScope) val items = filterList<DenyListRvItem>(viewModelScope)
val extraBindings = bindExtra { val extraBindings = bindExtra {
it.put(BR.viewModel, this) 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.repository.LogRepository
import com.topjohnwu.magisk.core.utils.MediaStoreUtils import com.topjohnwu.magisk.core.utils.MediaStoreUtils
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream 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.bindExtra
import com.topjohnwu.magisk.databinding.diffList
import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.events.SnackbarEvent import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.view.TextItem import com.topjohnwu.magisk.view.TextItem
@ -37,13 +37,13 @@ class LogViewModel(
// --- su log // --- su log
val items = DiffObservableList<SuLogRvItem>() val items = diffList<SuLogRvItem>()
val extraBindings = bindExtra { val extraBindings = bindExtra {
it.put(BR.viewModel, this) it.put(BR.viewModel, this)
} }
// --- magisk log // --- magisk log
val logs = DiffObservableList<LogRvItem>() val logs = diffList<LogRvItem>()
var magiskLogRaw = " " var magiskLogRaw = " "
override suspend fun doLoadWork() { 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.base.ContentResultCallback
import com.topjohnwu.magisk.core.model.module.LocalModule import com.topjohnwu.magisk.core.model.module.LocalModule
import com.topjohnwu.magisk.core.model.module.OnlineModule 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.MergeObservableList
import com.topjohnwu.magisk.databinding.RvItem import com.topjohnwu.magisk.databinding.RvItem
import com.topjohnwu.magisk.databinding.bindExtra import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.databinding.diffList
import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.dialog.LocalModuleInstallDialog import com.topjohnwu.magisk.dialog.LocalModuleInstallDialog
import com.topjohnwu.magisk.dialog.OnlineModuleInstallDialog import com.topjohnwu.magisk.dialog.OnlineModuleInstallDialog
@ -27,7 +27,7 @@ class ModuleViewModel : AsyncLoadViewModel() {
val bottomBarBarrierIds = intArrayOf(R.id.module_update, R.id.module_remove) 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 items = MergeObservableList<RvItem>()
val extraBindings = bindExtra { val extraBindings = bindExtra {

View File

@ -34,7 +34,7 @@ class SuperuserViewModel(
private val itemNoData = TextItem(R.string.superuser_policy_none) private val itemNoData = TextItem(R.string.superuser_policy_none)
private val itemsHelpers = ObservableArrayList<TextItem>() private val itemsHelpers = ObservableArrayList<TextItem>()
private val itemsPolicies = DiffObservableList<PolicyRvItem>() private val itemsPolicies = diffList<PolicyRvItem>()
val items = MergeObservableList<RvItem>() val items = MergeObservableList<RvItem>()
.insertList(itemsHelpers) .insertList(itemsHelpers)
@ -105,8 +105,10 @@ class SuperuserViewModel(
fun deletePressed(item: PolicyRvItem) { fun deletePressed(item: PolicyRvItem) {
fun updateState() = viewModelScope.launch { fun updateState() = viewModelScope.launch {
db.delete(item.item.uid) db.delete(item.item.uid)
itemsPolicies.removeAll { it.itemSameAs(item) } val list = ArrayList(itemsPolicies)
if (itemsPolicies.isEmpty() && itemsHelpers.isEmpty()) { list.removeAll { it.itemSameAs(item) }
itemsPolicies.update(list)
if (list.isEmpty() && itemsHelpers.isEmpty()) {
itemsHelpers.add(itemNoData) itemsHelpers.add(itemNoData)
} }
} }