From 383192784db23e4053c1ebee21725e09c8ec4f94 Mon Sep 17 00:00:00 2001 From: RikkaW Date: Fri, 3 Sep 2021 18:38:02 +0800 Subject: [PATCH] Use standard BottomNav & Remove hide on scroll for AppBar and BottomNav --- .../com/topjohnwu/magisk/ui/MainActivity.kt | 71 +++++---- .../utils/HideBottomViewOnScrollBehavior.kt | 127 ---------------- .../utils/HideTopViewOnScrollBehavior.kt | 143 ------------------ app/src/main/res/layout/activity_main_md2.xml | 56 ++----- app/src/main/res/layout/fragment_log_md2.xml | 2 +- .../main/res/layout/fragment_module_md2.xml | 7 +- .../res/layout/fragment_superuser_md2.xml | 1 + .../main/res/layout/include_log_magisk.xml | 1 + 8 files changed, 59 insertions(+), 349 deletions(-) delete mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/HideBottomViewOnScrollBehavior.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/HideTopViewOnScrollBehavior.kt 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 227a60659..4601b1bcd 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt @@ -1,15 +1,19 @@ package com.topjohnwu.magisk.ui +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.TimeInterpolator import android.content.Intent import android.content.pm.ApplicationInfo import android.os.Bundle import android.view.MenuItem import android.view.View -import android.view.ViewTreeObserver import android.view.WindowManager -import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.content.pm.ShortcutManagerCompat 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 com.topjohnwu.magisk.MainDirections import com.topjohnwu.magisk.R @@ -21,7 +25,6 @@ import com.topjohnwu.magisk.databinding.ActivityMainMd2Binding import com.topjohnwu.magisk.di.viewModel import com.topjohnwu.magisk.ktx.startAnimations import com.topjohnwu.magisk.ui.home.HomeFragmentDirections -import com.topjohnwu.magisk.utils.HideableBehavior import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.Shortcuts @@ -119,40 +122,44 @@ open class MainActivity : BaseUIActivity( @Suppress("UNCHECKED_CAST") internal fun requestNavigationHidden(hide: Boolean = true) { - val topView = binding.mainToolbarWrapper - val bottomView = binding.mainBottomBar + val bottomView = binding.mainNavigation - if (!binding.mainBottomBar.isAttachedToWindow) { - binding.mainBottomBar.viewTreeObserver.addOnWindowAttachListener(object : - ViewTreeObserver.OnWindowAttachListener { + // A copy of HideBottomViewOnScrollBehavior's animation - init { - val listener = - binding.mainBottomBar.tag as? ViewTreeObserver.OnWindowAttachListener - if (listener != null) { - binding.mainBottomBar.viewTreeObserver.removeOnWindowAttachListener(listener) - } - binding.mainBottomBar.tag = this - } - - override fun onWindowAttached() { - requestNavigationHidden(hide) - } - - override fun onWindowDetached() { - } - }) - return + fun animateTranslationY( + view: View, targetY: Int, duration: Long, interpolator: TimeInterpolator + ) { + view.tag = view + .animate() + .translationY(targetY.toFloat()) + .setInterpolator(interpolator) + .setDuration(duration) + .setListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + view.tag = null + } + }) } - val topParams = topView.layoutParams as? CoordinatorLayout.LayoutParams - val bottomParams = bottomView.layoutParams as? CoordinatorLayout.LayoutParams + (bottomView.tag as? Animator)?.cancel() + bottomView.clearAnimation() - val topBehavior = topParams?.behavior as? HideableBehavior - val bottomBehavior = bottomParams?.behavior as? HideableBehavior - - topBehavior?.setHidden(topView, hide = false, lockState = false) - bottomBehavior?.setHidden(bottomView, hide, hide) + if (hide) { + animateTranslationY( + bottomView, + bottomView.measuredHeight, + 175L, + FastOutLinearInInterpolator() + ) + } else { + animateTranslationY( + bottomView, + 0, + 225L, + LinearOutSlowInInterpolator() + ) + } } fun invalidateToolbar() { diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/HideBottomViewOnScrollBehavior.kt b/app/src/main/java/com/topjohnwu/magisk/utils/HideBottomViewOnScrollBehavior.kt deleted file mode 100644 index b2b76e23a..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/HideBottomViewOnScrollBehavior.kt +++ /dev/null @@ -1,127 +0,0 @@ -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(context: Context, attrs: AttributeSet) : - HideBottomViewOnScrollBehavior(), HideableBehavior { - - 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 { - 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() - -} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/HideTopViewOnScrollBehavior.kt b/app/src/main/java/com/topjohnwu/magisk/utils/HideTopViewOnScrollBehavior.kt deleted file mode 100644 index 637431fb6..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/HideTopViewOnScrollBehavior.kt +++ /dev/null @@ -1,143 +0,0 @@ -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(context: Context, attrs: AttributeSet) : - CoordinatorLayout.Behavior(), HideableBehavior { - - 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 } -} diff --git a/app/src/main/res/layout/activity_main_md2.xml b/app/src/main/res/layout/activity_main_md2.xml index 8c5136263..c76f08bce 100644 --- a/app/src/main/res/layout/activity_main_md2.xml +++ b/app/src/main/res/layout/activity_main_md2.xml @@ -35,7 +35,6 @@ style="@style/WidgetFoundation.Appbar" android:layout_width="match_parent" android:layout_height="wrap_content" - app:layout_behavior="com.topjohnwu.magisk.utils.HideTopViewOnScrollBehavior" app:fitsSystemWindowsInsets="top"> - - - - - - + android:paddingBottom="0dp" + app:fitsSystemWindowsInsets="start|end|bottom" + 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="labeled" + app:menu="@menu/menu_bottom_nav" /> diff --git a/app/src/main/res/layout/fragment_log_md2.xml b/app/src/main/res/layout/fragment_log_md2.xml index d289c8074..eb6244e53 100644 --- a/app/src/main/res/layout/fragment_log_md2.xml +++ b/app/src/main/res/layout/fragment_log_md2.xml @@ -29,7 +29,7 @@ android:layout_gravity="bottom|end" android:layout_marginStart="@dimen/l1" android:layout_marginEnd="@dimen/l1" - android:layout_marginBottom="@dimen/l1" + android:layout_marginBottom="72dp" app:layout_fitsSystemWindowsInsets="bottom" app:backgroundTint="?colorSurfaceSurfaceVariant" app:srcCompat="@drawable/ic_folder_list" diff --git a/app/src/main/res/layout/fragment_module_md2.xml b/app/src/main/res/layout/fragment_module_md2.xml index e71941ff1..0de58b86e 100644 --- a/app/src/main/res/layout/fragment_module_md2.xml +++ b/app/src/main/res/layout/fragment_module_md2.xml @@ -39,6 +39,7 @@ android:clipToPadding="false" android:orientation="vertical" android:paddingTop="@dimen/internal_action_bar_size" + android:paddingBottom="56dp" app:fitsSystemWindowsInsets="top|bottom" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" tools:listitem="@layout/item_module_md2" /> @@ -50,9 +51,9 @@ android:layout_gravity="bottom|end" android:layout_marginStart="@dimen/l1" android:layout_marginEnd="@dimen/l1" - android:layout_marginBottom="@dimen/l1" - app:layout_fitsSystemWindowsInsets="bottom" + android:layout_marginBottom="72dp" app:backgroundTint="?colorSurfaceSurfaceVariant" + app:layout_fitsSystemWindowsInsets="bottom" app:srcCompat="@drawable/ic_search_md2" app:tint="?colorPrimary" tools:layout_marginBottom="64dp" /> @@ -60,11 +61,11 @@ diff --git a/app/src/main/res/layout/include_log_magisk.xml b/app/src/main/res/layout/include_log_magisk.xml index 1b786a6c1..bb377f8e8 100644 --- a/app/src/main/res/layout/include_log_magisk.xml +++ b/app/src/main/res/layout/include_log_magisk.xml @@ -34,6 +34,7 @@ android:textAppearance="@style/AppearanceFoundation.Caption" android:textSize="10sp" android:paddingTop="@dimen/internal_action_bar_size" + android:paddingBottom="120dp" app:layout_fitsSystemWindowsInsets="top|bottom" tools:text="@tools:sample/lorem/random" />