diff --git a/app/src/main/java/com/topjohnwu/magisk/arch/BaseUIActivity.kt b/app/src/main/java/com/topjohnwu/magisk/arch/BaseUIActivity.kt index 13eab5e51..f7a84a279 100644 --- a/app/src/main/java/com/topjohnwu/magisk/arch/BaseUIActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/arch/BaseUIActivity.kt @@ -97,8 +97,6 @@ abstract class BaseUIActivity : it.setVariable(BR.viewModel, viewModel) it.lifecycleOwner = this } - - ensureInsets() } fun setAccessibilityDelegate(delegate: View.AccessibilityDelegate?) { diff --git a/app/src/main/java/com/topjohnwu/magisk/arch/BaseUIComponent.kt b/app/src/main/java/com/topjohnwu/magisk/arch/BaseUIComponent.kt index 90f2ebeb3..741ddaa1a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/arch/BaseUIComponent.kt +++ b/app/src/main/java/com/topjohnwu/magisk/arch/BaseUIComponent.kt @@ -1,12 +1,9 @@ package com.topjohnwu.magisk.arch import android.view.View -import androidx.core.graphics.Insets -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat import androidx.lifecycle.LifecycleOwner -interface BaseUIComponent: LifecycleOwner { +interface BaseUIComponent : LifecycleOwner { val viewRoot: View val viewModel: VM @@ -17,47 +14,8 @@ interface BaseUIComponent: LifecycleOwner { } } - fun consumeSystemWindowInsets(insets: Insets): Insets? = null - /** * Called for all [ViewEvent]s published by associated viewModel. */ fun onEventDispatched(event: ViewEvent) {} - - fun ensureInsets() { - ViewCompat.setOnApplyWindowInsetsListener(viewRoot) { _, insets -> - insets.asInsets() - .also { viewModel.insets = it } - .let { consumeSystemWindowInsets(it) } - ?.subtractBy(insets) ?: insets - } - if (ViewCompat.isAttachedToWindow(viewRoot)) { - ViewCompat.requestApplyInsets(viewRoot) - } else { - viewRoot.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { - override fun onViewDetachedFromWindow(v: View) = Unit - override fun onViewAttachedToWindow(v: View) { - ViewCompat.requestApplyInsets(v) - } - }) - } - } - - private fun WindowInsetsCompat.asInsets() = Insets.of( - systemWindowInsetLeft, - systemWindowInsetTop, - systemWindowInsetRight, - systemWindowInsetBottom - ) - - private fun Insets.subtractBy(insets: WindowInsetsCompat) = - WindowInsetsCompat.Builder(insets).setSystemWindowInsets( - Insets.of( - insets.systemWindowInsetLeft - left, - insets.systemWindowInsetTop - top, - insets.systemWindowInsetRight - right, - insets.systemWindowInsetBottom - bottom - ) - ).build() - } diff --git a/app/src/main/java/com/topjohnwu/magisk/arch/BaseUIFragment.kt b/app/src/main/java/com/topjohnwu/magisk/arch/BaseUIFragment.kt index 3ffe64431..318ab143f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/arch/BaseUIFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/arch/BaseUIFragment.kt @@ -24,8 +24,6 @@ abstract class BaseUIFragment : override val viewRoot: View get() = binding.root private val navigation get() = activity.navigation - override fun consumeSystemWindowInsets(insets: Insets) = insets - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) startObserveEvents() @@ -65,7 +63,6 @@ abstract class BaseUIFragment : return true } }) - ensureInsets() } override fun onResume() { diff --git a/app/src/main/java/com/topjohnwu/magisk/ktx/RecyclerView.kt b/app/src/main/java/com/topjohnwu/magisk/ktx/RecyclerView.kt new file mode 100644 index 000000000..c38632d48 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/ktx/RecyclerView.kt @@ -0,0 +1,178 @@ +@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.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) { + if (parent.adapter == null) { + return + } + val position = parent.getChildAdapterPosition(view) + val count = parent.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 + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt index 0457834a7..f499ae854 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt @@ -39,14 +39,6 @@ open class MainActivity : BaseUIActivity( override val viewModel by viewModel() override val navHost: Int = R.id.main_nav_host - //This temporarily fixes unwanted feature of BottomNavigationView - where the view applies - //padding on itself given insets are not consumed beforehand. Unfortunately the listener - //implementation doesn't favor us against the design library, so on re-create it's often given - //upper hand. - private val navObserver = ViewTreeObserver.OnGlobalLayoutListener { - binding.mainNavigation.setPadding(0) - } - private var isRootFragment = true override fun onCreate(savedInstanceState: Bundle?) { @@ -100,8 +92,6 @@ open class MainActivity : BaseUIActivity( (currentFragment as? ReselectionTarget)?.onReselected() } - binding.mainNavigation.viewTreeObserver.addOnGlobalLayoutListener(navObserver) - val section = if (intent.action == ACTION_APPLICATION_PREFERENCES) Const.Nav.SETTINGS else intent.getStringExtra(Const.Key.OPEN_SECTION) getScreen(section)?.navigate() @@ -121,11 +111,6 @@ open class MainActivity : BaseUIActivity( } } - override fun onDestroy() { - binding.mainNavigation.viewTreeObserver.removeOnGlobalLayoutListener(navObserver) - super.onDestroy() - } - override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { android.R.id.home -> onBackPressed() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideFragment.kt index 119bd803f..de7649844 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideFragment.kt @@ -12,6 +12,9 @@ import androidx.recyclerview.widget.RecyclerView import com.topjohnwu.magisk.R import com.topjohnwu.magisk.arch.BaseUIFragment import com.topjohnwu.magisk.databinding.FragmentHideMd2Binding +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 com.topjohnwu.magisk.utils.MotionRevealHelper import org.koin.androidx.viewmodel.ext.android.viewModel @@ -49,6 +52,21 @@ class HideFragment : BaseUIFragment() { } }) + val resource = requireContext().resources + val l_50 = resource.getDimensionPixelSize(R.dimen.l_50) + val l1 = resource.getDimensionPixelSize(R.dimen.l1) + binding.hideContent.addVerticalPadding( + l_50, + l1 + resource.getDimensionPixelSize(R.dimen.internal_action_bar_size) + ) + binding.hideContent.addSimpleItemDecoration( + left = l1, + top = l_50, + right = l1, + bottom = l_50, + ) + binding.hideContent.fixEdgeEffect() + val lama = binding.hideContent.layoutManager ?: return lama.isAutoMeasureEnabled = false } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/inflater/LayoutInflaterFactory.kt b/app/src/main/java/com/topjohnwu/magisk/ui/inflater/LayoutInflaterFactory.kt index 686085dd6..6585b2ae3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/inflater/LayoutInflaterFactory.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/inflater/LayoutInflaterFactory.kt @@ -1,6 +1,7 @@ package com.topjohnwu.magisk.ui.inflater import android.content.Context +import android.os.Build import android.util.AttributeSet import android.view.InflateException import android.view.LayoutInflater @@ -24,6 +25,10 @@ open class LayoutInflaterFactory(private val delegate: AppCompatDelegate) : Layo open fun onViewCreated(view: View?, parent: View?, name: String, context: Context, attrs: AttributeSet) { if (view == null) return + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + WindowInsetsHelper.attach(view, attrs) + } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/inflater/WindowInsetsHelper.kt b/app/src/main/java/com/topjohnwu/magisk/ui/inflater/WindowInsetsHelper.kt new file mode 100644 index 000000000..286be439e --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/ui/inflater/WindowInsetsHelper.kt @@ -0,0 +1,284 @@ +@file:Suppress("unused") + +package com.topjohnwu.magisk.ui.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 = (insets: Insets, left: Boolean, top: Boolean, right: Boolean, bottom: Boolean) -> T + +private class ApplyInsets(private val out: Rect) : ApplyInsetsCallback { + + 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 { + + 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 applyInsets(insets: Insets, fit: Int, callback: ApplyInsetsCallback): 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) +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt index 0197c7944..a7c20ed7b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt @@ -9,6 +9,9 @@ import androidx.core.view.isVisible import com.topjohnwu.magisk.R import com.topjohnwu.magisk.arch.BaseUIFragment import com.topjohnwu.magisk.databinding.FragmentLogMd2Binding +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 org.koin.androidx.viewmodel.ext.android.viewModel @@ -42,6 +45,21 @@ class LogFragment : BaseUIFragment() { binding.logFilterToggle.setOnClickListener { 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() } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt index d733fdaae..27d804504 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt @@ -13,6 +13,9 @@ import com.topjohnwu.magisk.arch.ReselectionTarget import com.topjohnwu.magisk.arch.ViewEvent import com.topjohnwu.magisk.core.download.BaseDownloader import com.topjohnwu.magisk.databinding.FragmentModuleMd2Binding +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 com.topjohnwu.magisk.ui.MainActivity import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener @@ -62,6 +65,37 @@ class ModuleFragment : BaseUIFragment 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 + resource.getDimensionPixelSize(R.dimen.internal_action_bar_size) + ) + addSimpleItemDecoration( + left = l1, + top = l_50, + right = l1, + bottom = l_50, + ) + fixEdgeEffect() + } + + binding.moduleFilterInclude.moduleFilterList.apply { + addVerticalPadding( + l_50, + l1 + resource.getDimensionPixelSize(R.dimen.internal_action_bar_size) + ) + addSimpleItemDecoration( + left = l1, + top = l_50, + right = l1, + bottom = l_50, + ) + fixEdgeEffect() + } } override fun onDestroyView() { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt index 707d55d80..7fc03db2b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt @@ -5,6 +5,9 @@ import android.view.View import com.topjohnwu.magisk.R import com.topjohnwu.magisk.arch.BaseUIFragment import com.topjohnwu.magisk.databinding.FragmentSettingsMd2Binding +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 org.koin.androidx.viewmodel.ext.android.viewModel @@ -24,6 +27,21 @@ class SettingsFragment : BaseUIFragment() { @@ -15,6 +20,25 @@ class SuperuserFragment : BaseUIFragment + app:fitsSystemWindowsInsets="top"> @@ -46,12 +45,13 @@ android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/l1" - android:layout_marginBottom="@{(int) @dimen/l1 + viewModel.insets.bottom}" + android:layout_marginBottom="@dimen/l1" android:onClick="@{() -> viewModel.restartPressed()}" android:text="@string/reboot" android:textAllCaps="false" android:textColor="?colorOnPrimary" android:textStyle="bold" + app:layout_fitsSystemWindowsInsets="bottom" app:backgroundTint="?colorPrimary" app:icon="@drawable/ic_restart" app:iconTint="?colorOnPrimary" /> diff --git a/app/src/main/res/layout/fragment_hide_md2.xml b/app/src/main/res/layout/fragment_hide_md2.xml index 50e6fbb14..82832b517 100644 --- a/app/src/main/res/layout/fragment_hide_md2.xml +++ b/app/src/main/res/layout/fragment_hide_md2.xml @@ -17,7 +17,6 @@ @@ -41,8 +38,9 @@ android:layout_gravity="bottom|end" android:layout_marginStart="@dimen/l1" android:layout_marginEnd="@dimen/l1" - android:layout_marginBottom="@{viewModel.insets.bottom + (int) @dimen/l1}" + android:layout_marginBottom="@dimen/l1" app:backgroundTint="?colorSurfaceSurfaceVariant" + app:layout_fitsSystemWindowsInsets="bottom" app:srcCompat="@drawable/ic_search_md2" app:tint="?colorPrimary" tools:layout_marginBottom="64dp" /> diff --git a/app/src/main/res/layout/fragment_home_md2.xml b/app/src/main/res/layout/fragment_home_md2.xml index 725530853..419870f24 100644 --- a/app/src/main/res/layout/fragment_home_md2.xml +++ b/app/src/main/res/layout/fragment_home_md2.xml @@ -22,8 +22,9 @@ android:layout_height="match_parent" android:clipToPadding="false" android:fillViewport="true" - android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}" - android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l3}" + android:paddingTop="@dimen/internal_action_bar_size" + android:paddingBottom="@dimen/l3" + app:fitsSystemWindowsInsets="top|bottom" tools:layout_marginTop="24dp"> + android:paddingTop="@dimen/l_50"> @@ -54,7 +50,8 @@ android:layout_gravity="bottom|end" android:layout_marginStart="@dimen/l1" android:layout_marginEnd="@dimen/l1" - android:layout_marginBottom="@{viewModel.insets.bottom + (int) @dimen/l1}" + android:layout_marginBottom="@dimen/l1" + app:layout_fitsSystemWindowsInsets="bottom" app:backgroundTint="?colorSurfaceSurfaceVariant" app:srcCompat="@drawable/ic_search_md2" app:tint="?colorPrimary" diff --git a/app/src/main/res/layout/fragment_safetynet_md2.xml b/app/src/main/res/layout/fragment_safetynet_md2.xml index 60163c385..f7f6b0b17 100644 --- a/app/src/main/res/layout/fragment_safetynet_md2.xml +++ b/app/src/main/res/layout/fragment_safetynet_md2.xml @@ -22,8 +22,8 @@ android:layout_height="match_parent" android:clipToPadding="false" android:fillViewport="true" - android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}" - android:paddingBottom="@{viewModel.insets.bottom}" + android:paddingTop="@dimen/internal_action_bar_size" + app:fitsSystemWindowsInsets="top|bottom" tools:paddingBottom="48dp" tools:paddingTop="24dp"> diff --git a/app/src/main/res/layout/fragment_settings_md2.xml b/app/src/main/res/layout/fragment_settings_md2.xml index 4f83edc5d..4150efe26 100644 --- a/app/src/main/res/layout/fragment_settings_md2.xml +++ b/app/src/main/res/layout/fragment_settings_md2.xml @@ -15,8 +15,6 @@ diff --git a/app/src/main/res/layout/fragment_theme_md2.xml b/app/src/main/res/layout/fragment_theme_md2.xml index f5f87f703..bf4babb35 100644 --- a/app/src/main/res/layout/fragment_theme_md2.xml +++ b/app/src/main/res/layout/fragment_theme_md2.xml @@ -1,5 +1,6 @@ - + @@ -17,15 +18,17 @@ android:clipToPadding="false" android:fillViewport="true" android:paddingStart="@dimen/l1" - android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size + (int) @dimen/l1}" + android:paddingTop="@dimen/internal_action_bar_size" android:paddingEnd="@dimen/l1" - android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l1}"> + android:paddingBottom="@dimen/l1" + app:fitsSystemWindowsInsets="top|bottom"> diff --git a/app/src/main/res/layout/include_log_magisk.xml b/app/src/main/res/layout/include_log_magisk.xml index 04b2e6dec..1b786a6c1 100644 --- a/app/src/main/res/layout/include_log_magisk.xml +++ b/app/src/main/res/layout/include_log_magisk.xml @@ -1,5 +1,6 @@ @@ -32,8 +33,8 @@ precomputedText="@{viewModel.consoleText}" android:textAppearance="@style/AppearanceFoundation.Caption" android:textSize="10sp" - android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}" - android:paddingBottom="@{viewModel.insets.bottom}" + android:paddingTop="@dimen/internal_action_bar_size" + app:layout_fitsSystemWindowsInsets="top|bottom" tools:text="@tools:sample/lorem/random" /> diff --git a/app/src/main/res/layout/include_log_superuser.xml b/app/src/main/res/layout/include_log_superuser.xml index a447326a4..a0cfb620d 100644 --- a/app/src/main/res/layout/include_log_superuser.xml +++ b/app/src/main/res/layout/include_log_superuser.xml @@ -16,16 +16,15 @@ android:layout_height="match_parent"> diff --git a/app/src/main/res/layout/include_module_filter.xml b/app/src/main/res/layout/include_module_filter.xml index d598208d3..c95dbe889 100644 --- a/app/src/main/res/layout/include_module_filter.xml +++ b/app/src/main/res/layout/include_module_filter.xml @@ -18,14 +18,13 @@ android:layout_height="match_parent" android:clipToPadding="false" android:orientation="vertical" - android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l1}" + android:paddingBottom="@dimen/l1" + app:fitsSystemWindowsInsets="bottom" tools:layout_gravity="bottom" tools:paddingBottom="64dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml index 398106f66..7be52e7fb 100644 --- a/app/src/main/res/values/ids.xml +++ b/app/src/main/res/values/ids.xml @@ -1,7 +1,8 @@ - - + + +