Merge Teanity into sources

This commit is contained in:
topjohnwu
2019-09-28 01:56:16 -04:00
parent 0cb90e2e55
commit fc886a5a47
67 changed files with 1436 additions and 570 deletions
@@ -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
}