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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,12 +4,15 @@ import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.model.su.SuLog 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.timeDateFormat
import com.topjohnwu.magisk.ktx.toTime import com.topjohnwu.magisk.ktx.toTime
import com.topjohnwu.magisk.utils.set 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 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) set(value) = set(value, field, { field = it }, BR.bottom)
override fun itemSameAs(other: LogRvItem) = item.appName == other.item.appName 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
} }

View File

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

View File

@ -9,7 +9,7 @@ import com.topjohnwu.magisk.arch.*
import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.Info
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.ComparableRvItem import com.topjohnwu.magisk.databinding.AnyDiffRvItem
import com.topjohnwu.magisk.databinding.RvItem import com.topjohnwu.magisk.databinding.RvItem
import com.topjohnwu.magisk.events.OpenReadmeEvent import com.topjohnwu.magisk.events.OpenReadmeEvent
import com.topjohnwu.magisk.events.SelectModuleEvent import com.topjohnwu.magisk.events.SelectModuleEvent
@ -44,8 +44,8 @@ class ModuleViewModel : BaseViewModel(), Queryable {
var searchLoading = false var searchLoading = false
set(value) = set(value, field, { field = it }, BR.searchLoading) set(value) = set(value, field, { field = it }, BR.searchLoading)
val itemsSearch = diffListOf<ComparableRvItem<*>>() val itemsSearch = diffListOf<AnyDiffRvItem>()
val itemSearchBinding = itemBindingOf<ComparableRvItem<*>> { val itemSearchBinding = itemBindingOf<AnyDiffRvItem> {
it.bindExtra(BR.viewModel, this) 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()) { return if (query.isBlank()) {
itemsSearch.clear() itemsSearch.clear()
listOf() listOf()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,21 +1,15 @@
package com.topjohnwu.magisk.view package com.topjohnwu.magisk.view
import com.topjohnwu.magisk.R 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 title: Int
abstract val icon: Int abstract val icon: Int
override val layoutRes = R.layout.item_tappable_headline 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 // --- listener
interface Listener { interface Listener {

View File

@ -1,11 +1,10 @@
package com.topjohnwu.magisk.view package com.topjohnwu.magisk.view
import com.topjohnwu.magisk.R 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 val layoutRes = R.layout.item_text
override fun contentSameAs(other: TextItem) = text == other.text override fun contentSameAs(other: TextItem) = text == other.text
override fun itemSameAs(other: TextItem) = contentSameAs(other)
} }