Commit 38319278 authored by RikkaW's avatar RikkaW Committed by John Wu

Use standard BottomNav & Remove hide on scroll for AppBar and BottomNav

parent 605189bc
package com.topjohnwu.magisk.ui package com.topjohnwu.magisk.ui
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.TimeInterpolator
import android.content.Intent import android.content.Intent
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewTreeObserver
import android.view.WindowManager import android.view.WindowManager
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.view.forEach import androidx.core.view.forEach
import androidx.core.view.isGone
import androidx.interpolator.view.animation.FastOutLinearInInterpolator
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
import androidx.navigation.NavDirections import androidx.navigation.NavDirections
import com.topjohnwu.magisk.MainDirections import com.topjohnwu.magisk.MainDirections
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
...@@ -21,7 +25,6 @@ import com.topjohnwu.magisk.databinding.ActivityMainMd2Binding ...@@ -21,7 +25,6 @@ import com.topjohnwu.magisk.databinding.ActivityMainMd2Binding
import com.topjohnwu.magisk.di.viewModel import com.topjohnwu.magisk.di.viewModel
import com.topjohnwu.magisk.ktx.startAnimations import com.topjohnwu.magisk.ktx.startAnimations
import com.topjohnwu.magisk.ui.home.HomeFragmentDirections import com.topjohnwu.magisk.ui.home.HomeFragmentDirections
import com.topjohnwu.magisk.utils.HideableBehavior
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.magisk.view.Shortcuts
...@@ -119,40 +122,44 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>( ...@@ -119,40 +122,44 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
internal fun requestNavigationHidden(hide: Boolean = true) { internal fun requestNavigationHidden(hide: Boolean = true) {
val topView = binding.mainToolbarWrapper val bottomView = binding.mainNavigation
val bottomView = binding.mainBottomBar
// A copy of HideBottomViewOnScrollBehavior's animation
if (!binding.mainBottomBar.isAttachedToWindow) {
binding.mainBottomBar.viewTreeObserver.addOnWindowAttachListener(object : fun animateTranslationY(
ViewTreeObserver.OnWindowAttachListener { view: View, targetY: Int, duration: Long, interpolator: TimeInterpolator
) {
init { view.tag = view
val listener = .animate()
binding.mainBottomBar.tag as? ViewTreeObserver.OnWindowAttachListener .translationY(targetY.toFloat())
if (listener != null) { .setInterpolator(interpolator)
binding.mainBottomBar.viewTreeObserver.removeOnWindowAttachListener(listener) .setDuration(duration)
} .setListener(
binding.mainBottomBar.tag = this object : AnimatorListenerAdapter() {
} override fun onAnimationEnd(animation: Animator) {
view.tag = null
override fun onWindowAttached() { }
requestNavigationHidden(hide) })
} }
override fun onWindowDetached() { (bottomView.tag as? Animator)?.cancel()
} bottomView.clearAnimation()
})
return if (hide) {
animateTranslationY(
bottomView,
bottomView.measuredHeight,
175L,
FastOutLinearInInterpolator()
)
} else {
animateTranslationY(
bottomView,
0,
225L,
LinearOutSlowInInterpolator()
)
} }
val topParams = topView.layoutParams as? CoordinatorLayout.LayoutParams
val bottomParams = bottomView.layoutParams as? CoordinatorLayout.LayoutParams
val topBehavior = topParams?.behavior as? HideableBehavior<View>
val bottomBehavior = bottomParams?.behavior as? HideableBehavior<View>
topBehavior?.setHidden(topView, hide = false, lockState = false)
bottomBehavior?.setHidden(bottomView, hide, hide)
} }
fun invalidateToolbar() { fun invalidateToolbar() {
......
package com.topjohnwu.magisk.utils
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import com.google.android.material.behavior.HideBottomViewOnScrollBehavior
import com.google.android.material.snackbar.Snackbar
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Info
import kotlin.math.roundToInt
class HideBottomViewOnScrollBehavior<V : View>(context: Context, attrs: AttributeSet) :
HideBottomViewOnScrollBehavior<V>(), HideableBehavior<V> {
private var lockState: Boolean = false
private var isLaidOut = false
override fun layoutDependsOn(parent: CoordinatorLayout, child: V, dependency: View) =
super.layoutDependsOn(parent, child, dependency) or (dependency is Snackbar.SnackbarLayout)
override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
isLaidOut = true
return super.onLayoutChild(parent, child, layoutDirection)
}
override fun onDependentViewChanged(
parent: CoordinatorLayout,
child: V,
dependency: View
) = when (dependency) {
is Snackbar.SnackbarLayout -> onDependentViewChanged(parent, child, dependency)
else -> super.onDependentViewChanged(parent, child, dependency)
}
override fun onDependentViewRemoved(
parent: CoordinatorLayout,
child: V,
dependency: View
) = when (dependency) {
is Snackbar.SnackbarLayout -> onDependentViewRemoved(parent, child, dependency)
else -> super.onDependentViewRemoved(parent, child, dependency)
}
//---
private fun onDependentViewChanged(
parent: CoordinatorLayout,
child: V,
dependency: Snackbar.SnackbarLayout
): Boolean {
val viewMargin = (child.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin
val additionalMargin = dependency.resources.getDimension(R.dimen.l1).roundToInt()
val translation = dependency.height + additionalMargin
dependency.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = viewMargin
}
// checks whether the navigation is not hidden via scroll
if (child.isVisible && child.translationY <= 0) {
child.translationY(-translation.toFloat())
}
return false
}
private fun onDependentViewRemoved(
parent: CoordinatorLayout,
child: V,
dependency: Snackbar.SnackbarLayout
) {
// checks whether the navigation is not hidden via scroll
if (child.isVisible && child.translationY <= 0) {
child.translationY(0f)
}
}
//---
override fun slideUp(child: V) {
if (lockState) return
super.slideUp(child)
}
override fun slideDown(child: V) {
if (lockState) return
super.slideDown(child)
}
override fun setHidden(
view: V,
hide: Boolean,
lockState: Boolean
) {
if (!lockState) {
this.lockState = lockState
}
if (hide || !Info.env.isActive) {
// view is not laid out and drawn yet properly, so animation will not be attached
// hence we just simply hide the view
if (!isLaidOut) {
view.isGone = true
} else {
slideDown(view)
}
} else {
view.isVisible = Info.env.isActive
slideUp(view)
}
this.lockState = lockState
}
//---
private fun View.translationY(destination: Float) = animate()
.translationY(destination)
.setInterpolator(FastOutSlowInInterpolator())
.start()
}
package com.topjohnwu.magisk.utils
import android.animation.TimeInterpolator
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.view.ViewPropertyAnimator
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat
import com.google.android.material.animation.AnimationUtils
class HideTopViewOnScrollBehavior<V : View>(context: Context, attrs: AttributeSet) :
CoordinatorLayout.Behavior<V>(), HideableBehavior<V> {
companion object {
private const val STATE_SCROLLED_DOWN = 1
private const val STATE_SCROLLED_UP = 2
private const val ENTER_ANIMATION_DURATION = 225
private const val EXIT_ANIMATION_DURATION = 175
}
private var height = 0
private var currentState = STATE_SCROLLED_UP
private var currentAnimator: ViewPropertyAnimator? = null
private var lockState: Boolean = false
override fun onLayoutChild(
parent: CoordinatorLayout,
child: V,
layoutDirection: Int
): Boolean {
val paramsCompat = child.layoutParams as ViewGroup.MarginLayoutParams
height = child.measuredHeight + paramsCompat.topMargin
return super.onLayoutChild(parent, child, layoutDirection)
}
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: V,
directTargetChild: View,
target: View,
nestedScrollAxes: Int,
type: Int
) = nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
override fun onNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: V,
target: View,
dxConsumed: Int,
dyConsumed: Int,
dxUnconsumed: Int,
dyUnconsumed: Int,
type: Int,
consumed: IntArray
) {
// when initiating scroll while the view is at the bottom or at the top and pushing it
// further, the parent will report consumption of 0
if (dyConsumed == 0) return
setHidden(child, dyConsumed > 0, false)
}
@Suppress("UNCHECKED_CAST")
override fun setHidden(
view: V,
hide: Boolean,
lockState: Boolean
) {
if (!lockState) {
this.lockState = lockState
}
if (hide) {
slideUp(view)
} else {
slideDown(view)
}
this.lockState = lockState
}
/**
* Perform an animation that will slide the child from it's current position to be totally on the
* screen.
*/
private fun slideDown(child: V) {
if (currentState == STATE_SCROLLED_UP || lockState) {
return
}
currentAnimator?.let {
it.cancel()
child.clearAnimation()
}
currentState = STATE_SCROLLED_UP
animateChildTo(
child,
0,
ENTER_ANIMATION_DURATION.toLong(),
AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR
)
}
/**
* Perform an animation that will slide the child from it's current position to be totally off the
* screen.
*/
private fun slideUp(child: V) {
if (currentState == STATE_SCROLLED_DOWN || lockState) {
return
}
currentAnimator?.let {
it.cancel()
child.clearAnimation()
}
currentState = STATE_SCROLLED_DOWN
animateChildTo(
child,
-height,
EXIT_ANIMATION_DURATION.toLong(),
AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR
)
}
private fun animateChildTo(
child: V,
targetY: Int,
duration: Long,
interpolator: TimeInterpolator
) = child
.animate()
.translationY(targetY.toFloat())
.setInterpolator(interpolator)
.setDuration(duration)
.withEndAction { currentAnimator = null }
.let { currentAnimator = it }
}
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
style="@style/WidgetFoundation.Appbar" style="@style/WidgetFoundation.Appbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_behavior="com.topjohnwu.magisk.utils.HideTopViewOnScrollBehavior"
app:fitsSystemWindowsInsets="top"> app:fitsSystemWindowsInsets="top">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
...@@ -49,51 +48,22 @@ ...@@ -49,51 +48,22 @@
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<!-- <com.google.android.material.bottomnavigation.BottomNavigationView
todo(diareuse) this stupid-ass bottom menu doesn't fit with the fab-s, so either of those android:id="@+id/main_navigation"
things need to be removed, probably the fab-s. android:layout_width="match_parent"
Using the BottomAppBar is not viable as it looks terrible with the blank space in the
middle and / or with the "animation" which BottomNavigationView performs when it's
reinflating its menu items. This bullshit cockblocks the entire feature. Terrific.
Possibility is then to keep the middle fab and assign it some other functionality on all
screens. Which -ha-ha- good luck finding something on superuser and homepage...
-->
<com.google.android.material.card.MaterialCardView
android:id="@+id/main_bottom_bar"
style="@style/WidgetFoundation.Card.Elevated"
android:layout_width="224dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal" android:layout_gravity="bottom|center_horizontal"
android:layout_marginStart="@dimen/l1"
android:layout_marginEnd="@dimen/l1"
android:layout_marginBottom="@dimen/l1"
android:fitsSystemWindows="false" android:fitsSystemWindows="false"
app:layout_fitsSystemWindowsInsets="bottom" android:paddingBottom="0dp"
app:layout_behavior="com.topjohnwu.magisk.utils.HideBottomViewOnScrollBehavior" app:fitsSystemWindowsInsets="start|end|bottom"
tools:layout_marginBottom="64dp"> app:itemHorizontalTranslationEnabled="false"
app:itemIconTint="@color/color_menu_tint"
<com.google.android.material.bottomnavigation.BottomNavigationView app:itemRippleColor="?colorPrimary"
android:id="@+id/main_navigation" app:itemTextAppearanceActive="@style/AppearanceFoundation.Tiny.Bold"
android:layout_width="match_parent" app:itemTextAppearanceInactive="@style/AppearanceFoundation.Tiny.Bold"
android:layout_height="wrap_content" app:itemTextColor="@color/color_menu_tint"
android:background="@android:color/transparent" app:labelVisibilityMode="labeled"
android:textStyle="bold" app:menu="@menu/menu_bottom_nav" />
android:fitsSystemWindows="false"
android:paddingBottom="0dp"
app:fitsSystemWindowsInsets="start|end"
app:elevation="0dp"
app:itemHorizontalTranslationEnabled="false"
app:itemIconTint="@color/color_menu_tint"
app:itemRippleColor="?colorPrimary"
app:itemTextAppearanceActive="@style/AppearanceFoundation.Tiny.Bold"
app:itemTextAppearanceInactive="@style/AppearanceFoundation.Tiny.Bold"
app:itemTextColor="@color/color_menu_tint"
app:labelVisibilityMode="unlabeled"
app:menu="@menu/menu_bottom_nav" />
</com.google.android.material.card.MaterialCardView>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_marginStart="@dimen/l1" android:layout_marginStart="@dimen/l1"
android:layout_marginEnd="@dimen/l1" android:layout_marginEnd="@dimen/l1"
android:layout_marginBottom="@dimen/l1" android:layout_marginBottom="72dp"
app:layout_fitsSystemWindowsInsets="bottom" app:layout_fitsSystemWindowsInsets="bottom"
app:backgroundTint="?colorSurfaceSurfaceVariant" app:backgroundTint="?colorSurfaceSurfaceVariant"
app:srcCompat="@drawable/ic_folder_list" app:srcCompat="@drawable/ic_folder_list"
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical"
android:paddingTop="@dimen/internal_action_bar_size" android:paddingTop="@dimen/internal_action_bar_size"
android:paddingBottom="56dp"
app:fitsSystemWindowsInsets="top|bottom" app:fitsSystemWindowsInsets="top|bottom"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_module_md2" /> tools:listitem="@layout/item_module_md2" />
...@@ -50,9 +51,9 @@ ...@@ -50,9 +51,9 @@
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_marginStart="@dimen/l1" android:layout_marginStart="@dimen/l1"
android:layout_marginEnd="@dimen/l1" android:layout_marginEnd="@dimen/l1"
android:layout_marginBottom="@dimen/l1" android:layout_marginBottom="72dp"
app:layout_fitsSystemWindowsInsets="bottom"
app:backgroundTint="?colorSurfaceSurfaceVariant" app:backgroundTint="?colorSurfaceSurfaceVariant"
app:layout_fitsSystemWindowsInsets="bottom"
app:srcCompat="@drawable/ic_search_md2" app:srcCompat="@drawable/ic_search_md2"
app:tint="?colorPrimary" app:tint="?colorPrimary"
tools:layout_marginBottom="64dp" /> tools:layout_marginBottom="64dp" />
...@@ -60,11 +61,11 @@ ...@@ -60,11 +61,11 @@
<com.google.android.material.circularreveal.cardview.CircularRevealCardView <com.google.android.material.circularreveal.cardview.CircularRevealCardView
android:id="@+id/module_filter" android:id="@+id/module_filter"
style="@style/WidgetFoundation.Card" style="@style/WidgetFoundation.Card"
app:cardBackgroundColor="?colorSurface"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:visibility="invisible" android:visibility="invisible"
app:cardBackgroundColor="?colorSurface"
app:cardCornerRadius="0dp"> app:cardCornerRadius="0dp">
<include <include
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical"
android:paddingTop="@dimen/internal_action_bar_size" android:paddingTop="@dimen/internal_action_bar_size"
android:paddingBottom="56dp"
app:fitsSystemWindowsInsets="top|bottom" app:fitsSystemWindowsInsets="top|bottom"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_policy_md2" /> tools:listitem="@layout/item_policy_md2" />
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
android:textAppearance="@style/AppearanceFoundation.Caption" android:textAppearance="@style/AppearanceFoundation.Caption"
android:textSize="10sp" android:textSize="10sp"
android:paddingTop="@dimen/internal_action_bar_size" android:paddingTop="@dimen/internal_action_bar_size"
android:paddingBottom="120dp"
app:layout_fitsSystemWindowsInsets="top|bottom" app:layout_fitsSystemWindowsInsets="top|bottom"
tools:text="@tools:sample/lorem/random" /> tools:text="@tools:sample/lorem/random" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment