You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Merge Teanity into sources
This commit is contained in:
@@ -16,9 +16,9 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import com.skoumal.teanity.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.extensions.replaceRandomWithSpecial
|
||||
import com.topjohnwu.magisk.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.model.entity.state.IndeterminateState
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
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> {
|
||||
|
||||
private val LIST_LOCK = Object()
|
||||
private var list: MutableList<T> = ArrayList()
|
||||
private val listeners = ListChangeRegistry()
|
||||
private 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 = synchronized(LIST_LOCK) {
|
||||
ArrayList(list)
|
||||
}
|
||||
return doCalculateDiff(frozenList, newItems)
|
||||
}
|
||||
|
||||
private 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 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) {
|
||||
synchronized(LIST_LOCK) {
|
||||
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)
|
||||
list = newItems.toMutableList()
|
||||
diffResult.dispatchUpdatesTo(listCallback)
|
||||
}
|
||||
|
||||
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): T {
|
||||
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) {
|
||||
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(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
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.view.get
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.topjohnwu.magisk.extensions.drawableCompat
|
||||
|
||||
class KItemDecoration(
|
||||
private val context: Context,
|
||||
@RecyclerView.Orientation private val orientation: Int
|
||||
) :
|
||||
RecyclerView.ItemDecoration() {
|
||||
|
||||
private val bounds = Rect()
|
||||
private var divider: Drawable? = null
|
||||
var showAfterLast = true
|
||||
|
||||
fun setDeco(@DrawableRes drawable: Int) = apply {
|
||||
setDeco(context.drawableCompat(drawable))
|
||||
}
|
||||
|
||||
fun setDeco(drawable: Drawable?) = apply {
|
||||
divider = drawable
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
parent.layoutManager ?: return
|
||||
|
||||
divider?.let {
|
||||
if (orientation == DividerItemDecoration.VERTICAL) {
|
||||
drawVertical(canvas, parent, it)
|
||||
} else {
|
||||
drawHorizontal(canvas, parent, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawVertical(canvas: Canvas, parent: RecyclerView, drawable: Drawable) {
|
||||
canvas.save()
|
||||
val left: Int
|
||||
val right: Int
|
||||
if (parent.clipToPadding) {
|
||||
left = parent.paddingLeft
|
||||
right = parent.width - parent.paddingRight
|
||||
canvas.clipRect(
|
||||
left, parent.paddingTop, right,
|
||||
parent.height - parent.paddingBottom
|
||||
)
|
||||
} else {
|
||||
left = 0
|
||||
right = parent.width
|
||||
}
|
||||
|
||||
val to = if (showAfterLast) parent.childCount else parent.childCount - 1
|
||||
|
||||
(0 until to)
|
||||
.map { parent[it] }
|
||||
.forEach { child ->
|
||||
parent.getDecoratedBoundsWithMargins(child, bounds)
|
||||
val bottom = bounds.bottom + Math.round(child.translationY)
|
||||
val top = bottom - drawable.intrinsicHeight
|
||||
drawable.setBounds(left, top, right, bottom)
|
||||
drawable.draw(canvas)
|
||||
}
|
||||
canvas.restore()
|
||||
}
|
||||
|
||||
private fun drawHorizontal(canvas: Canvas, parent: RecyclerView, drawable: Drawable) {
|
||||
canvas.save()
|
||||
val top: Int
|
||||
val bottom: Int
|
||||
if (parent.clipToPadding) {
|
||||
top = parent.paddingTop
|
||||
bottom = parent.height - parent.paddingBottom
|
||||
canvas.clipRect(
|
||||
parent.paddingLeft, top,
|
||||
parent.width - parent.paddingRight, bottom
|
||||
)
|
||||
} else {
|
||||
top = 0
|
||||
bottom = parent.height
|
||||
}
|
||||
|
||||
val to = if (showAfterLast) parent.childCount else parent.childCount - 1
|
||||
|
||||
(0 until to)
|
||||
.map { parent[it] }
|
||||
.forEach { child ->
|
||||
parent.layoutManager!!.getDecoratedBoundsWithMargins(child, bounds)
|
||||
val right = bounds.right + Math.round(child.translationX)
|
||||
val left = right - drawable.intrinsicWidth
|
||||
drawable.setBounds(left, top, right, bottom)
|
||||
drawable.draw(canvas)
|
||||
}
|
||||
canvas.restore()
|
||||
}
|
||||
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
if (parent.getChildAdapterPosition(view) == state.itemCount - 1) {
|
||||
outRect.setEmpty()
|
||||
return
|
||||
}
|
||||
|
||||
if (orientation == RecyclerView.VERTICAL) {
|
||||
outRect.set(0, 0, 0, divider?.intrinsicHeight ?: 0)
|
||||
} else {
|
||||
outRect.set(0, 0, divider?.intrinsicWidth ?: 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import androidx.databinding.Observable
|
||||
import androidx.databinding.ObservableField
|
||||
import java.io.Serializable
|
||||
|
||||
/**
|
||||
* Kotlin version of [ObservableField].
|
||||
* You can define if wrapped type is Nullable or not.
|
||||
* You can use kotlin get/set syntax for value
|
||||
*/
|
||||
class KObservableField<T> : ObservableField<T>, Serializable {
|
||||
|
||||
var value: T
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
notifyChange()
|
||||
}
|
||||
}
|
||||
|
||||
constructor(init: T) {
|
||||
value = init
|
||||
}
|
||||
|
||||
constructor(init: T, vararg dependencies: Observable) : super(*dependencies) {
|
||||
value = init
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
message = "Needed for data binding, use KObservableField.value syntax from code",
|
||||
replaceWith = ReplaceWith("value")
|
||||
)
|
||||
override fun get(): T {
|
||||
return value
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
message = "Needed for data binding, use KObservableField.value = ... syntax from code",
|
||||
replaceWith = ReplaceWith("value = newValue")
|
||||
)
|
||||
override fun set(newValue: T) {
|
||||
value = newValue
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "KObservableField(value=$value)"
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package com.topjohnwu.magisk.utils
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import com.skoumal.teanity.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.*
|
||||
import com.topjohnwu.magisk.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.ui.SplashActivity
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
import com.topjohnwu.signing.JarMap
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
|
||||
class RxBus {
|
||||
|
||||
private val _bus = PublishSubject.create<Event>()
|
||||
|
||||
val bus: Observable<Event> get() = _bus
|
||||
|
||||
fun post(event: Event) {
|
||||
_bus.onNext(event)
|
||||
}
|
||||
|
||||
fun post(event: Int) {
|
||||
_bus.onNext(SimpleEvent(event))
|
||||
}
|
||||
|
||||
inline fun <reified T : Event> register(noinline predicate: (T) -> Boolean = { true }): Observable<T> {
|
||||
return bus
|
||||
.ofType(T::class.java)
|
||||
.filter(predicate)
|
||||
}
|
||||
|
||||
fun register(eventId: Int): Observable<Int> {
|
||||
return bus
|
||||
.ofType(SimpleEvent::class.java)
|
||||
.map { it.eventId }
|
||||
.filter { it == eventId }
|
||||
}
|
||||
|
||||
interface Event
|
||||
|
||||
private class SimpleEvent(val eventId: Int) : Event
|
||||
}
|
||||
Reference in New Issue
Block a user