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 cda73bf7b..2e260f209 100644 --- a/app/src/main/java/com/topjohnwu/magisk/databinding/DiffObservableList.kt +++ b/app/src/main/java/com/topjohnwu/magisk/databinding/DiffObservableList.kt @@ -5,23 +5,20 @@ import androidx.databinding.ListChangeRegistry import androidx.databinding.ObservableList import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListUpdateCallback -import java.util.* +import java.util.AbstractList -/** - * @param callback The callback that controls the behavior of the DiffObservableList. - * @param detectMoves True if DiffUtil should try to detect moved items, false otherwise. - */ open class DiffObservableList( - private val callback: Callback, - private val detectMoves: Boolean = true + private val callback: Callback ) : AbstractList(), ObservableList { - protected var list: MutableList = ArrayList() + protected var list: List = emptyList() private val listeners = ListChangeRegistry() protected val listCallback = ObservableListUpdateCallback() override val size: Int get() = list.size + override fun get(index: Int) = list[index] + /** * Calculates the list of update operations that can convert this list into the given one. * @@ -30,8 +27,7 @@ open class DiffObservableList( * list into the given one. */ fun calculateDiff(newItems: List): DiffUtil.DiffResult { - val frozenList = ArrayList(list) - return doCalculateDiff(frozenList, newItems) + return doCalculateDiff(list, newItems) } protected fun doCalculateDiff(oldItems: List, newItems: List): DiffUtil.DiffResult { @@ -51,7 +47,7 @@ open class DiffObservableList( val newItem = newItems[newItemPosition] return callback.areContentsTheSame(oldItem, newItem) } - }, detectMoves) + }, true) } /** @@ -63,26 +59,10 @@ open class DiffObservableList( */ @MainThread fun update(newItems: List, diffResult: DiffUtil.DiffResult) { - list = newItems.toMutableList() + list = ArrayList(newItems) diffResult.dispatchUpdatesTo(listCallback) } - /** - * Sets this list to the given items. This is a convenience method for calling [ ][.calculateDiff] followed by [.update]. - * - * - * **Warning!** If the lists are large this operation may be too slow for the main thread. In - * that case, you should call [.calculateDiff] on a background thread and then - * [.update] on the main thread. - * - * @param newItems The items to set this list to. - */ - @MainThread - fun update(newItems: List) { - val diffResult = doCalculateDiff(list, newItems) - update(newItems, diffResult) - } - override fun addOnListChangedCallback(listener: ObservableList.OnListChangedCallback>) { listeners.add(listener) } @@ -91,53 +71,6 @@ open class DiffObservableList( listeners.remove(listener) } - 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) = addAll(size, elements) - - override fun addAll(index: Int, elements: Collection): Boolean { - val added = list.addAll(index, elements) - if (added) { - notifyAdd(index, elements.size) - } - return added - } - - override fun clear() { - val oldSize = size - list.clear() - if (oldSize != 0) { - notifyRemove(0, oldSize) - } - } - - override fun remove(element: T): Boolean { - val index = indexOf(element) - return if (index >= 0) { - removeAt(index) - true - } else { - false - } - } - - override fun removeAt(index: Int): T { - val element = list.removeAt(index) - notifyRemove(index, 1) - return element - } - - override fun set(index: Int, element: T): T { - val old = list.set(index, element) - listeners.notifyChanged(this, index, 1) - return old - } - private fun notifyAdd(start: Int, count: Int) { listeners.notifyInserted(this, start, count) } @@ -146,38 +79,8 @@ open class DiffObservableList( listeners.notifyRemoved(this, start, count) } - /** - * A Callback class used by DiffUtil while calculating the diff between two lists. - */ interface Callback { - /** - * Called by the DiffUtil to decide whether two object represent the same Item. - * - * - * For example, if your items have unique ids, this method should check their id equality. - * - * @param oldItem The old item. - * @param newItem The new item. - * @return True if the two items represent the same object or false if they are different. - */ fun areItemsTheSame(oldItem: T, newItem: T): Boolean - - /** - * Called by the DiffUtil when it wants to check whether two items have the same data. - * DiffUtil uses this information to detect if the contents of an item has changed. - * - * - * DiffUtil uses this method to check equality instead of [Object.equals] so - * that you can change its behavior depending on your UI. - * - * - * This method is called only if [.areItemsTheSame] returns `true` for - * these items. - * - * @param oldItem The old item. - * @param newItem The new item which replaces the old item. - * @return True if the contents of the items are the same or false if they are different. - */ fun areContentsTheSame(oldItem: T, newItem: T): Boolean } diff --git a/app/src/main/java/com/topjohnwu/magisk/databinding/FilterableDiffObservableList.kt b/app/src/main/java/com/topjohnwu/magisk/databinding/FilterableDiffObservableList.kt index 00a4b13ea..7cfbd6ce9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/databinding/FilterableDiffObservableList.kt +++ b/app/src/main/java/com/topjohnwu/magisk/databinding/FilterableDiffObservableList.kt @@ -5,14 +5,13 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.Collections -class FilterableDiffObservableList( +open class FilterableDiffObservableList( callback: Callback, private val scope: CoroutineScope ) : DiffObservableList(callback) { - private var sublist: MutableList = list + private var sublist: List = emptyList() private var job: Job? = null // --- @@ -20,10 +19,11 @@ class FilterableDiffObservableList( fun filter(filter: (T) -> Boolean) { job?.cancel() job = scope.launch(Dispatchers.Default) { + val oldList = sublist val newList = list.filter(filter) - val diff = synchronized(this) { doCalculateDiff(sublist, newList) } + val diff = doCalculateDiff(oldList, newList) withContext(Dispatchers.Main) { - sublist = Collections.synchronizedList(newList) + sublist = newList diff.dispatchUpdatesTo(listCallback) } } @@ -35,34 +35,6 @@ class FilterableDiffObservableList( return sublist[index] } - override fun add(element: T): Boolean { - return sublist.add(element) - } - - override fun add(index: Int, element: T) { - sublist.add(index, element) - } - - override fun addAll(elements: Collection): Boolean { - return sublist.addAll(elements) - } - - override fun addAll(index: Int, elements: Collection): Boolean { - return sublist.addAll(index, elements) - } - - override fun remove(element: T): Boolean { - return sublist.remove(element) - } - - override fun removeAt(index: Int): T { - return sublist.removeAt(index) - } - - override fun set(index: Int, element: T): T { - return sublist.set(index, element) - } - override val size: Int get() = sublist.size } diff --git a/app/src/main/java/com/topjohnwu/magisk/databinding/Helpers.kt b/app/src/main/java/com/topjohnwu/magisk/databinding/Helpers.kt deleted file mode 100644 index 113675a4c..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/databinding/Helpers.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.topjohnwu.magisk.databinding - -import kotlinx.coroutines.CoroutineScope - -fun diffListOf() = - DiffObservableList(DiffRvItem.callback()) - -fun filterableListOf(scope: CoroutineScope) = - FilterableDiffObservableList(DiffRvItem.callback(), scope) diff --git a/app/src/main/java/com/topjohnwu/magisk/databinding/RecyclerViewItems.kt b/app/src/main/java/com/topjohnwu/magisk/databinding/RecyclerViewItems.kt index b1f1503f6..0f007866a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/databinding/RecyclerViewItems.kt +++ b/app/src/main/java/com/topjohnwu/magisk/databinding/RecyclerViewItems.kt @@ -3,6 +3,7 @@ package com.topjohnwu.magisk.databinding import androidx.databinding.PropertyChangeRegistry import androidx.databinding.ViewDataBinding import androidx.recyclerview.widget.RecyclerView +import kotlinx.coroutines.CoroutineScope abstract class RvItem { abstract val layoutRes: Int @@ -33,27 +34,6 @@ abstract class DiffRvItem : RvItem() { is ComparableRv<*> -> comparableEqual(other) else -> this == other } - - companion object { - private val callback = object : DiffObservableList.Callback> { - override fun areItemsTheSame( - oldItem: DiffRvItem, - newItem: DiffRvItem - ): Boolean { - return oldItem::class == newItem::class && oldItem.itemSameAs(newItem) - } - - override fun areContentsTheSame( - oldItem: DiffRvItem, - newItem: DiffRvItem - ): Boolean { - return oldItem.contentSameAs(newItem) - } - } - - @Suppress("UNCHECKED_CAST") - fun callback() = callback as DiffObservableList.Callback - } } typealias AnyDiffRvItem = DiffRvItem<*> @@ -65,3 +45,27 @@ abstract class ObservableDiffRvItem : DiffRvItem(), ObservableHost { abstract class ObservableRvItem : RvItem(), ObservableHost { override var callbacks: PropertyChangeRegistry? = null } + +private object DiffRvItemCallback : DiffObservableList.Callback> { + override fun areItemsTheSame( + oldItem: DiffRvItem, + newItem: DiffRvItem + ): Boolean { + return oldItem::class == newItem::class && oldItem.itemSameAs(newItem) + } + + override fun areContentsTheSame( + oldItem: DiffRvItem, + newItem: DiffRvItem + ): Boolean { + return oldItem.contentSameAs(newItem) + } +} + +@Suppress("UNCHECKED_CAST") +class DiffRvItemList : DiffObservableList(DiffRvItemCallback as Callback) + +@Suppress("UNCHECKED_CAST") +class DiffRvItemFilterList( + scope: CoroutineScope +) : FilterableDiffObservableList(DiffRvItemCallback as Callback, scope) 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 13a3ed82f..da505ae9c 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 @@ -7,8 +7,8 @@ import androidx.lifecycle.viewModelScope import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.arch.AsyncLoadViewModel import com.topjohnwu.magisk.core.di.AppContext +import com.topjohnwu.magisk.databinding.DiffRvItemFilterList import com.topjohnwu.magisk.databinding.bindExtra -import com.topjohnwu.magisk.databinding.filterableListOf import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.ktx.concurrentMap import com.topjohnwu.superuser.Shell @@ -38,7 +38,7 @@ class DenyListViewModel : AsyncLoadViewModel() { query() } - val items = filterableListOf(viewModelScope) + val items = DiffRvItemFilterList(viewModelScope) val extraBindings = bindExtra { it.put(BR.viewModel, this) } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt index cf3517481..827f043bf 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt @@ -15,7 +15,7 @@ import com.topjohnwu.magisk.core.tasks.FlashZip import com.topjohnwu.magisk.core.tasks.MagiskInstaller import com.topjohnwu.magisk.core.utils.MediaStoreUtils import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream -import com.topjohnwu.magisk.databinding.diffListOf +import com.topjohnwu.magisk.databinding.DiffRvItemList import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.events.SnackbarEvent import com.topjohnwu.magisk.ktx.reboot @@ -40,7 +40,7 @@ class FlashViewModel : BaseViewModel() { var showReboot = Info.isRooted set(value) = set(value, field, { field = it }, BR.showReboot) - val items = diffListOf() + val items = DiffRvItemList() lateinit var args: FlashFragmentArgs private val logItems = mutableListOf().synchronized() 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 73976c33f..259b5e960 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 @@ -11,8 +11,8 @@ import com.topjohnwu.magisk.core.Info 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.DiffRvItemList import com.topjohnwu.magisk.databinding.bindExtra -import com.topjohnwu.magisk.databinding.diffListOf import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.events.SnackbarEvent import com.topjohnwu.magisk.ktx.timeFormatStandard @@ -34,7 +34,7 @@ class LogViewModel( // --- su log - val items = diffListOf() + val items = DiffRvItemList() val extraBindings = bindExtra { it.put(BR.viewModel, this) } 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 10b386d4e..d7dc0a6e2 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.DiffRvItemList import com.topjohnwu.magisk.databinding.MergeObservableList import com.topjohnwu.magisk.databinding.RvItem import com.topjohnwu.magisk.databinding.bindExtra -import com.topjohnwu.magisk.databinding.diffListOf import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.events.GetContentEvent import com.topjohnwu.magisk.events.SnackbarEvent @@ -26,7 +26,7 @@ class ModuleViewModel : AsyncLoadViewModel() { val bottomBarBarrierIds = intArrayOf(R.id.module_update, R.id.module_remove) - private val itemsInstalled = diffListOf() + private val itemsInstalled = DiffRvItemList() 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 2b754d97f..d4e0a62db 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 = diffListOf() + private val itemsPolicies = DiffRvItemList() val items = MergeObservableList() .insertList(itemsHelpers)