Animated insets for the fabs when keyboard shows

The reason I'm on android 11's sdk in the first place

Also gonna need a lot of checks because android just deprecated all the inset stuff for pre 11

Some of these changes actually optimzed the tab bar animations a lot
This commit is contained in:
Jays2Kings 2021-05-26 23:25:28 -04:00
parent 47c2f5f97f
commit 0fa3eef40e
6 changed files with 122 additions and 7 deletions

View File

@ -8,6 +8,7 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.util.TypedValue
@ -20,12 +21,16 @@ import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.ViewPropertyAnimator
import android.view.WindowInsets
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.recyclerview.widget.GridLayoutManager
@ -80,6 +85,7 @@ import eu.kanade.tachiyomi.util.moveCategories
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getBottomGestureInsets
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.isImeVisible
import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.view.activityBinding
import eu.kanade.tachiyomi.util.view.collapse
@ -218,6 +224,28 @@ class LibraryController(
)
}
val cb = object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
override fun onStart(
animation: WindowInsetsAnimationCompat,
bounds: WindowInsetsAnimationCompat.BoundsCompat
): WindowInsetsAnimationCompat.BoundsCompat {
updateHopperY()
return bounds
}
override fun onProgress(
insets: WindowInsetsCompat,
runningAnimations: List<WindowInsetsAnimationCompat>
): WindowInsetsCompat {
updateHopperY(insets.toWindowInsets())
return insets
}
override fun onEnd(animation: WindowInsetsAnimationCompat) {
updateHopperY()
}
}
private var scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
@ -536,6 +564,8 @@ class LibraryController(
}
setSwipeRefresh()
ViewCompat.setWindowInsetsAnimationCallback(view, cb)
if (selectedMangas.isNotEmpty()) {
createActionModeIfNeeded()
}
@ -691,16 +721,21 @@ class LibraryController(
}
}
fun updateHopperY() {
fun updateHopperY(windowInsets: WindowInsets? = null) {
val view = view ?: return
val insets = windowInsets ?: view.rootWindowInsets
val listOfYs = mutableListOf(
binding.filterBottomSheet.filterBottomSheet.y,
activityBinding?.bottomNav?.y ?: binding.filterBottomSheet.filterBottomSheet.y
)
val insetBottom = view.rootWindowInsets?.systemWindowInsetBottom ?: 0
val insetBottom = insets?.systemWindowInsetBottom ?: 0
if (!preferences.autohideHopper().get() || activityBinding?.bottomNav == null) {
listOfYs.add(view.height - (insetBottom).toFloat())
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && insets?.isImeVisible() == true) {
val insetKey = insets.getInsets(WindowInsets.Type.ime() or WindowInsets.Type.systemBars()).bottom
listOfYs.add(view.height - (insetKey).toFloat())
}
binding.categoryHopperFrame.y = -binding.categoryHopperFrame.height +
(listOfYs.minOrNull() ?: binding.filterBottomSheet.filterBottomSheet.y) +
hopperOffset +

View File

@ -767,11 +767,15 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
setFloatingToolbar(canShowFloatingToolbar(to))
val onRoot = router.backstackSize == 1
if (onRoot) {
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R && !isPush) {
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
}
binding.toolbar.navigationIcon = searchDrawable
binding.cardToolbar.navigationIcon = searchDrawable
} else {
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
}
binding.toolbar.navigationIcon = drawerArrow
binding.cardToolbar.navigationIcon = drawerArrow
}

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.source.browse
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
@ -35,6 +36,7 @@ import eu.kanade.tachiyomi.util.addOrRemoveToFavorites
import eu.kanade.tachiyomi.util.system.connectivityManager
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.view.applyBottomAnimatedInsets
import eu.kanade.tachiyomi.util.view.inflate
import eu.kanade.tachiyomi.util.view.scrollViewWith
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
@ -177,11 +179,14 @@ open class BrowseSourceController(bundle: Bundle) :
recycler,
true,
afterInsets = { insets ->
binding.fab.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = insets.systemWindowInsetBottom + 16.dpToPx
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
binding.fab.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = insets.systemWindowInsetBottom + 16.dpToPx
}
}
}
)
binding.fab.applyBottomAnimatedInsets(16.dpToPx)
recycler.addOnScrollListener(
object : RecyclerView.OnScrollListener() {

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.util.system
import android.os.Build
import android.view.WindowInsets
import androidx.annotation.RequiresApi
fun WindowInsets.getBottomGestureInsets(): Int {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) mandatorySystemGestureInsets.bottom
@ -20,3 +21,6 @@ fun WindowInsets.hasSideInsets() = systemWindowInsetLeft > 0 || systemWindowInse
fun WindowInsets.hasSideNavBar() =
(systemWindowInsetLeft > 0 || systemWindowInsetRight > 0) && !isBottomTappable() &&
systemWindowInsetBottom == 0
@RequiresApi(Build.VERSION_CODES.R)
fun WindowInsets.isImeVisible() = isVisible(WindowInsets.Type.ime())

View File

@ -113,7 +113,11 @@ fun Controller.liftAppbarWith(recycler: RecyclerView, padView: Boolean = false)
val headerHeight = insets.systemWindowInsetTop + appBarHeight
view.updatePaddingRelative(
top = headerHeight,
bottom = insets.systemWindowInsetBottom
bottom = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
insets.getInsets(WindowInsets.Type.ime() or WindowInsets.Type.systemBars()).bottom
} else {
insets.systemWindowInsetBottom
}
)
}
} else {

View File

@ -25,6 +25,8 @@ import androidx.appcompat.widget.PopupMenu
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.forEach
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSmoothScroller
@ -121,6 +123,57 @@ object RecyclerWindowInsetsListener : View.OnApplyWindowInsetsListener {
}
}
fun View.applyBottomAnimatedInsets(bottomMargin: Int = 0) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return
val setInsets: ((WindowInsets) -> Unit) = { insets ->
updateLayoutParams<ViewGroup.MarginLayoutParams> {
val bottom = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
insets.getInsets(WindowInsets.Type.systemBars() or WindowInsets.Type.ime()).bottom
} else {
insets.systemWindowInsetBottom
}
this.bottomMargin = bottom + bottomMargin
}
}
var handleInsets = true
doOnApplyWindowInsets { _, insets, _ ->
if (handleInsets) {
setInsets(insets)
}
}
ViewCompat.setWindowInsetsAnimationCallback(
this,
object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
override fun onPrepare(animation: WindowInsetsAnimationCompat) {
handleInsets = false
super.onPrepare(animation)
}
override fun onStart(
animation: WindowInsetsAnimationCompat,
bounds: WindowInsetsAnimationCompat.BoundsCompat
): WindowInsetsAnimationCompat.BoundsCompat {
handleInsets = false
rootWindowInsets?.let { insets -> setInsets(insets) }
return super.onStart(animation, bounds)
}
override fun onProgress(
insets: WindowInsetsCompat,
runningAnimations: List<WindowInsetsAnimationCompat>
): WindowInsetsCompat {
insets.toWindowInsets()?.let { setInsets(it) }
return insets
}
override fun onEnd(animation: WindowInsetsAnimationCompat) {
handleInsets = true
rootWindowInsets?.let { insets -> setInsets(insets) }
}
}
)
}
object ControllerViewWindowInsetsListener : View.OnApplyWindowInsetsListener {
override fun onApplyWindowInsets(v: View, insets: WindowInsets): WindowInsets {
v.updateLayoutParams<FrameLayout.LayoutParams> {
@ -155,6 +208,16 @@ fun View.doOnApplyWindowInsets(f: (View, WindowInsets, ViewPaddingState) -> Unit
requestApplyInsetsWhenAttached()
}
fun View.doOnApplyWindowInsetsCompat(f: (View, WindowInsetsCompat, ViewPaddingState) -> Unit) {
// Create a snapshot of the view's padding state
val paddingState = createStateForView(this)
ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets ->
f(v, insets, paddingState)
insets
}
requestApplyInsetsWhenAttached()
}
fun View.applyWindowInsetsForController() {
setOnApplyWindowInsetsListener(ControllerViewWindowInsetsListener)
requestApplyInsetsWhenAttached()