You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Reorganize app source code
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
package com.topjohnwu.magisk.databinding
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.databinding.BindingAdapter
|
||||
import com.topjohnwu.magisk.ktx.coroutineScope
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
import io.noties.markwon.Markwon
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@BindingAdapter("gone")
|
||||
fun setGone(view: View, gone: Boolean) {
|
||||
view.isGone = gone
|
||||
}
|
||||
|
||||
@BindingAdapter("invisible")
|
||||
fun setInvisible(view: View, invisible: Boolean) {
|
||||
view.isInvisible = invisible
|
||||
}
|
||||
|
||||
@BindingAdapter("goneUnless")
|
||||
fun setGoneUnless(view: View, goneUnless: Boolean) {
|
||||
setGone(view, goneUnless.not())
|
||||
}
|
||||
|
||||
@BindingAdapter("invisibleUnless")
|
||||
fun setInvisibleUnless(view: View, invisibleUnless: Boolean) {
|
||||
setInvisible(view, invisibleUnless.not())
|
||||
}
|
||||
|
||||
@BindingAdapter("markdownText")
|
||||
fun setMarkdownText(tv: TextView, text: CharSequence) {
|
||||
tv.coroutineScope.launch(Dispatchers.IO) {
|
||||
val markwon = get<Markwon>()
|
||||
markwon.setMarkdown(tv, text.toString())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
package com.topjohnwu.magisk.databinding
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.graphics.Paint
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.databinding.BindingAdapter
|
||||
import androidx.databinding.InverseBindingAdapter
|
||||
import androidx.databinding.InverseBindingListener
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
|
||||
import androidx.recyclerview.widget.*
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.ktx.coroutineScope
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
import com.topjohnwu.magisk.ktx.replaceRandomWithSpecial
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||
import com.topjohnwu.widget.IndeterminateCheckBox
|
||||
import io.noties.markwon.Markwon
|
||||
import kotlinx.coroutines.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@BindingAdapter("gone")
|
||||
fun setGone(view: View, gone: Boolean) {
|
||||
view.isGone = gone
|
||||
}
|
||||
|
||||
@BindingAdapter("invisible")
|
||||
fun setInvisible(view: View, invisible: Boolean) {
|
||||
view.isInvisible = invisible
|
||||
}
|
||||
|
||||
@BindingAdapter("goneUnless")
|
||||
fun setGoneUnless(view: View, goneUnless: Boolean) {
|
||||
setGone(view, goneUnless.not())
|
||||
}
|
||||
|
||||
@BindingAdapter("invisibleUnless")
|
||||
fun setInvisibleUnless(view: View, invisibleUnless: Boolean) {
|
||||
setInvisible(view, invisibleUnless.not())
|
||||
}
|
||||
|
||||
@BindingAdapter("markdownText")
|
||||
fun setMarkdownText(tv: TextView, text: CharSequence) {
|
||||
tv.coroutineScope.launch(Dispatchers.IO) {
|
||||
val markwon = get<Markwon>()
|
||||
markwon.setMarkdown(tv, text.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("onNavigationClick")
|
||||
fun setOnNavigationClickedListener(view: Toolbar, listener: View.OnClickListener) {
|
||||
view.setNavigationOnClickListener(listener)
|
||||
}
|
||||
|
||||
@BindingAdapter("srcCompat")
|
||||
fun setImageResource(view: ImageView, @DrawableRes resId: Int) {
|
||||
view.setImageResource(resId)
|
||||
}
|
||||
|
||||
@BindingAdapter("srcCompat")
|
||||
fun setImageResource(view: ImageView, drawable: Drawable) {
|
||||
view.setImageDrawable(drawable)
|
||||
}
|
||||
|
||||
@BindingAdapter("movieBehavior", "movieBehaviorText")
|
||||
fun setMovieBehavior(view: TextView, isMovieBehavior: Boolean, text: String) {
|
||||
(view.tag as? Job)?.cancel()
|
||||
view.tag = null
|
||||
if (isMovieBehavior) {
|
||||
view.tag = GlobalScope.launch(Dispatchers.Main.immediate) {
|
||||
while (true) {
|
||||
delay(150)
|
||||
view.text = text.replaceRandomWithSpecial()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
view.text = text
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("onTouch")
|
||||
fun setOnTouchListener(view: View, listener: View.OnTouchListener) {
|
||||
view.setOnTouchListener(listener)
|
||||
}
|
||||
|
||||
@BindingAdapter("scrollToLast")
|
||||
fun setScrollToLast(view: RecyclerView, shouldScrollToLast: Boolean) {
|
||||
|
||||
fun scrollToLast() = UiThreadHandler.handler.postDelayed({
|
||||
view.scrollToPosition(view.adapter?.itemCount?.minus(1) ?: 0)
|
||||
}, 30)
|
||||
|
||||
fun wait(callback: () -> Unit) {
|
||||
UiThreadHandler.handler.postDelayed(callback, 1000)
|
||||
}
|
||||
|
||||
fun RecyclerView.Adapter<*>.setListener() {
|
||||
val observer = object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||
scrollToLast()
|
||||
}
|
||||
}
|
||||
registerAdapterDataObserver(observer)
|
||||
view.setTag(R.id.recyclerScrollListener, observer)
|
||||
}
|
||||
|
||||
fun RecyclerView.Adapter<*>.removeListener() {
|
||||
val observer =
|
||||
view.getTag(R.id.recyclerScrollListener) as? RecyclerView.AdapterDataObserver ?: return
|
||||
unregisterAdapterDataObserver(observer)
|
||||
}
|
||||
|
||||
fun trySetListener(): Unit = view.adapter?.setListener() ?: wait { trySetListener() }
|
||||
|
||||
if (shouldScrollToLast) {
|
||||
trySetListener()
|
||||
} else {
|
||||
view.adapter?.removeListener()
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("isEnabled")
|
||||
fun setEnabled(view: View, isEnabled: Boolean) {
|
||||
view.isEnabled = isEnabled
|
||||
}
|
||||
|
||||
@BindingAdapter("error")
|
||||
fun TextInputLayout.setErrorString(error: String) {
|
||||
val newError = error.let { if (it.isEmpty()) null else it }
|
||||
if (this.error == null && newError == null) return
|
||||
this.error = newError
|
||||
}
|
||||
|
||||
// md2
|
||||
|
||||
@BindingAdapter(
|
||||
"android:layout_marginLeft",
|
||||
"android:layout_marginTop",
|
||||
"android:layout_marginRight",
|
||||
"android:layout_marginBottom",
|
||||
"android:layout_marginStart",
|
||||
"android:layout_marginEnd",
|
||||
requireAll = false
|
||||
)
|
||||
fun View.setMargins(
|
||||
marginLeft: Int?,
|
||||
marginTop: Int?,
|
||||
marginRight: Int?,
|
||||
marginBottom: Int?,
|
||||
marginStart: Int?,
|
||||
marginEnd: Int?
|
||||
) = updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
marginLeft?.let { leftMargin = it }
|
||||
marginTop?.let { topMargin = it }
|
||||
marginRight?.let { rightMargin = it }
|
||||
marginBottom?.let { bottomMargin = it }
|
||||
marginStart?.let { this.marginStart = it }
|
||||
marginEnd?.let { this.marginEnd = it }
|
||||
}
|
||||
|
||||
@BindingAdapter("nestedScrollingEnabled")
|
||||
fun RecyclerView.setNestedScrolling(enabled: Boolean) {
|
||||
isNestedScrollingEnabled = enabled
|
||||
}
|
||||
|
||||
@BindingAdapter("isSelected")
|
||||
fun View.isSelected(isSelected: Boolean) {
|
||||
this.isSelected = isSelected
|
||||
}
|
||||
|
||||
@BindingAdapter("dividerVertical", "dividerHorizontal", requireAll = false)
|
||||
fun RecyclerView.setDividers(dividerVertical: Drawable?, dividerHorizontal: Drawable?) {
|
||||
if (dividerHorizontal != null) {
|
||||
DividerItemDecoration(context, LinearLayoutManager.HORIZONTAL).apply {
|
||||
setDrawable(dividerHorizontal)
|
||||
}.let { addItemDecoration(it) }
|
||||
}
|
||||
if (dividerVertical != null) {
|
||||
DividerItemDecoration(context, LinearLayoutManager.VERTICAL).apply {
|
||||
setDrawable(dividerVertical)
|
||||
}.let { addItemDecoration(it) }
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("app:icon")
|
||||
fun Button.setIconRes(res: Int) {
|
||||
(this as MaterialButton).setIconResource(res)
|
||||
}
|
||||
|
||||
@BindingAdapter("strokeWidth")
|
||||
fun MaterialCardView.setCardStrokeWidthBound(stroke: Float) {
|
||||
strokeWidth = stroke.roundToInt()
|
||||
}
|
||||
|
||||
@BindingAdapter("onMenuClick")
|
||||
fun Toolbar.setOnMenuClickListener(listener: Toolbar.OnMenuItemClickListener) {
|
||||
setOnMenuItemClickListener(listener)
|
||||
}
|
||||
|
||||
@BindingAdapter("onCloseClicked")
|
||||
fun Chip.setOnCloseClickedListenerBinding(listener: View.OnClickListener) {
|
||||
setOnCloseIconClickListener(listener)
|
||||
}
|
||||
|
||||
@BindingAdapter("progressAnimated")
|
||||
fun ProgressBar.setProgressAnimated(newProgress: Int) {
|
||||
val animator = tag as? ValueAnimator
|
||||
animator?.cancel()
|
||||
|
||||
ValueAnimator.ofInt(progress, newProgress).apply {
|
||||
interpolator = FastOutSlowInInterpolator()
|
||||
addUpdateListener { progress = it.animatedValue as Int }
|
||||
tag = this
|
||||
}.start()
|
||||
}
|
||||
|
||||
@BindingAdapter("android:text")
|
||||
fun TextView.setTextSafe(text: Int) {
|
||||
if (text == 0) this.text = null else setText(text)
|
||||
}
|
||||
|
||||
@BindingAdapter("android:onLongClick")
|
||||
fun View.setOnLongClickListenerBinding(listener: () -> Unit) {
|
||||
setOnLongClickListener {
|
||||
listener()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("strikeThrough")
|
||||
fun TextView.setStrikeThroughEnabled(useStrikeThrough: Boolean) {
|
||||
paintFlags = if (useStrikeThrough) {
|
||||
paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
||||
} else {
|
||||
paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
||||
}
|
||||
}
|
||||
|
||||
interface OnPopupMenuItemClickListener {
|
||||
fun onMenuItemClick(itemId: Int)
|
||||
}
|
||||
|
||||
@BindingAdapter("popupMenu", "popupMenuOnClickListener", requireAll = false)
|
||||
fun View.setPopupMenu(popupMenu: Int, listener: OnPopupMenuItemClickListener) {
|
||||
val menu = tag as? PopupMenu ?: let {
|
||||
val themeWrapper = ContextThemeWrapper(context, R.style.Foundation_PopupMenu)
|
||||
PopupMenu(themeWrapper, this)
|
||||
}
|
||||
tag = menu.apply {
|
||||
this.menu.clear()
|
||||
menuInflater.inflate(popupMenu, this.menu)
|
||||
setOnMenuItemClickListener {
|
||||
listener.onMenuItemClick(it.itemId)
|
||||
true
|
||||
}
|
||||
}
|
||||
setOnClickListener {
|
||||
(tag as PopupMenu).show()
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("spanCount")
|
||||
fun RecyclerView.setSpanCount(count: Int) {
|
||||
when (val lama = layoutManager) {
|
||||
is GridLayoutManager -> lama.spanCount = count
|
||||
is StaggeredGridLayoutManager -> lama.spanCount = count
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("state")
|
||||
fun setState(view: IndeterminateCheckBox, state: Boolean?) {
|
||||
if (view.state != state)
|
||||
view.state = state
|
||||
}
|
||||
|
||||
@InverseBindingAdapter(attribute = "state")
|
||||
fun getState(view: IndeterminateCheckBox) = view.state
|
||||
|
||||
@BindingAdapter("stateAttrChanged")
|
||||
fun setListeners(
|
||||
view: IndeterminateCheckBox,
|
||||
attrChange: InverseBindingListener
|
||||
) {
|
||||
view.setOnStateChangedListener { _, _ ->
|
||||
attrChange.onChange()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.topjohnwu.magisk.databinding
|
||||
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.topjohnwu.magisk.model.entity.recycler.LenientRvItem
|
||||
import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter
|
||||
|
||||
class RvBindingAdapter<T : RvItem> : BindingRecyclerViewAdapter<T>() {
|
||||
|
||||
private var recyclerView: RecyclerView? = null
|
||||
|
||||
override fun onBindBinding(
|
||||
binding: ViewDataBinding,
|
||||
variableId: Int,
|
||||
layoutRes: Int,
|
||||
position: Int,
|
||||
item: T
|
||||
) {
|
||||
super.onBindBinding(binding, variableId, layoutRes, position, item)
|
||||
|
||||
when (item) {
|
||||
is LenientRvItem<*> -> {
|
||||
val recycler = recyclerView ?: return
|
||||
item.onBindingBound(binding)
|
||||
item.onBindingBound(binding, recycler)
|
||||
}
|
||||
else -> item.onBindingBound(binding)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||
super.onAttachedToRecyclerView(recyclerView)
|
||||
this.recyclerView = recyclerView
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user