Skip to content
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
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
9 changes: 9 additions & 0 deletions android/src/main/java/com/swmansion/rnscreens/Screen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
import com.swmansion.rnscreens.bottomsheet.BottomSheetMetrics
import com.swmansion.rnscreens.bottomsheet.isSheetFitToContents
import com.swmansion.rnscreens.bottomsheet.useSingleDetent
import com.swmansion.rnscreens.bottomsheet.usesFormSheetPresentation
Expand Down Expand Up @@ -142,6 +143,9 @@ class Screen(
if (usesFormSheetPresentation()) {
if (isSheetFitToContents()) {
sheetBehavior?.useSingleDetent(height)
// During the initial call in `onCreateView`, insets are not yet available,
// so we need to request an additional layout pass later to account for them.
requestLayout()
}

if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
Expand Down Expand Up @@ -530,6 +534,11 @@ class Screen(
}
}

fun isOverflowingStatusBar(
topInset: Int,
metrics: BottomSheetMetrics,
): Boolean = metrics.maxSheetHeight >= metrics.availableHeight - topInset

enum class StackPresentation {
PUSH,
MODAL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,37 @@ import android.view.View
import com.google.android.material.bottomsheet.BottomSheetBehavior

internal fun <T : View> BottomSheetBehavior<T>.useSingleDetent(
height: Int? = null,
maxAllowedHeight: Int? = null,
forceExpandedState: Boolean = true,
): BottomSheetBehavior<T> {
this.skipCollapsed = true
this.isFitToContents = true
if (forceExpandedState) {
this.state = BottomSheetBehavior.STATE_EXPANDED
}
height?.let {
maxHeight = height
maxAllowedHeight?.let {
maxHeight = maxAllowedHeight
}
return this
}

internal fun <T : View> BottomSheetBehavior<T>.useTwoDetents(
@BottomSheetBehavior.StableState state: Int? = null,
firstHeight: Int? = null,
secondHeight: Int? = null,
maxAllowedHeight: Int? = null,
): BottomSheetBehavior<T> {
skipCollapsed = false
isFitToContents = true
state?.let { this.state = state }
firstHeight?.let { peekHeight = firstHeight }
secondHeight?.let { maxHeight = secondHeight }
maxAllowedHeight?.let { maxHeight = maxAllowedHeight }
return this
}

internal fun <T : View> BottomSheetBehavior<T>.useThreeDetents(
@BottomSheetBehavior.StableState state: Int? = null,
firstHeight: Int? = null,
maxAllowedHeight: Int? = null,
halfExpandedRatio: Float? = null,
expandedOffsetFromTop: Int? = null,
): BottomSheetBehavior<T> {
Expand All @@ -43,5 +44,6 @@ internal fun <T : View> BottomSheetBehavior<T>.useThreeDetents(
firstHeight?.let { this.peekHeight = firstHeight }
halfExpandedRatio?.let { this.halfExpandedRatio = halfExpandedRatio }
expandedOffsetFromTop?.let { this.expandedOffset = expandedOffsetFromTop }
maxAllowedHeight?.let { maxHeight = maxAllowedHeight }
return this
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.swmansion.rnscreens.bottomsheet

import com.swmansion.rnscreens.Screen

data class BottomSheetMetrics(
val availableHeight: Int,
val maxDetent: Double,
val maxSheetHeight: Int,
)

fun getSheetMetrics(
screen: Screen,
availableHeight: Int,
sheetHeight: Int,
): BottomSheetMetrics? {
if (!screen.usesFormSheetPresentation()) {
return null
}

val maxDetent = screen.sheetDetents.lastOrNull() ?: 1.0

val maxSheetHeight =
when {
screen.isSheetFitToContents() -> sheetHeight
else -> (availableHeight * maxDetent).toInt()
}

return BottomSheetMetrics(
availableHeight = availableHeight,
maxDetent = maxDetent,
maxSheetHeight = maxSheetHeight,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.swmansion.rnscreens.KeyboardState
import com.swmansion.rnscreens.KeyboardVisible
import com.swmansion.rnscreens.Screen
import com.swmansion.rnscreens.ScreenStackFragment
import com.swmansion.rnscreens.utils.RNSLog

class SheetDelegate(
val screen: Screen,
Expand Down Expand Up @@ -142,7 +143,7 @@ class SheetDelegate(
} else {
(screen.sheetDetents.first() * containerHeight).toInt()
}
useSingleDetent(height = height)
useSingleDetent(maxAllowedHeight = height)
}

2 ->
Expand All @@ -153,7 +154,7 @@ class SheetDelegate(
screen.sheetDetents.count(),
),
firstHeight = (screen.sheetDetents[0] * containerHeight).toInt(),
secondHeight = (screen.sheetDetents[1] * containerHeight).toInt(),
maxAllowedHeight = (screen.sheetDetents.last() * containerHeight).toInt(),
)

3 ->
Expand All @@ -164,6 +165,7 @@ class SheetDelegate(
screen.sheetDetents.count(),
),
firstHeight = (screen.sheetDetents[0] * containerHeight).toInt(),
maxAllowedHeight = (screen.sheetDetents.last() * containerHeight).toInt(),
halfExpandedRatio = (screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat(),
expandedOffsetFromTop = ((1 - screen.sheetDetents[2]) * containerHeight).toInt(),
)
Expand Down Expand Up @@ -212,19 +214,20 @@ class SheetDelegate(
when (screen.sheetDetents.count()) {
1 ->
behavior.useSingleDetent(
height = (screen.sheetDetents.first() * containerHeight).toInt(),
maxAllowedHeight = (screen.sheetDetents.last() * containerHeight).toInt(),
forceExpandedState = false,
)

2 ->
behavior.useTwoDetents(
firstHeight = (screen.sheetDetents[0] * containerHeight).toInt(),
secondHeight = (screen.sheetDetents[1] * containerHeight).toInt(),
maxAllowedHeight = (screen.sheetDetents.last() * containerHeight).toInt(),
)

3 ->
behavior.useThreeDetents(
firstHeight = (screen.sheetDetents[0] * containerHeight).toInt(),
maxAllowedHeight = (screen.sheetDetents.last() * containerHeight).toInt(),
halfExpandedRatio = (screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat(),
expandedOffsetFromTop = ((1 - screen.sheetDetents[2]) * containerHeight).toInt(),
)
Expand All @@ -244,46 +247,56 @@ class SheetDelegate(
): WindowInsetsCompat {
val isImeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
val imeInset = insets.getInsets(WindowInsetsCompat.Type.ime())
val prevInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())

if (isImeVisible) {
isKeyboardVisible = true
keyboardState = KeyboardVisible(imeInset.bottom)
sheetBehavior?.let {
this.configureBottomSheetBehaviour(it, keyboardState)
}

val prevInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
return WindowInsetsCompat
.Builder(insets)
.setInsets(
WindowInsetsCompat.Type.navigationBars(),
Insets.of(
prevInsets.left,
prevInsets.top,
prevInsets.right,
0,
),
).build()
} else {
sheetBehavior?.let {
if (isKeyboardVisible) {
this.configureBottomSheetBehaviour(it, KeyboardDidHide)
} else if (keyboardState != KeyboardNotVisible) {
this.configureBottomSheetBehaviour(it, KeyboardNotVisible)
} else {
}
}

keyboardState = KeyboardNotVisible
isKeyboardVisible = false
}

val prevInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
// We're taking the metrics of the rootView, not screen to support nested navigators
val availableHeight = screen.rootView.height

val metrics =
getSheetMetrics(
screen = screen,
availableHeight = availableHeight,
sheetHeight = screen.height,
)

if (metrics == null) {
RNSLog.w("[RNScreens]", "Sheet metrics were not defined for the screen that's expected to be the sheet")
return insets
}

val newTopInset =
if (screen.isOverflowingStatusBar(prevInsets.top, metrics) && !isImeVisible) {
prevInsets.top - (metrics.availableHeight - metrics.maxSheetHeight)
} else {
0
}

val newBottomInset = if (!isImeVisible) prevInsets.bottom else 0

return WindowInsetsCompat
.Builder(insets)
.setInsets(
WindowInsetsCompat.Type.navigationBars(),
Insets.of(prevInsets.left, prevInsets.top, prevInsets.right, 0),
WindowInsetsCompat.Type.systemBars(),
Insets.of(prevInsets.left, newTopInset, prevInsets.right, newBottomInset),
).build()
}

Expand Down
19 changes: 19 additions & 0 deletions android/src/main/java/com/swmansion/rnscreens/utils/RNSLog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,23 @@ object RNSLog {
Log.d(tag, message.format(*args))
}
}

fun w(
tag: String,
message: String,
) {
if (BuildConfig.RNS_DEBUG_LOGGING) {
Log.w(tag, message)
}
}

fun w(
tag: String,
message: String,
vararg args: Any,
) {
if (BuildConfig.RNS_DEBUG_LOGGING) {
Log.w(tag, message.format(*args))
}
}
}
Loading
Loading