You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
App code reorganization
This commit is contained in:
@@ -1,204 +0,0 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.databinding.ListChangeRegistry
|
||||
import androidx.databinding.ObservableList
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListUpdateCallback
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
/**
|
||||
* @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<T>(
|
||||
private val callback: Callback<T>,
|
||||
private val detectMoves: Boolean = true
|
||||
) : AbstractList<T>(), ObservableList<T> {
|
||||
|
||||
protected var list: MutableList<T> = ArrayList()
|
||||
private val listeners = ListChangeRegistry()
|
||||
protected val listCallback = ObservableListUpdateCallback()
|
||||
|
||||
override val size: Int get() = list.size
|
||||
|
||||
/**
|
||||
* Calculates the list of update operations that can convert this list into the given one.
|
||||
*
|
||||
* @param newItems The items that this list will be set to.
|
||||
* @return A DiffResult that contains the information about the edit sequence to covert this
|
||||
* list into the given one.
|
||||
*/
|
||||
fun calculateDiff(newItems: List<T>): DiffUtil.DiffResult {
|
||||
val frozenList = ArrayList(list)
|
||||
return doCalculateDiff(frozenList, newItems)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
val oldItem = oldItems[oldItemPosition]
|
||||
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]
|
||||
return callback.areContentsTheSame(oldItem, newItem)
|
||||
}
|
||||
}, detectMoves)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the contents of this list to the given one using the DiffResults to dispatch change
|
||||
* notifications.
|
||||
*
|
||||
* @param newItems The items to set this list to.
|
||||
* @param diffResult The diff results to dispatch change notifications.
|
||||
*/
|
||||
@MainThread
|
||||
fun update(newItems: List<T>, diffResult: DiffUtil.DiffResult) {
|
||||
list = newItems.toMutableList()
|
||||
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<T>) {
|
||||
val diffResult = doCalculateDiff(list, newItems)
|
||||
update(newItems, diffResult)
|
||||
}
|
||||
|
||||
override fun addOnListChangedCallback(listener: ObservableList.OnListChangedCallback<out ObservableList<T>>) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
override fun removeOnListChangedCallback(listener: ObservableList.OnListChangedCallback<out ObservableList<T>>) {
|
||||
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<T>) = addAll(size, elements)
|
||||
|
||||
override fun addAll(index: Int, elements: Collection<T>): 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)
|
||||
}
|
||||
|
||||
private fun notifyRemove(start: Int, count: Int) {
|
||||
listeners.notifyRemoved(this, start, count)
|
||||
}
|
||||
|
||||
/**
|
||||
* A Callback class used by DiffUtil while calculating the diff between two lists.
|
||||
*/
|
||||
interface Callback<T> {
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
inner class ObservableListUpdateCallback : ListUpdateCallback {
|
||||
override fun onChanged(position: Int, count: Int, payload: Any?) {
|
||||
listeners.notifyChanged(this@DiffObservableList, position, count)
|
||||
}
|
||||
|
||||
override fun onMoved(fromPosition: Int, toPosition: Int) {
|
||||
listeners.notifyMoved(this@DiffObservableList, fromPosition, toPosition, 1)
|
||||
}
|
||||
|
||||
override fun onInserted(position: Int, count: Int) {
|
||||
modCount += 1
|
||||
listeners.notifyInserted(this@DiffObservableList, position, count)
|
||||
}
|
||||
|
||||
override fun onRemoved(position: Int, count: Int) {
|
||||
modCount += 1
|
||||
listeners.notifyRemoved(this@DiffObservableList, position, count)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.os.Looper
|
||||
import java.util.*
|
||||
|
||||
class FilterableDiffObservableList<T>(
|
||||
callback: Callback<T>
|
||||
) : DiffObservableList<T>(callback) {
|
||||
|
||||
var filter: ((T) -> Boolean)? = null
|
||||
set(value) {
|
||||
field = value
|
||||
queueUpdate()
|
||||
}
|
||||
@Volatile
|
||||
private var sublist: MutableList<T> = super.list
|
||||
|
||||
// ---
|
||||
|
||||
private val ui by lazy { Handler(Looper.getMainLooper()) }
|
||||
private val handler = Handler(HandlerThread("List${hashCode()}").apply { start() }.looper)
|
||||
private val updater = Runnable {
|
||||
val filter = filter ?: { true }
|
||||
val newList = super.list.filter(filter)
|
||||
val diff = synchronized(this) { doCalculateDiff(sublist, newList) }
|
||||
ui.post {
|
||||
sublist = Collections.synchronizedList(newList)
|
||||
diff.dispatchUpdatesTo(listCallback)
|
||||
}
|
||||
}
|
||||
|
||||
private fun queueUpdate() {
|
||||
handler.removeCallbacks(updater)
|
||||
handler.post(updater)
|
||||
}
|
||||
|
||||
fun hasFilter() = filter != null
|
||||
|
||||
fun filter(switch: (T) -> Boolean) {
|
||||
filter = switch
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
filter = null
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
override fun get(index: Int): T {
|
||||
return sublist.get(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<T>): Boolean {
|
||||
return sublist.addAll(elements)
|
||||
}
|
||||
|
||||
override fun addAll(index: Int, elements: Collection<T>): 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
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import androidx.databinding.Observable
|
||||
import androidx.databinding.PropertyChangeRegistry
|
||||
|
||||
/**
|
||||
* Modified from https://github.com/skoumalcz/teanity/blob/1.2/core/src/main/java/com/skoumal/teanity/observable/Notifyable.kt
|
||||
*
|
||||
* Interface that allows user to be observed via DataBinding or manually by assigning listeners.
|
||||
*
|
||||
* @see [androidx.databinding.Observable]
|
||||
* */
|
||||
interface ObservableHost : Observable {
|
||||
|
||||
var callbacks: PropertyChangeRegistry?
|
||||
|
||||
/**
|
||||
* Notifies all observers that something has changed. By default implementation this method is
|
||||
* synchronous, hence observers will never be notified in undefined order. Observers might
|
||||
* choose to refresh the view completely, which is beyond the scope of this function.
|
||||
* */
|
||||
fun notifyChange() {
|
||||
synchronized(this) {
|
||||
callbacks ?: return
|
||||
}.notifyCallbacks(this, 0, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all observers about field with [fieldId] has been changed. This will happen
|
||||
* synchronously before or after [notifyChange] has been called. It will never be called during
|
||||
* the execution of aforementioned method.
|
||||
* */
|
||||
fun notifyPropertyChanged(fieldId: Int) {
|
||||
synchronized(this) {
|
||||
callbacks ?: return
|
||||
}.notifyCallbacks(this, fieldId, null)
|
||||
}
|
||||
|
||||
override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) {
|
||||
synchronized(this) {
|
||||
callbacks ?: PropertyChangeRegistry().also { callbacks = it }
|
||||
}.add(callback)
|
||||
}
|
||||
|
||||
override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) {
|
||||
synchronized(this) {
|
||||
callbacks ?: return
|
||||
}.remove(callback)
|
||||
}
|
||||
}
|
||||
|
||||
fun ObservableHost.addOnPropertyChangedCallback(
|
||||
fieldId: Int,
|
||||
removeAfterChanged: Boolean = false,
|
||||
callback: () -> Unit
|
||||
) {
|
||||
addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
|
||||
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
|
||||
if (fieldId == propertyId) {
|
||||
callback()
|
||||
if (removeAfterChanged)
|
||||
removeOnPropertyChangedCallback(this)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects boilerplate implementation for {@literal @}[androidx.databinding.Bindable] field setters.
|
||||
*
|
||||
* # Examples:
|
||||
* ```kotlin
|
||||
* @get:Bindable
|
||||
* var myField = defaultValue
|
||||
* set(value) = set(value, field, { field = it }, BR.myField) {
|
||||
* doSomething(it)
|
||||
* }
|
||||
* ```
|
||||
* */
|
||||
|
||||
inline fun <reified T> ObservableHost.set(
|
||||
new: T, old: T, setter: (T) -> Unit, fieldId: Int, afterChanged: (T) -> Unit = {}) {
|
||||
if (old != new) {
|
||||
setter(new)
|
||||
notifyPropertyChanged(fieldId)
|
||||
afterChanged(new)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T> ObservableHost.set(
|
||||
new: T, old: T, setter: (T) -> Unit, vararg fieldIds: Int, afterChanged: (T) -> Unit = {}) {
|
||||
if (old != new) {
|
||||
setter(new)
|
||||
fieldIds.forEach { notifyPropertyChanged(it) }
|
||||
afterChanged(new)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user