1
0
mirror of https://github.com/topjohnwu/Magisk.git synced 2025-03-26 23:41:01 +00:00

Cleanup RvItems

This commit is contained in:
topjohnwu 2021-09-01 00:14:55 -07:00
parent f5c982355a
commit 067248da75
16 changed files with 117 additions and 158 deletions

@ -1,7 +1,8 @@
package com.topjohnwu.magisk.arch
import androidx.databinding.ViewDataBinding
import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.databinding.AnyDiffRvItem
import com.topjohnwu.magisk.databinding.DiffRvItem
import com.topjohnwu.magisk.databinding.RvItem
import com.topjohnwu.magisk.utils.DiffObservableList
import com.topjohnwu.magisk.utils.FilterableDiffObservableList
@ -9,23 +10,14 @@ import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter
import me.tatarka.bindingcollectionadapter2.ItemBinding
import me.tatarka.bindingcollectionadapter2.OnItemBind
fun <T : ComparableRvItem<*>> diffListOf(
vararg newItems: T
) = diffListOf(newItems.toList())
fun <T : AnyDiffRvItem> diffListOf() =
DiffObservableList(DiffRvItem.callback<T>())
fun <T : ComparableRvItem<*>> diffListOf(
newItems: List<T>
) = DiffObservableList(object : DiffObservableList.Callback<T> {
override fun areItemsTheSame(oldItem: T, newItem: T) = oldItem.genericItemSameAs(newItem)
override fun areContentsTheSame(oldItem: T, newItem: T) = oldItem.genericContentSameAs(newItem)
}).also { it.update(newItems) }
fun <T : AnyDiffRvItem> diffListOf(newItems: List<T>) =
DiffObservableList(DiffRvItem.callback<T>()).also { it.update(newItems) }
fun <T : ComparableRvItem<*>> filterableListOf(
vararg newItems: T
) = FilterableDiffObservableList(object : DiffObservableList.Callback<T> {
override fun areItemsTheSame(oldItem: T, newItem: T) = oldItem.genericItemSameAs(newItem)
override fun areContentsTheSame(oldItem: T, newItem: T) = oldItem.genericContentSameAs(newItem)
}).also { it.update(newItems.toList()) }
fun <T : AnyDiffRvItem> filterableListOf() =
FilterableDiffObservableList(DiffRvItem.callback<T>())
fun <T : RvItem> adapterOf() = object : BindingRecyclerViewAdapter<T>() {
override fun onBindBinding(

@ -7,13 +7,15 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.*
class LocalModule(path: String) : Module() {
override var id: String = ""
override var name: String = ""
override var author: String = ""
override var version: String = ""
override var versionCode: Int = -1
override var description: String = ""
data class LocalModule(
private val path: String,
override var id: String = "",
override var name: String = "",
override var author: String = "",
override var version: String = "",
override var versionCode: Int = -1,
override var description: String = "",
) : Module() {
private val removeFile = SuFile(path, "remove")
private val disableFile = SuFile(path, "disable")

@ -26,38 +26,57 @@ abstract class RvItem {
open fun onBindingBound(binding: ViewDataBinding) {}
}
abstract class ComparableRvItem<in T> : RvItem() {
interface RvContainer<E> {
val item: E
}
// Use Any.equals by default
open fun itemSameAs(other: T) = this == other
// Use compareTo if this is Comparable or assume not same
interface ComparableRv<T> : Comparable<T> {
@Suppress("UNCHECKED_CAST")
fun comparableEqual(o: Any?) =
o != null && o::class == this::class && compareTo(o as T) == 0
}
abstract class DiffRvItem<T> : RvItem() {
// Defer to contentSameAs by default
open fun itemSameAs(other: T) = true
open fun contentSameAs(other: T) =
(this as? Comparable<T>)?.run { compareTo(other) == 0 } ?: false
@Suppress("UNCHECKED_CAST")
open fun genericItemSameAs(other: Any): Boolean = other::class == this::class && itemSameAs(other as T)
@Suppress("UNCHECKED_CAST")
open fun genericContentSameAs(other: Any): Boolean = other::class == this::class && contentSameAs(other as T)
when (this) {
is RvContainer<*> -> item == (other as RvContainer<*>).item
is ComparableRv<*> -> comparableEqual(other)
else -> this == other
}
companion object {
val callback = object : DiffObservableList.Callback<ComparableRvItem<*>> {
private val callback = object : DiffObservableList.Callback<DiffRvItem<Any>> {
override fun areItemsTheSame(
oldItem: ComparableRvItem<*>,
newItem: ComparableRvItem<*>
) = oldItem.genericItemSameAs(newItem)
oldItem: DiffRvItem<Any>,
newItem: DiffRvItem<Any>
): Boolean {
return oldItem::class == newItem::class && oldItem.itemSameAs(newItem)
}
override fun areContentsTheSame(
oldItem: ComparableRvItem<*>,
newItem: ComparableRvItem<*>
) = oldItem.genericContentSameAs(newItem)
oldItem: DiffRvItem<Any>,
newItem: DiffRvItem<Any>
): Boolean {
return oldItem.contentSameAs(newItem)
}
}
@Suppress("UNCHECKED_CAST")
fun <T : AnyDiffRvItem> callback() = callback as DiffObservableList.Callback<T>
}
}
abstract class ObservableItem<T> : ComparableRvItem<T>(), ObservableHost {
typealias AnyDiffRvItem = DiffRvItem<*>
abstract class ObservableDiffRvItem<T> : DiffRvItem<T>(), ObservableHost {
override var callbacks: PropertyChangeRegistry? = null
}
abstract class ObservableRvItem : RvItem(), ObservableHost {
override var callbacks: PropertyChangeRegistry? = null
}

@ -6,11 +6,15 @@ import androidx.core.view.updateLayoutParams
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.databinding.DiffRvItem
import com.topjohnwu.magisk.databinding.LenientRvItem
import com.topjohnwu.magisk.databinding.RvContainer
import kotlin.math.max
class ConsoleItem(val item: String) : ComparableRvItem<ConsoleItem>(), LenientRvItem {
class ConsoleItem(
override val item: String
) : DiffRvItem<ConsoleItem>(), LenientRvItem,
RvContainer<String> {
override val layoutRes = R.layout.item_console_md2
private var parentWidth = -1
@ -31,7 +35,4 @@ class ConsoleItem(val item: String) : ComparableRvItem<ConsoleItem>(), LenientRv
recyclerView.requestLayout()
}
}
override fun contentSameAs(other: ConsoleItem) = itemSameAs(other)
override fun itemSameAs(other: ConsoleItem) = item == other.item
}

@ -5,7 +5,8 @@ import android.view.ViewGroup
import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ObservableItem
import com.topjohnwu.magisk.databinding.ComparableRv
import com.topjohnwu.magisk.databinding.ObservableDiffRvItem
import com.topjohnwu.magisk.ktx.startAnimations
import com.topjohnwu.magisk.utils.addOnPropertyChangedCallback
import com.topjohnwu.magisk.utils.set
@ -14,7 +15,7 @@ import kotlin.math.roundToInt
class HideRvItem(
val info: HideAppInfo
) : ObservableItem<HideRvItem>(), Comparable<HideRvItem> {
) : ObservableDiffRvItem<HideRvItem>(), ComparableRv<HideRvItem> {
override val layoutRes get() = R.layout.item_hide_md2
@ -89,7 +90,7 @@ class HideRvItem(
class HideProcessRvItem(
val process: HideProcessInfo
) : ObservableItem<HideProcessRvItem>() {
) : ObservableDiffRvItem<HideProcessRvItem>() {
override val layoutRes get() = R.layout.item_hide_process_md2

@ -4,12 +4,15 @@ import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.model.su.SuLog
import com.topjohnwu.magisk.databinding.ObservableItem
import com.topjohnwu.magisk.databinding.ObservableDiffRvItem
import com.topjohnwu.magisk.databinding.RvContainer
import com.topjohnwu.magisk.ktx.timeDateFormat
import com.topjohnwu.magisk.ktx.toTime
import com.topjohnwu.magisk.utils.set
class LogRvItem(val item: SuLog) : ObservableItem<LogRvItem>() {
class LogRvItem(
override val item: SuLog
) : ObservableDiffRvItem<LogRvItem>(), RvContainer<SuLog> {
override val layoutRes = R.layout.item_log_access_md2
@ -24,14 +27,4 @@ class LogRvItem(val item: SuLog) : ObservableItem<LogRvItem>() {
set(value) = set(value, field, { field = it }, BR.bottom)
override fun itemSameAs(other: LogRvItem) = item.appName == other.item.appName
override fun contentSameAs(other: LogRvItem) = item.fromUid == other.item.fromUid &&
item.toUid == other.item.toUid &&
item.fromPid == other.item.fromPid &&
item.packageName == other.item.packageName &&
item.command == other.item.command &&
item.action == other.item.action &&
item.time == other.item.time &&
isTop == other.isTop &&
isBottom == other.isBottom
}

@ -5,22 +5,20 @@ import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.model.module.LocalModule
import com.topjohnwu.magisk.core.model.module.OnlineModule
import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.databinding.ObservableItem
import com.topjohnwu.magisk.databinding.DiffRvItem
import com.topjohnwu.magisk.databinding.ObservableDiffRvItem
import com.topjohnwu.magisk.databinding.RvContainer
import com.topjohnwu.magisk.utils.set
object InstallModule : ComparableRvItem<InstallModule>() {
object InstallModule : DiffRvItem<InstallModule>() {
override val layoutRes = R.layout.item_module_download
override fun contentSameAs(other: InstallModule) = this == other
override fun itemSameAs(other: InstallModule) = this === other
}
class SectionTitle(
val title: Int,
_button: Int = 0,
_icon: Int = 0
) : ObservableItem<SectionTitle>() {
) : ObservableDiffRvItem<SectionTitle>() {
override val layoutRes = R.layout.item_section_md2
@get:Bindable
@ -34,12 +32,11 @@ class SectionTitle(
@get:Bindable
var hasButton = _button != 0 && _icon != 0
set(value) = set(value, field, { field = it }, BR.hasButton)
override fun itemSameAs(other: SectionTitle): Boolean = this === other
override fun contentSameAs(other: SectionTitle): Boolean = this === other
}
class OnlineModuleRvItem(val item: OnlineModule) : ObservableItem<OnlineModuleRvItem>() {
class OnlineModuleRvItem(
override val item: OnlineModule
) : ObservableDiffRvItem<OnlineModuleRvItem>(), RvContainer<OnlineModule> {
override val layoutRes: Int = R.layout.item_repo_md2
@get:Bindable
@ -48,12 +45,12 @@ class OnlineModuleRvItem(val item: OnlineModule) : ObservableItem<OnlineModuleRv
var hasUpdate = false
override fun contentSameAs(other: OnlineModuleRvItem): Boolean = item == other.item
override fun itemSameAs(other: OnlineModuleRvItem): Boolean = item.id == other.item.id
}
class LocalModuleRvItem(val item: LocalModule) : ObservableItem<LocalModuleRvItem>() {
class LocalModuleRvItem(
override val item: LocalModule
) : ObservableDiffRvItem<LocalModuleRvItem>(), RvContainer<LocalModule> {
override val layoutRes = R.layout.item_module_md2
@ -81,10 +78,5 @@ class LocalModuleRvItem(val item: LocalModule) : ObservableItem<LocalModuleRvIte
viewModel.updateActiveState()
}
override fun contentSameAs(other: LocalModuleRvItem): Boolean = item.version == other.item.version
&& item.versionCode == other.item.versionCode
&& item.description == other.item.description
&& item.name == other.item.name
override fun itemSameAs(other: LocalModuleRvItem): Boolean = item.id == other.item.id
}

@ -9,7 +9,7 @@ import com.topjohnwu.magisk.arch.*
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.model.module.LocalModule
import com.topjohnwu.magisk.core.model.module.OnlineModule
import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.databinding.AnyDiffRvItem
import com.topjohnwu.magisk.databinding.RvItem
import com.topjohnwu.magisk.events.OpenReadmeEvent
import com.topjohnwu.magisk.events.SelectModuleEvent
@ -44,8 +44,8 @@ class ModuleViewModel : BaseViewModel(), Queryable {
var searchLoading = false
set(value) = set(value, field, { field = it }, BR.searchLoading)
val itemsSearch = diffListOf<ComparableRvItem<*>>()
val itemSearchBinding = itemBindingOf<ComparableRvItem<*>> {
val itemsSearch = diffListOf<AnyDiffRvItem>()
val itemSearchBinding = itemBindingOf<AnyDiffRvItem> {
it.bindExtra(BR.viewModel, this)
}
@ -110,7 +110,7 @@ class ModuleViewModel : BaseViewModel(), Queryable {
// ---
private suspend fun queryInternal(query: String): List<ComparableRvItem<*>> {
private suspend fun queryInternal(query: String): List<AnyDiffRvItem> {
return if (query.isBlank()) {
itemsSearch.clear()
listOf()

@ -7,12 +7,12 @@ import androidx.annotation.CallSuper
import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ObservableItem
import com.topjohnwu.magisk.databinding.ObservableRvItem
import com.topjohnwu.magisk.utils.TextHolder
import com.topjohnwu.magisk.utils.set
import com.topjohnwu.magisk.view.MagiskDialog
sealed class BaseSettingsItem : ObservableItem<BaseSettingsItem>() {
sealed class BaseSettingsItem : ObservableRvItem() {
override val layoutRes get() = R.layout.item_settings
@ -42,9 +42,6 @@ sealed class BaseSettingsItem : ObservableItem<BaseSettingsItem>() {
open fun refresh() {}
override fun itemSameAs(other: BaseSettingsItem) = this === other
override fun contentSameAs(other: BaseSettingsItem) = itemSameAs(other)
// ---
interface Callback {

@ -10,7 +10,6 @@ import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.arch.adapterOf
import com.topjohnwu.magisk.arch.diffListOf
import com.topjohnwu.magisk.arch.itemBindingOf
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
@ -32,7 +31,7 @@ class SettingsViewModel(
val adapter = adapterOf<BaseSettingsItem>()
val itemBinding = itemBindingOf<BaseSettingsItem> { it.bindExtra(BR.callback, this) }
val items = diffListOf(createItems())
val items = createItems()
init {
viewModelScope.launch {

@ -5,14 +5,15 @@ import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.model.su.SuPolicy
import com.topjohnwu.magisk.databinding.ObservableItem
import com.topjohnwu.magisk.databinding.ObservableDiffRvItem
import com.topjohnwu.magisk.databinding.RvContainer
import com.topjohnwu.magisk.utils.set
class PolicyRvItem(
val item: SuPolicy,
override val item: SuPolicy,
val icon: Drawable,
val viewModel: SuperuserViewModel
) : ObservableItem<PolicyRvItem>() {
) : ObservableDiffRvItem<PolicyRvItem>(), RvContainer<SuPolicy> {
override val layoutRes = R.layout.item_policy_md2
@get:Bindable
@ -64,7 +65,6 @@ class PolicyRvItem(
viewModel.deletePressed(this)
}
override fun contentSameAs(other: PolicyRvItem) = itemSameAs(other)
override fun itemSameAs(other: PolicyRvItem) = item.uid == other.item.uid
}

@ -13,7 +13,7 @@ import com.topjohnwu.magisk.core.magiskdb.PolicyDao
import com.topjohnwu.magisk.core.model.su.SuPolicy
import com.topjohnwu.magisk.core.utils.BiometricHelper
import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.databinding.AnyDiffRvItem
import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.events.dialog.BiometricEvent
import com.topjohnwu.magisk.events.dialog.SuperuserRevokeDialog
@ -35,13 +35,13 @@ class SuperuserViewModel(
private val itemsPolicies = diffListOf<PolicyRvItem>()
private val itemsHelpers = ObservableArrayList<TextItem>()
val adapter = adapterOf<ComparableRvItem<*>>()
val items = MergeObservableList<ComparableRvItem<*>>().apply {
val adapter = adapterOf<AnyDiffRvItem>()
val items = MergeObservableList<AnyDiffRvItem>().apply {
if (Config.magiskHide)
insertItem(TappableHeadlineItem.Hide)
}.insertList(itemsHelpers)
.insertList(itemsPolicies)
val itemBinding = itemBindingOf<ComparableRvItem<*>> {
val itemBinding = itemBindingOf<AnyDiffRvItem> {
it.bindExtra(BR.listener, this)
}
@ -84,7 +84,7 @@ class SuperuserViewModel(
fun deletePressed(item: PolicyRvItem) {
fun updateState() = viewModelScope.launch {
db.delete(item.item.uid)
itemsPolicies.removeAll { it.genericItemSameAs(item) }
itemsPolicies.removeAll { it.itemSameAs(item) }
if (itemsPolicies.isEmpty() && itemsHelpers.isEmpty()) {
itemsHelpers.add(itemNoData)
}

@ -13,11 +13,10 @@ import kotlin.collections.ArrayList
* @param detectMoves True if DiffUtil should try to detect moved items, false otherwise.
*/
open class DiffObservableList<T>(
private val callback: Callback<T>,
private val detectMoves: Boolean = true
private val callback: Callback<T>,
private val detectMoves: Boolean = true
) : AbstractList<T>(), ObservableList<T> {
private val LIST_LOCK = Object()
protected var list: MutableList<T> = ArrayList()
private val listeners = ListChangeRegistry()
protected val listCallback = ObservableListUpdateCallback()
@ -32,27 +31,25 @@ open class DiffObservableList<T>(
* list into the given one.
*/
fun calculateDiff(newItems: List<T>): DiffUtil.DiffResult {
val frozenList = synchronized(LIST_LOCK) {
ArrayList(list)
}
val frozenList = ArrayList(list)
return doCalculateDiff(frozenList, newItems)
}
protected fun doCalculateDiff(oldItems: List<T>, newItems: List<T>?): DiffUtil.DiffResult {
protected fun doCalculateDiff(oldItems: List<T>, newItems: List<T>): DiffUtil.DiffResult {
return DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun getOldListSize() = oldItems.size
override fun getNewListSize() = newItems?.size ?: 0
override fun getNewListSize() = newItems.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems!![newItemPosition]
val newItem = newItems[newItemPosition]
return callback.areItemsTheSame(oldItem, newItem)
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems!![newItemPosition]
val newItem = newItems[newItemPosition]
return callback.areContentsTheSame(oldItem, newItem)
}
}, detectMoves)
@ -67,9 +64,7 @@ open class DiffObservableList<T>(
*/
@MainThread
fun update(newItems: List<T>, diffResult: DiffUtil.DiffResult) {
synchronized(LIST_LOCK) {
list = newItems.toMutableList()
}
list = newItems.toMutableList()
diffResult.dispatchUpdatesTo(listCallback)
}
@ -97,29 +92,14 @@ open class DiffObservableList<T>(
listeners.remove(listener)
}
override fun get(index: Int): T {
return list[index]
}
override fun add(element: T): Boolean {
list.add(element)
notifyAdd(size - 1, 1)
return true
}
override fun get(index: Int) = list[index]
override fun add(index: Int, element: T) {
list.add(index, element)
notifyAdd(index, 1)
}
override fun addAll(elements: Collection<T>): Boolean {
val oldSize = size
val added = list.addAll(elements)
if (added) {
notifyAdd(oldSize, size - oldSize)
}
return added
}
override fun addAll(elements: Collection<T>) = addAll(size, elements)
override fun addAll(index: Int, elements: Collection<T>): Boolean {
val added = list.addAll(index, elements)
@ -153,14 +133,6 @@ open class DiffObservableList<T>(
return element
}
fun removeLast(): T? {
if (size > 0) {
val index = size - 1
return removeAt(index)
}
return null
}
override fun set(index: Int, element: T): T {
val old = list.set(index, element)
listeners.notifyChanged(this, index, 1)
@ -228,6 +200,5 @@ open class DiffObservableList<T>(
modCount += 1
listeners.notifyRemoved(this@DiffObservableList, position, count)
}
}
}
}

@ -23,8 +23,9 @@ import com.google.android.material.shape.MaterialShapeDrawable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.itemBindingOf
import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.databinding.DiffRvItem
import com.topjohnwu.magisk.databinding.DialogMagiskBaseBinding
import com.topjohnwu.magisk.databinding.RvContainer
import com.topjohnwu.magisk.utils.ObservableHost
import com.topjohnwu.magisk.utils.set
import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapters
@ -188,12 +189,10 @@ class MagiskDialog(
}
class DialogItem(
val item: CharSequence,
override val item: CharSequence,
val position: Int
) : ComparableRvItem<DialogItem>() {
) : DiffRvItem<DialogItem>(), RvContainer<CharSequence> {
override val layoutRes = R.layout.item_list_single_line
override fun itemSameAs(other: DialogItem) = item == other.item
override fun contentSameAs(other: DialogItem) = itemSameAs(other)
}
interface ActualOnDialogClickListener {

@ -1,21 +1,15 @@
package com.topjohnwu.magisk.view
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.databinding.DiffRvItem
sealed class TappableHeadlineItem : ComparableRvItem<TappableHeadlineItem>() {
sealed class TappableHeadlineItem : DiffRvItem<TappableHeadlineItem>() {
abstract val title: Int
abstract val icon: Int
override val layoutRes = R.layout.item_tappable_headline
override fun itemSameAs(other: TappableHeadlineItem) =
this === other
override fun contentSameAs(other: TappableHeadlineItem) =
title == other.title && icon == other.icon
// --- listener
interface Listener {

@ -1,11 +1,10 @@
package com.topjohnwu.magisk.view
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.databinding.DiffRvItem
class TextItem(val text: Int) : ComparableRvItem<TextItem>() {
class TextItem(val text: Int) : DiffRvItem<TextItem>() {
override val layoutRes = R.layout.item_text
override fun contentSameAs(other: TextItem) = text == other.text
override fun itemSameAs(other: TextItem) = contentSameAs(other)
}