Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class ScreenStackFragment :
private var toolbar: Toolbar? = null
private var isToolbarShadowHidden = false
private var isToolbarTranslucent = false

private var isCustomAnimationRunning = false
private var lastFocusedChild: View? = null

var searchView: CustomSearchView? = null
Expand Down Expand Up @@ -249,10 +249,49 @@ class ScreenStackFragment :
object : WindowInsetsAnimationCompat.Callback(
DISPATCH_MODE_STOP,
) {
var startTranslationY = 0F
var endTranslationY = 0F

override fun onPrepare(animation: WindowInsetsAnimationCompat) {
if (animation.typeMask and WindowInsetsCompat.Type.ime() != 0) {
println("ScreenStackFragment onPrepare translationY = ${screen.translationY}")
startTranslationY = screen.translationY
}

super.onPrepare(animation)
}

override fun onStart(
animation: WindowInsetsAnimationCompat,
bounds: WindowInsetsAnimationCompat.BoundsCompat,
): WindowInsetsAnimationCompat.BoundsCompat {
if (animation.typeMask and WindowInsetsCompat.Type.ime() != 0) {
println("ScreenStackFragment onStart translationY = ${screen.translationY}")
endTranslationY = screen.translationY
screen.translationY = startTranslationY
}

return super.onStart(animation, bounds)
}

override fun onProgress(
insets: WindowInsetsCompat,
runningAnimations: MutableList<WindowInsetsAnimationCompat>,
): WindowInsetsCompat = insets
): WindowInsetsCompat {
if (isCustomAnimationRunning) {
return insets
}

runningAnimations.forEach {
if (it.typeMask and WindowInsetsCompat.Type.ime() != 0) {
// println("ScreenStackFragment IME onProgress interpolation = ${it.interpolatedFraction}")
// screen.translationY = - startTranslationY + diff * it.interpolatedFraction
screen.translationY = startTranslationY + (it.interpolatedFraction * (endTranslationY - startTranslationY))
}
}

return insets
}
},
)
}
Expand Down Expand Up @@ -288,7 +327,8 @@ class ScreenStackFragment :

val animatorSet = AnimatorSet()
val dimmingDelegate = requireDimmingDelegate()

val initialTranslationY = screen.translationY
println("ScreenStackFragment onCreateAnimator translationY=${screen.translationY}")
if (enter) {
val alphaAnimator =
ValueAnimator.ofFloat(0f, dimmingDelegate.maxAlpha).apply {
Expand All @@ -298,15 +338,16 @@ class ScreenStackFragment :
}
}
val startValueCallback = { initialStartValue: Number? -> screen.height.toFloat() }
val evaluator = ExternalBoundaryValuesEvaluator(startValueCallback, { 0f })
val evaluator = ExternalBoundaryValuesEvaluator(startValueCallback, { initialTranslationY })
val slideAnimator =
ValueAnimator.ofObject(evaluator, screen.height.toFloat(), 0f).apply {
ValueAnimator.ofObject(evaluator, screen.height.toFloat(), initialTranslationY).apply {
addUpdateListener { anim ->
val animatedValue = anim.animatedValue as? Float
animatedValue?.let { screen.translationY = it }
}
}

println("")
animatorSet
.play(slideAnimator)
.takeIf {
Expand All @@ -324,7 +365,7 @@ class ScreenStackFragment :
}
}
val slideAnimator =
ValueAnimator.ofFloat(0f, (coordinatorLayout.bottom - screen.top).toFloat()).apply {
ValueAnimator.ofFloat(initialTranslationY, (coordinatorLayout.bottom - screen.top).toFloat()).apply {
addUpdateListener { anim ->
val animatedValue = anim.animatedValue as? Float
animatedValue?.let { screen.translationY = it }
Expand All @@ -343,6 +384,23 @@ class ScreenStackFragment :
},
),
)
animatorSet.addListener(
object : Animator.AnimatorListener {
override fun onAnimationStart(p0: Animator) {
println("ScreenStackFragment onAnimationStart translationY=${screen.translationY}")
isCustomAnimationRunning = true
}

override fun onAnimationEnd(p0: Animator) {
println("ScreenStackFragment onAnimationEnd translationY=${screen.translationY}")
isCustomAnimationRunning = false
}

override fun onAnimationCancel(p0: Animator) = Unit

override fun onAnimationRepeat(p0: Animator) = Unit
},
)
return animatorSet
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ import android.os.Build
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import androidx.core.graphics.Insets
import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.swmansion.rnscreens.InsetsObserverProxy
import com.swmansion.rnscreens.KeyboardDidHide
import com.swmansion.rnscreens.KeyboardNotVisible
import com.swmansion.rnscreens.KeyboardState
Expand Down Expand Up @@ -236,6 +233,51 @@ class SheetDelegate(
}
}

private fun getMaxOffsetFromTop(): Int {
val containerHeight = tryResolveContainerHeight()

check(containerHeight != null) {
"[RNScreens] Failed to find window height during bottom sheet behaviour configuration"
}
val offestFromTop =
when (screen.sheetDetents.count()) {
1 -> {
val height =
if (screen.isSheetFitToContents()) {
screen.contentWrapper?.let { contentWrapper ->
contentWrapper.height.takeIf {
// subtree might not be laid out, e.g. after fragment reattachment
// and view recreation, however since it is retained by
// react-native it has its height cached. We want to use it.
// Otherwise we would have to trigger RN layout manually.
contentWrapper.isLaidOutOrHasCachedLayout()
}
}
} else {
(screen.sheetDetents.first() * containerHeight).toInt()
}

return containerHeight - (height ?: 0)
}
2 -> ((1 - screen.sheetDetents[1]) * containerHeight).toInt()
3 -> ((1 - screen.sheetDetents[2]) * containerHeight).toInt()

else -> throw IllegalStateException(
"[RNScreens] Invalid detent count ${screen.sheetDetents.count()}. Expected at most 3.",
)
}

return offestFromTop
}

private fun getAvailableSpaceAboveKeyboard(insets: WindowInsetsCompat): Int {
val imeInset = insets.getInsets(WindowInsetsCompat.Type.ime())
val availableSpace = this.getMaxOffsetFromTop()
val bottomPadding = if (availableSpace > imeInset.bottom) imeInset.bottom else availableSpace

return bottomPadding - insets.getInsets(WindowInsetsCompat.Type.systemBars()).top
}

// This is listener function, not the view's.
override fun onApplyWindowInsets(
v: View,
Expand All @@ -244,13 +286,18 @@ class SheetDelegate(
val isImeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
val imeInset = insets.getInsets(WindowInsetsCompat.Type.ime())

println("SheetDelegate onApplyWindowInsets isIme=$isImeVisible imeInsets=$imeInset")
if (isImeVisible) {
isKeyboardVisible = true
keyboardState = KeyboardVisible(imeInset.bottom)
sheetBehavior?.let {
this.configureBottomSheetBehaviour(it, keyboardState)
}

screen.translationY = -getAvailableSpaceAboveKeyboard(insets).toFloat()
} else {
screen.translationY = 0F

sheetBehavior?.let {
if (isKeyboardVisible) {
this.configureBottomSheetBehaviour(it, KeyboardDidHide)
Expand All @@ -264,6 +311,8 @@ class SheetDelegate(
isKeyboardVisible = false
}

screen.fragment?.startPostponedEnterTransition()

return insets
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.swmansion.rnscreens.bottomsheet

import android.os.Build
import android.view.View
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED
Expand Down Expand Up @@ -144,9 +145,14 @@ fun Screen.requiresEnterTransitionPostponing(): Boolean {
// there. Tween animations have some magic way to make this work (maybe they
// postpone the transition internally, dunno).

if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED && this.usesFormSheetPresentation()) {
return true
}

if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED || !this.usesFormSheetPresentation()) {
return false
}

// Assumes that formSheet uses content wrapper
return !this.isLaidOutOrHasCachedLayout() || this.contentWrapper?.isLaidOutOrHasCachedLayout() != true
}
Expand Down