mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-17 21:47:37 +00:00
Use libs instead of copy code
This commit is contained in:
parent
a8640f52ef
commit
11600fc116
@ -74,6 +74,9 @@ dependencies {
|
||||
implementation("com.github.topjohnwu:lz4-java:1.7.1")
|
||||
implementation("com.jakewharton.timber:timber:4.7.1")
|
||||
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
|
||||
implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0")
|
||||
implementation("dev.rikka.rikkax.insets:insets:1.1.1")
|
||||
implementation("dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.3.1")
|
||||
|
||||
val vBAdapt = "4.0.0"
|
||||
val bindingAdapter = "me.tatarka.bindingcollectionadapter2:bindingcollectionadapter"
|
||||
|
@ -14,9 +14,10 @@ import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDirections
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.arch.inflater.LayoutInflaterFactory
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||
import rikka.insets.WindowInsetsHelper
|
||||
import rikka.layoutinflater.view.LayoutInflaterFactory
|
||||
|
||||
abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
BaseActivity(), BaseUIComponent<VM> {
|
||||
@ -44,6 +45,7 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
layoutInflater.factory2 = LayoutInflaterFactory(delegate)
|
||||
.addOnViewCreatedListener(WindowInsetsHelper.LISTENER)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
@ -1,85 +0,0 @@
|
||||
package com.topjohnwu.magisk.arch.inflater
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.InflateException
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.collection.SimpleArrayMap
|
||||
import java.lang.reflect.Constructor
|
||||
|
||||
open class LayoutInflaterFactory(private val delegate: AppCompatDelegate) : LayoutInflater.Factory2 {
|
||||
|
||||
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
|
||||
return onCreateView(null, name, context, attrs)
|
||||
}
|
||||
|
||||
override fun onCreateView(parent: View?, name: String, context: Context, attrs: AttributeSet): View? {
|
||||
val view = delegate.createView(parent, name, context, attrs)
|
||||
?: LayoutInflaterFactoryDefaultImpl.createViewFromTag(context, name, attrs)
|
||||
onViewCreated(view, parent, name, context, attrs)
|
||||
return view
|
||||
}
|
||||
|
||||
open fun onViewCreated(view: View?, parent: View?, name: String, context: Context, attrs: AttributeSet) {
|
||||
if (view == null) return
|
||||
|
||||
WindowInsetsHelper.attach(view, attrs)
|
||||
}
|
||||
}
|
||||
|
||||
private object LayoutInflaterFactoryDefaultImpl {
|
||||
|
||||
private val constructorSignature = arrayOf(
|
||||
Context::class.java, AttributeSet::class.java)
|
||||
|
||||
private val classPrefixList = arrayOf(
|
||||
"android.widget.",
|
||||
"android.view.",
|
||||
"android.webkit."
|
||||
)
|
||||
|
||||
private val constructorMap = SimpleArrayMap<String, Constructor<out View?>>()
|
||||
|
||||
fun createViewFromTag(context: Context, name: String, attrs: AttributeSet): View? {
|
||||
var name = name
|
||||
if (name == "view") {
|
||||
name = attrs.getAttributeValue(null, "class")
|
||||
}
|
||||
return try {
|
||||
if (-1 == name.indexOf('.')) {
|
||||
for (prefix in classPrefixList) {
|
||||
val view: View? = createViewByPrefix(context, name, attrs, prefix)
|
||||
if (view != null) {
|
||||
return view
|
||||
}
|
||||
}
|
||||
null
|
||||
} else {
|
||||
createViewByPrefix(context, name, attrs, null)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(ClassNotFoundException::class, InflateException::class)
|
||||
private fun createViewByPrefix(context: Context, name: String, attrs: AttributeSet, prefix: String?): View? {
|
||||
var constructor = constructorMap[name]
|
||||
return try {
|
||||
if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it
|
||||
val clazz = Class.forName(
|
||||
if (prefix != null) prefix + name else name,
|
||||
false,
|
||||
context.classLoader).asSubclass(View::class.java)
|
||||
constructor = clazz.getConstructor(*constructorSignature)
|
||||
constructorMap.put(name, constructor)
|
||||
}
|
||||
constructor!!.isAccessible = true
|
||||
constructor.newInstance(context, attrs)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
@ -1,284 +0,0 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.topjohnwu.magisk.arch.inflater
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.annotation.TargetApi
|
||||
import android.graphics.Rect
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity.*
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.OnApplyWindowInsetsListener
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.topjohnwu.magisk.R
|
||||
|
||||
private typealias ApplyInsetsCallback<T> = (insets: Insets, left: Boolean, top: Boolean, right: Boolean, bottom: Boolean) -> T
|
||||
|
||||
private class ApplyInsets(private val out: Rect) : ApplyInsetsCallback<Unit> {
|
||||
|
||||
override fun invoke(insets: Insets, left: Boolean, top: Boolean, right: Boolean, bottom: Boolean) {
|
||||
out.left += if (left) insets.left else 0
|
||||
out.top += if (top) insets.top else 0
|
||||
out.right += if (right) insets.right else 0
|
||||
out.bottom += if (bottom) insets.bottom else 0
|
||||
}
|
||||
}
|
||||
|
||||
private class ConsumeInsets : ApplyInsetsCallback<Insets> {
|
||||
|
||||
override fun invoke(insets: Insets, left: Boolean, top: Boolean, right: Boolean, bottom: Boolean): Insets {
|
||||
val insetsLeft = if (left) 0 else insets.left
|
||||
val insetsTop = if (top) 0 else insets.top
|
||||
val insetsRight = if (right) 0 else insets.right
|
||||
val insetsBottom = if (bottom) 0 else insets.bottom
|
||||
return Insets.of(insetsLeft, insetsTop, insetsRight, insetsBottom)
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
open class WindowInsetsHelper private constructor(
|
||||
private val view: View,
|
||||
private val fitSystemWindows: Int,
|
||||
private val layout_fitsSystemWindowsInsets: Int,
|
||||
private val consumeSystemWindows: Int) : OnApplyWindowInsetsListener {
|
||||
|
||||
internal var initialPaddingLeft: Int = view.paddingLeft
|
||||
internal var initialPaddingTop: Int = view.paddingTop
|
||||
internal var initialPaddingRight: Int = view.paddingRight
|
||||
internal var initialPaddingBottom: Int = view.paddingBottom
|
||||
|
||||
private var initialMargin = false
|
||||
internal var initialMarginLeft: Int = 0
|
||||
internal var initialMarginTop: Int = 0
|
||||
internal var initialMarginRight: Int = 0
|
||||
internal var initialMarginBottom: Int = 0
|
||||
internal var initialMarginStart: Int = 0
|
||||
internal var initialMarginEnd: Int = 0
|
||||
|
||||
private var lastInsets: WindowInsetsCompat? = null
|
||||
|
||||
open fun setInitialPadding(left: Int, top: Int, right: Int, bottom: Int) {
|
||||
initialPaddingLeft = left
|
||||
initialPaddingTop = top
|
||||
initialPaddingRight = right
|
||||
initialPaddingBottom = bottom
|
||||
|
||||
lastInsets?.let { applyWindowInsets(it) }
|
||||
}
|
||||
|
||||
open fun setInitialPaddingRelative(start: Int, top: Int, end: Int, bottom: Int) {
|
||||
val isRTL = view.layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||
if (isRTL) {
|
||||
setInitialPadding(start, top, end, bottom)
|
||||
} else {
|
||||
setInitialPadding(start, top, end, bottom)
|
||||
}
|
||||
}
|
||||
|
||||
open fun setInitialMargin(left: Int, top: Int, right: Int, bottom: Int) {
|
||||
initialPaddingLeft = left
|
||||
initialPaddingTop = top
|
||||
initialPaddingRight = right
|
||||
initialPaddingBottom = bottom
|
||||
|
||||
lastInsets?.let { applyWindowInsets(it) }
|
||||
}
|
||||
|
||||
open fun setInitialMarginRelative(start: Int, top: Int, end: Int, bottom: Int) {
|
||||
initialMarginStart = start
|
||||
initialMarginTop = top
|
||||
initialMarginEnd = end
|
||||
initialMarginBottom = bottom
|
||||
|
||||
lastInsets?.let { applyWindowInsets(it) }
|
||||
}
|
||||
|
||||
@SuppressLint("RtlHardcoded")
|
||||
private fun <T> applyInsets(insets: Insets, fit: Int, callback: ApplyInsetsCallback<T>): T {
|
||||
val relativeMode = (fit and RELATIVE_LAYOUT_DIRECTION) == RELATIVE_LAYOUT_DIRECTION
|
||||
|
||||
val isRTL = view.layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||
|
||||
val left: Boolean
|
||||
val top = fit and TOP == TOP
|
||||
val right: Boolean
|
||||
val bottom = fit and BOTTOM == BOTTOM
|
||||
|
||||
if (relativeMode) {
|
||||
val start = fit and START == START
|
||||
val end = fit and END == END
|
||||
left = (!isRTL && start) || (isRTL && end)
|
||||
right = (!isRTL && end) || (isRTL && start)
|
||||
} else {
|
||||
left = fit and LEFT == LEFT
|
||||
right = fit and RIGHT == RIGHT
|
||||
}
|
||||
|
||||
return callback.invoke(insets, left, top, right, bottom)
|
||||
}
|
||||
|
||||
private fun applyWindowInsets(windowInsets: WindowInsetsCompat): WindowInsetsCompat {
|
||||
if (fitSystemWindows != 0) {
|
||||
val padding = Rect(initialPaddingLeft, initialPaddingTop, initialPaddingRight, initialPaddingBottom)
|
||||
applyInsets(windowInsets.systemWindowInsets, fitSystemWindows, ApplyInsets(padding))
|
||||
view.setPadding(padding.left, padding.top, padding.right, padding.bottom)
|
||||
}
|
||||
|
||||
if (layout_fitsSystemWindowsInsets != 0) {
|
||||
if (!initialMargin) {
|
||||
initialMarginLeft = (view.layoutParams as? ViewGroup.MarginLayoutParams)?.leftMargin ?: 0
|
||||
initialMarginTop = (view.layoutParams as? ViewGroup.MarginLayoutParams)?.topMargin ?: 0
|
||||
initialMarginRight = (view.layoutParams as? ViewGroup.MarginLayoutParams)?.rightMargin ?: 0
|
||||
initialMarginBottom = (view.layoutParams as? ViewGroup.MarginLayoutParams)?.bottomMargin ?: 0
|
||||
initialMarginStart = (view.layoutParams as? ViewGroup.MarginLayoutParams)?.marginStart ?: 0
|
||||
initialMarginEnd = (view.layoutParams as? ViewGroup.MarginLayoutParams)?.marginEnd ?: 0
|
||||
initialMargin = true
|
||||
}
|
||||
|
||||
val margin = if ((layout_fitsSystemWindowsInsets and RELATIVE_LAYOUT_DIRECTION) == RELATIVE_LAYOUT_DIRECTION)
|
||||
Rect(initialMarginLeft, initialMarginTop, initialMarginRight, initialMarginBottom)
|
||||
else
|
||||
Rect(initialMarginStart, initialMarginTop, initialMarginEnd, initialMarginBottom)
|
||||
|
||||
applyInsets(windowInsets.systemWindowInsets, layout_fitsSystemWindowsInsets, ApplyInsets(margin))
|
||||
|
||||
val lp = view.layoutParams
|
||||
if (lp is ViewGroup.MarginLayoutParams) {
|
||||
lp.topMargin = margin.top
|
||||
lp.bottomMargin = margin.bottom
|
||||
|
||||
if ((layout_fitsSystemWindowsInsets and RELATIVE_LAYOUT_DIRECTION) == RELATIVE_LAYOUT_DIRECTION) {
|
||||
lp.marginStart = margin.left
|
||||
lp.marginEnd = margin.right
|
||||
} else {
|
||||
lp.leftMargin = margin.left
|
||||
lp.rightMargin = margin.right
|
||||
}
|
||||
|
||||
view.layoutParams = lp
|
||||
}
|
||||
}
|
||||
|
||||
val systemWindowInsets = if (consumeSystemWindows != 0) applyInsets(windowInsets.systemWindowInsets, consumeSystemWindows, ConsumeInsets()) else windowInsets.systemWindowInsets
|
||||
|
||||
return WindowInsetsCompat.Builder(windowInsets)
|
||||
.setSystemWindowInsets(systemWindowInsets)
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun onApplyWindowInsets(view: View, insets: WindowInsetsCompat): WindowInsetsCompat {
|
||||
if (lastInsets == insets) {
|
||||
return insets
|
||||
}
|
||||
|
||||
lastInsets = insets
|
||||
|
||||
return applyWindowInsets(insets)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun attach(view: View, attrs: AttributeSet) {
|
||||
val a = view.context.obtainStyledAttributes(attrs, R.styleable.WindowInsetsHelper, 0, 0)
|
||||
val edgeToEdge = a.getBoolean(R.styleable.WindowInsetsHelper_edgeToEdge, false)
|
||||
val fitsSystemWindowsInsets = a.getInt(R.styleable.WindowInsetsHelper_fitsSystemWindowsInsets, 0)
|
||||
val layout_fitsSystemWindowsInsets = a.getInt(R.styleable.WindowInsetsHelper_layout_fitsSystemWindowsInsets, 0)
|
||||
val consumeSystemWindowsInsets = a.getInt(R.styleable.WindowInsetsHelper_consumeSystemWindowsInsets, 0)
|
||||
a.recycle()
|
||||
|
||||
attach(view, edgeToEdge, fitsSystemWindowsInsets, layout_fitsSystemWindowsInsets, consumeSystemWindowsInsets)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun attach(view: View, edgeToEdge: Boolean, fitsSystemWindowsInsets: Int, layout_fitsSystemWindowsInsets: Int, consumeSystemWindowsInsets: Int) {
|
||||
if (edgeToEdge) {
|
||||
view.systemUiVisibility = (view.systemUiVisibility
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
|
||||
}
|
||||
|
||||
if (fitsSystemWindowsInsets == 0 && layout_fitsSystemWindowsInsets == 0 && consumeSystemWindowsInsets == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
val listener = WindowInsetsHelper(view, fitsSystemWindowsInsets, layout_fitsSystemWindowsInsets, consumeSystemWindowsInsets)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(view, listener)
|
||||
view.setTag(R.id.tag_rikka_material_WindowInsetsHelper, listener)
|
||||
|
||||
if (!view.isAttachedToWindow) {
|
||||
view.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
|
||||
override fun onViewAttachedToWindow(v: View) {
|
||||
v.removeOnAttachStateChangeListener(this)
|
||||
v.requestApplyInsets()
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(v: View) = Unit
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val View.windowInsetsHelper: WindowInsetsHelper?
|
||||
get() {
|
||||
val value = getTag(R.id.tag_rikka_material_WindowInsetsHelper)
|
||||
return if (value is WindowInsetsHelper) value else null
|
||||
}
|
||||
|
||||
val View.initialPaddingLeft: Int
|
||||
get() = windowInsetsHelper?.initialPaddingLeft ?: 0
|
||||
|
||||
val View.initialPaddingTop: Int
|
||||
get() = windowInsetsHelper?.initialPaddingTop ?: 0
|
||||
|
||||
val View.initialPaddingRight: Int
|
||||
get() = windowInsetsHelper?.initialPaddingRight ?: 0
|
||||
|
||||
val View.initialPaddingBottom: Int
|
||||
get() = windowInsetsHelper?.initialPaddingBottom ?: 0
|
||||
|
||||
val View.initialPaddingStart: Int
|
||||
get() = if (layoutDirection == View.LAYOUT_DIRECTION_RTL) initialPaddingRight else initialPaddingLeft
|
||||
|
||||
val View.initialPaddingEnd: Int
|
||||
get() = if (layoutDirection == View.LAYOUT_DIRECTION_RTL) initialPaddingLeft else initialPaddingRight
|
||||
|
||||
fun View.setInitialPadding(left: Int, top: Int, right: Int, bottom: Int) {
|
||||
windowInsetsHelper?.setInitialPadding(left, top, right, bottom)
|
||||
}
|
||||
|
||||
fun View.setInitialPaddingRelative(start: Int, top: Int, end: Int, bottom: Int) {
|
||||
windowInsetsHelper?.setInitialPaddingRelative(start, top, end, bottom)
|
||||
}
|
||||
|
||||
val View.initialMarginLeft: Int
|
||||
get() = windowInsetsHelper?.initialMarginLeft ?: 0
|
||||
|
||||
val View.initialMarginTop: Int
|
||||
get() = windowInsetsHelper?.initialMarginTop ?: 0
|
||||
|
||||
val View.initialMarginRight: Int
|
||||
get() = windowInsetsHelper?.initialMarginRight ?: 0
|
||||
|
||||
val View.initialMarginBottom: Int
|
||||
get() = windowInsetsHelper?.initialMarginBottom ?: 0
|
||||
|
||||
val View.initialMarginStart: Int
|
||||
get() = windowInsetsHelper?.initialMarginStart ?: 0
|
||||
|
||||
val View.initialMarginEnd: Int
|
||||
get() = windowInsetsHelper?.initialMarginEnd ?: 0
|
||||
|
||||
fun View.setInitialMargin(left: Int, top: Int, right: Int, bottom: Int) {
|
||||
windowInsetsHelper?.setInitialMargin(left, top, right, bottom)
|
||||
}
|
||||
|
||||
fun View.setInitialMarginRelative(start: Int, top: Int, end: Int, bottom: Int) {
|
||||
windowInsetsHelper?.setInitialMarginRelative(start, top, end, bottom)
|
||||
}
|
@ -1,193 +0,0 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.topjohnwu.magisk.ktx
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import android.widget.EdgeEffect
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.topjohnwu.magisk.R
|
||||
|
||||
fun RecyclerView.addInvalidateItemDecorationsObserver() {
|
||||
|
||||
adapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||
invalidateItemDecorations()
|
||||
}
|
||||
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
invalidateItemDecorations()
|
||||
}
|
||||
|
||||
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
|
||||
invalidateItemDecorations()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun RecyclerView.addVerticalPadding(paddingTop: Int = 0, paddingBottom: Int = 0) {
|
||||
addItemDecoration(VerticalPaddingDecoration(paddingTop, paddingBottom))
|
||||
}
|
||||
|
||||
private class VerticalPaddingDecoration(private val paddingTop: Int = 0, private val paddingBottom: Int = 0) : RecyclerView.ItemDecoration() {
|
||||
|
||||
private var allowTop: Boolean = true
|
||||
private var allowBottom: Boolean = true
|
||||
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
val adapter = parent.adapter ?: return
|
||||
val position = parent.getChildAdapterPosition(view)
|
||||
val count = adapter.itemCount
|
||||
if (position == 0 && allowTop) {
|
||||
outRect.top = paddingTop
|
||||
} else if (position == count - 1 && allowBottom) {
|
||||
outRect.bottom = paddingBottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun RecyclerView.addSimpleItemDecoration(
|
||||
left: Int = 0,
|
||||
top: Int = 0,
|
||||
right: Int = 0,
|
||||
bottom: Int = 0,
|
||||
) {
|
||||
addItemDecoration(SimpleItemDecoration(left, top, right, bottom))
|
||||
}
|
||||
|
||||
private class SimpleItemDecoration(
|
||||
private val left: Int = 0,
|
||||
private val top: Int = 0,
|
||||
private val right: Int = 0,
|
||||
private val bottom: Int = 0
|
||||
) : RecyclerView.ItemDecoration() {
|
||||
|
||||
private var allowLeft: Boolean = true
|
||||
private var allowTop: Boolean = true
|
||||
private var allowRight: Boolean = true
|
||||
private var allowBottom: Boolean = true
|
||||
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
if (parent.adapter == null) {
|
||||
return
|
||||
}
|
||||
if (allowLeft) {
|
||||
outRect.left = left
|
||||
}
|
||||
if (allowTop) {
|
||||
outRect.top = top
|
||||
}
|
||||
if (allowRight) {
|
||||
outRect.right = right
|
||||
}
|
||||
if (allowBottom) {
|
||||
outRect.top = bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun RecyclerView.fixEdgeEffect(overScrollIfContentScrolls: Boolean = true, alwaysClipToPadding: Boolean = true) {
|
||||
if (overScrollIfContentScrolls) {
|
||||
val listener = OverScrollIfContentScrollsListener()
|
||||
addOnLayoutChangeListener(listener)
|
||||
setTag(R.id.tag_rikka_recyclerView_OverScrollIfContentScrollsListener, listener)
|
||||
} else {
|
||||
val listener = getTag(R.id.tag_rikka_recyclerView_OverScrollIfContentScrollsListener) as? OverScrollIfContentScrollsListener
|
||||
if (listener != null) {
|
||||
removeOnLayoutChangeListener(listener)
|
||||
setTag(R.id.tag_rikka_recyclerView_OverScrollIfContentScrollsListener, null)
|
||||
}
|
||||
}
|
||||
|
||||
edgeEffectFactory = if (alwaysClipToPadding && !clipToPadding) {
|
||||
AlwaysClipToPaddingEdgeEffectFactory()
|
||||
} else {
|
||||
RecyclerView.EdgeEffectFactory()
|
||||
}
|
||||
}
|
||||
|
||||
private class OverScrollIfContentScrollsListener : View.OnLayoutChangeListener {
|
||||
private var show = true
|
||||
override fun onLayoutChange(v: View, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) {
|
||||
if (shouldDrawOverScroll(v as RecyclerView) != show) {
|
||||
show = !show
|
||||
if (show) {
|
||||
v.setOverScrollMode(View.OVER_SCROLL_IF_CONTENT_SCROLLS)
|
||||
} else {
|
||||
v.setOverScrollMode(View.OVER_SCROLL_NEVER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldDrawOverScroll(recyclerView: RecyclerView): Boolean {
|
||||
if (recyclerView.layoutManager == null || recyclerView.adapter == null || recyclerView.adapter!!.itemCount == 0) {
|
||||
return false
|
||||
}
|
||||
if (recyclerView.layoutManager is LinearLayoutManager) {
|
||||
val itemCount = recyclerView.layoutManager!!.itemCount
|
||||
val firstPosition: Int = (recyclerView.layoutManager as LinearLayoutManager?)!!.findFirstCompletelyVisibleItemPosition()
|
||||
val lastPosition: Int = (recyclerView.layoutManager as LinearLayoutManager?)!!.findLastCompletelyVisibleItemPosition()
|
||||
return firstPosition != 0 || lastPosition != itemCount - 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private class AlwaysClipToPaddingEdgeEffectFactory : RecyclerView.EdgeEffectFactory() {
|
||||
|
||||
override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect {
|
||||
|
||||
return object : EdgeEffect(view.context) {
|
||||
private var ensureSize = false
|
||||
|
||||
private fun ensureSize() {
|
||||
if (ensureSize) return
|
||||
ensureSize = true
|
||||
|
||||
when (direction) {
|
||||
DIRECTION_LEFT -> {
|
||||
setSize(view.measuredHeight - view.paddingTop - view.paddingBottom,
|
||||
view.measuredWidth - view.paddingLeft - view.paddingRight)
|
||||
}
|
||||
DIRECTION_TOP -> {
|
||||
setSize(view.measuredWidth - view.paddingLeft - view.paddingRight,
|
||||
view.measuredHeight - view.paddingTop - view.paddingBottom)
|
||||
}
|
||||
DIRECTION_RIGHT -> {
|
||||
setSize(view.measuredHeight - view.paddingTop - view.paddingBottom,
|
||||
view.measuredWidth - view.paddingLeft - view.paddingRight)
|
||||
}
|
||||
DIRECTION_BOTTOM -> {
|
||||
setSize(view.measuredWidth - view.paddingLeft - view.paddingRight,
|
||||
view.measuredHeight - view.paddingTop - view.paddingBottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun draw(c: Canvas): Boolean {
|
||||
ensureSize()
|
||||
|
||||
val restore = c.save()
|
||||
when (direction) {
|
||||
DIRECTION_LEFT -> {
|
||||
c.translate(view.paddingBottom.toFloat(), 0f)
|
||||
}
|
||||
DIRECTION_TOP -> {
|
||||
c.translate(view.paddingLeft.toFloat(), view.paddingTop.toFloat())
|
||||
}
|
||||
DIRECTION_RIGHT -> {
|
||||
c.translate(-view.paddingTop.toFloat(), 0f)
|
||||
}
|
||||
DIRECTION_BOTTOM -> {
|
||||
c.translate(view.paddingRight.toFloat(), view.paddingBottom.toFloat())
|
||||
}
|
||||
}
|
||||
val res = super.draw(c)
|
||||
c.restoreToCount(restore)
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,10 +12,10 @@ import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||
import com.topjohnwu.magisk.databinding.FragmentDenyMd2Binding
|
||||
import com.topjohnwu.magisk.di.viewModel
|
||||
import com.topjohnwu.magisk.ktx.addSimpleItemDecoration
|
||||
import com.topjohnwu.magisk.ktx.addVerticalPadding
|
||||
import com.topjohnwu.magisk.ktx.fixEdgeEffect
|
||||
import com.topjohnwu.magisk.ktx.hideKeyboard
|
||||
import rikka.recyclerview.addEdgeSpacing
|
||||
import rikka.recyclerview.addItemSpacing
|
||||
import rikka.recyclerview.fixEdgeEffect
|
||||
|
||||
class DenyListFragment : BaseUIFragment<DenyListViewModel, FragmentDenyMd2Binding>() {
|
||||
|
||||
@ -39,20 +39,11 @@ class DenyListFragment : BaseUIFragment<DenyListViewModel, FragmentDenyMd2Bindin
|
||||
}
|
||||
})
|
||||
|
||||
val resource = requireContext().resources
|
||||
val l_50 = resource.getDimensionPixelSize(R.dimen.l_50)
|
||||
val l1 = resource.getDimensionPixelSize(R.dimen.l1)
|
||||
binding.appList.addVerticalPadding(
|
||||
l_50,
|
||||
l1 + resource.getDimensionPixelSize(R.dimen.internal_action_bar_size)
|
||||
)
|
||||
binding.appList.addSimpleItemDecoration(
|
||||
left = l1,
|
||||
top = l_50,
|
||||
right = l1,
|
||||
bottom = l_50,
|
||||
)
|
||||
binding.appList.fixEdgeEffect()
|
||||
binding.appList.apply {
|
||||
addEdgeSpacing(top = R.dimen.l_50, bottom = R.dimen.l1)
|
||||
addItemSpacing(R.dimen.l1, R.dimen.l_50, R.dimen.l1)
|
||||
fixEdgeEffect()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPreBind(binding: FragmentDenyMd2Binding) = Unit
|
||||
|
@ -10,11 +10,11 @@ import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||
import com.topjohnwu.magisk.databinding.FragmentLogMd2Binding
|
||||
import com.topjohnwu.magisk.di.viewModel
|
||||
import com.topjohnwu.magisk.ktx.addSimpleItemDecoration
|
||||
import com.topjohnwu.magisk.ktx.addVerticalPadding
|
||||
import com.topjohnwu.magisk.ktx.fixEdgeEffect
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.utils.MotionRevealHelper
|
||||
import rikka.recyclerview.addEdgeSpacing
|
||||
import rikka.recyclerview.addItemSpacing
|
||||
import rikka.recyclerview.fixEdgeEffect
|
||||
|
||||
class LogFragment : BaseUIFragment<LogViewModel, FragmentLogMd2Binding>() {
|
||||
|
||||
@ -47,20 +47,11 @@ class LogFragment : BaseUIFragment<LogViewModel, FragmentLogMd2Binding>() {
|
||||
isMagiskLogVisible = true
|
||||
}
|
||||
|
||||
val resource = requireContext().resources
|
||||
val l_50 = resource.getDimensionPixelSize(R.dimen.l_50)
|
||||
val l1 = resource.getDimensionPixelSize(R.dimen.l1)
|
||||
binding.logFilterSuperuser.logSuperuser.addVerticalPadding(
|
||||
0,
|
||||
l1
|
||||
)
|
||||
binding.logFilterSuperuser.logSuperuser.addSimpleItemDecoration(
|
||||
left = l1,
|
||||
top = l_50,
|
||||
right = l1,
|
||||
bottom = l_50,
|
||||
)
|
||||
binding.logFilterSuperuser.logSuperuser.fixEdgeEffect()
|
||||
binding.logFilterSuperuser.logSuperuser.apply {
|
||||
addEdgeSpacing(bottom = R.dimen.l1)
|
||||
addItemSpacing(R.dimen.l1, R.dimen.l_50, R.dimen.l1)
|
||||
fixEdgeEffect()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,48 +1,20 @@
|
||||
package com.topjohnwu.magisk.ui.module
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||
import com.topjohnwu.magisk.arch.ReselectionTarget
|
||||
import com.topjohnwu.magisk.databinding.FragmentModuleMd2Binding
|
||||
import com.topjohnwu.magisk.di.viewModel
|
||||
import com.topjohnwu.magisk.ktx.*
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener
|
||||
import com.topjohnwu.magisk.utils.MotionRevealHelper
|
||||
import rikka.recyclerview.addEdgeSpacing
|
||||
import rikka.recyclerview.addInvalidateItemDecorationsObserver
|
||||
import rikka.recyclerview.addItemSpacing
|
||||
import rikka.recyclerview.fixEdgeEffect
|
||||
|
||||
class ModuleFragment : BaseUIFragment<ModuleViewModel, FragmentModuleMd2Binding>(),
|
||||
ReselectionTarget {
|
||||
class ModuleFragment : BaseUIFragment<ModuleViewModel, FragmentModuleMd2Binding>() {
|
||||
|
||||
override val layoutRes = R.layout.fragment_module_md2
|
||||
override val viewModel by viewModel<ModuleViewModel>()
|
||||
override val snackbarAnchorView: View
|
||||
get() {
|
||||
return if (isFilterVisible) {
|
||||
binding.moduleFilterInclude.moduleFilterTitleSearch
|
||||
} else {
|
||||
binding.moduleFilterToggle
|
||||
}
|
||||
}
|
||||
|
||||
private val listeners = hashSetOf<EndlessRecyclerScrollListener>()
|
||||
|
||||
private var isFilterVisible
|
||||
get() = binding.moduleFilter.isVisible
|
||||
set(value) {
|
||||
if (!value) hideKeyboard()
|
||||
MotionRevealHelper.withViews(binding.moduleFilter, binding.moduleFilterToggle, value)
|
||||
with(activity as MainActivity) {
|
||||
requestNavigationHidden(value)
|
||||
setDisplayHomeAsUpEnabled(value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
@ -53,92 +25,14 @@ class ModuleFragment : BaseUIFragment<ModuleViewModel, FragmentModuleMd2Binding>
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.moduleFilterToggle.setOnClickListener {
|
||||
isFilterVisible = true
|
||||
}
|
||||
binding.moduleFilterInclude.moduleFilterDone.setOnClickListener {
|
||||
isFilterVisible = false
|
||||
}
|
||||
binding.moduleFilterInclude.moduleFilterList.addOnScrollListener(object :
|
||||
RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
if (newState != RecyclerView.SCROLL_STATE_IDLE) hideKeyboard()
|
||||
}
|
||||
})
|
||||
|
||||
val resource = requireContext().resources
|
||||
val l_50 = resource.getDimensionPixelSize(R.dimen.l_50)
|
||||
val l1 = resource.getDimensionPixelSize(R.dimen.l1)
|
||||
binding.moduleList.apply {
|
||||
addVerticalPadding(
|
||||
l_50,
|
||||
l1 + l_50 + resource.getDimensionPixelSize(R.dimen.internal_action_bar_size)
|
||||
)
|
||||
addSimpleItemDecoration(
|
||||
left = l1,
|
||||
top = l_50,
|
||||
right = l1,
|
||||
bottom = l_50,
|
||||
)
|
||||
addEdgeSpacing(top = R.dimen.l_50, bottom = R.dimen.l1)
|
||||
addItemSpacing(R.dimen.l1, R.dimen.l_50, R.dimen.l1)
|
||||
fixEdgeEffect()
|
||||
post {
|
||||
addInvalidateItemDecorationsObserver()
|
||||
post { addInvalidateItemDecorationsObserver() }
|
||||
}
|
||||
}
|
||||
|
||||
binding.moduleFilterInclude.moduleFilterList.apply {
|
||||
addVerticalPadding(
|
||||
l_50,
|
||||
l_50
|
||||
)
|
||||
addSimpleItemDecoration(
|
||||
left = l1,
|
||||
top = l_50,
|
||||
right = l1,
|
||||
bottom = l_50,
|
||||
)
|
||||
fixEdgeEffect()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
listeners.forEach {
|
||||
binding.moduleList.removeOnScrollListener(it)
|
||||
binding.moduleFilterInclude.moduleFilterList.removeOnScrollListener(it)
|
||||
}
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
if (isFilterVisible) {
|
||||
isFilterVisible = false
|
||||
return true
|
||||
}
|
||||
return super.onBackPressed()
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.menu_module_md2, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_refresh -> viewModel.forceRefresh()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
override fun onReselected() {
|
||||
binding.moduleList.scrollToPosition(10)
|
||||
binding.moduleList.also { it.post { it.smoothScrollToPosition(0) } }
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
override fun onPreBind(binding: FragmentModuleMd2Binding) = Unit
|
||||
|
||||
}
|
||||
|
@ -6,10 +6,9 @@ import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||
import com.topjohnwu.magisk.databinding.FragmentSettingsMd2Binding
|
||||
import com.topjohnwu.magisk.di.viewModel
|
||||
import com.topjohnwu.magisk.ktx.addSimpleItemDecoration
|
||||
import com.topjohnwu.magisk.ktx.addVerticalPadding
|
||||
import com.topjohnwu.magisk.ktx.fixEdgeEffect
|
||||
import com.topjohnwu.magisk.ktx.setOnViewReadyListener
|
||||
import rikka.recyclerview.addEdgeSpacing
|
||||
import rikka.recyclerview.addItemSpacing
|
||||
import rikka.recyclerview.fixEdgeEffect
|
||||
|
||||
class SettingsFragment : BaseUIFragment<SettingsViewModel, FragmentSettingsMd2Binding>() {
|
||||
|
||||
@ -24,24 +23,11 @@ class SettingsFragment : BaseUIFragment<SettingsViewModel, FragmentSettingsMd2Bi
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.settingsList.setOnViewReadyListener {
|
||||
binding.settingsList.scrollToPosition(0)
|
||||
binding.settingsList.apply {
|
||||
addEdgeSpacing(bottom = R.dimen.l1)
|
||||
addItemSpacing(R.dimen.l1, R.dimen.l_50, R.dimen.l1)
|
||||
fixEdgeEffect()
|
||||
}
|
||||
|
||||
val resource = requireContext().resources
|
||||
val l_50 = resource.getDimensionPixelSize(R.dimen.l_50)
|
||||
val l1 = resource.getDimensionPixelSize(R.dimen.l1)
|
||||
binding.settingsList.addVerticalPadding(
|
||||
0,
|
||||
l1
|
||||
)
|
||||
binding.settingsList.addSimpleItemDecoration(
|
||||
left = l1,
|
||||
top = l_50,
|
||||
right = l1,
|
||||
bottom = l_50,
|
||||
)
|
||||
binding.settingsList.fixEdgeEffect()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -6,9 +6,9 @@ import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||
import com.topjohnwu.magisk.databinding.FragmentSuperuserMd2Binding
|
||||
import com.topjohnwu.magisk.di.viewModel
|
||||
import com.topjohnwu.magisk.ktx.addSimpleItemDecoration
|
||||
import com.topjohnwu.magisk.ktx.addVerticalPadding
|
||||
import com.topjohnwu.magisk.ktx.fixEdgeEffect
|
||||
import rikka.recyclerview.addEdgeSpacing
|
||||
import rikka.recyclerview.addItemSpacing
|
||||
import rikka.recyclerview.fixEdgeEffect
|
||||
|
||||
class SuperuserFragment : BaseUIFragment<SuperuserViewModel, FragmentSuperuserMd2Binding>() {
|
||||
|
||||
@ -23,20 +23,11 @@ class SuperuserFragment : BaseUIFragment<SuperuserViewModel, FragmentSuperuserMd
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val resource = requireContext().resources
|
||||
val l_50 = resource.getDimensionPixelSize(R.dimen.l_50)
|
||||
val l1 = resource.getDimensionPixelSize(R.dimen.l1)
|
||||
binding.superuserList.addVerticalPadding(
|
||||
l_50,
|
||||
l1
|
||||
)
|
||||
binding.superuserList.addSimpleItemDecoration(
|
||||
left = l1,
|
||||
top = l_50,
|
||||
right = l1,
|
||||
bottom = l_50,
|
||||
)
|
||||
binding.superuserList.fixEdgeEffect()
|
||||
binding.superuserList.apply {
|
||||
addEdgeSpacing(top = R.dimen.l_50, bottom = R.dimen.l1)
|
||||
addItemSpacing(R.dimen.l1, R.dimen.l_50, R.dimen.l1)
|
||||
fixEdgeEffect()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPreBind(binding: FragmentSuperuserMd2Binding) {}
|
||||
|
@ -19,34 +19,6 @@
|
||||
|
||||
<!--endregion-->
|
||||
|
||||
<declare-styleable name="WindowInsetsHelper">
|
||||
<attr name="edgeToEdge" format="boolean"/>
|
||||
<attr name="fitsSystemWindowsInsets" format="flags">
|
||||
<flag name="top" value="0x30" />
|
||||
<flag name="bottom" value="0x50" />
|
||||
<flag name="left" value="0x03" />
|
||||
<flag name="right" value="0x05" />
|
||||
<flag name="start" value="0x00800003" />
|
||||
<flag name="end" value="0x00800005" />
|
||||
</attr>
|
||||
<attr name="consumeSystemWindowsInsets" format="flags">
|
||||
<flag name="top" value="0x30" />
|
||||
<flag name="bottom" value="0x50" />
|
||||
<flag name="left" value="0x03" />
|
||||
<flag name="right" value="0x05" />
|
||||
<flag name="start" value="0x00800003" />
|
||||
<flag name="end" value="0x00800005" />
|
||||
</attr>
|
||||
<attr name="layout_fitsSystemWindowsInsets" format="flags">
|
||||
<flag name="top" value="0x30" />
|
||||
<flag name="bottom" value="0x50" />
|
||||
<flag name="left" value="0x03" />
|
||||
<flag name="right" value="0x05" />
|
||||
<flag name="start" value="0x00800003" />
|
||||
<flag name="end" value="0x00800005" />
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ConcealableView">
|
||||
<attr name="state_hidden" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
@ -2,7 +2,4 @@
|
||||
<resources>
|
||||
<item name="recyclerScrollListener" type="id" />
|
||||
<item name="coroutineScope" type="id" />
|
||||
|
||||
<item name="tag_rikka_material_WindowInsetsHelper" type="id"/>
|
||||
<item name="tag_rikka_recyclerView_OverScrollIfContentScrollsListener" type="id"/>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user