diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt index 996193c67a..dfc4b022ac 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt @@ -37,6 +37,11 @@ import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.ShapeAppearanceModel import com.swmansion.rnscreens.bottomsheet.DimmingFragment import com.swmansion.rnscreens.bottomsheet.SheetUtils +import com.swmansion.rnscreens.bottomsheet.isSheetFitToContents +import com.swmansion.rnscreens.bottomsheet.useSingleDetent +import com.swmansion.rnscreens.bottomsheet.useThreeDetents +import com.swmansion.rnscreens.bottomsheet.useTwoDetents +import com.swmansion.rnscreens.bottomsheet.usesFormSheetPresentation import com.swmansion.rnscreens.ext.recycle import com.swmansion.rnscreens.utils.DeviceUtils @@ -157,14 +162,15 @@ class ScreenStackFragment : screen.notifySheetDetentChange( SheetUtils.detentIndexFromSheetState( lastStableState, - screen.sheetDetents.count() - ), true + screen.sheetDetents.count(), + ), + true, ) } else if (newState == BottomSheetBehavior.STATE_DRAGGING) { screen.notifySheetDetentChange( SheetUtils.detentIndexFromSheetState( lastStableState, - screen.sheetDetents.count() + screen.sheetDetents.count(), ), false, ) @@ -223,7 +229,7 @@ class ScreenStackFragment : LinearLayout.LayoutParams.MATCH_PARENT, ).apply { behavior = - if (screen.stackPresentation == Screen.StackPresentation.FORM_SHEET) { + if (screen.usesFormSheetPresentation()) { createAndConfigureBottomSheetBehaviour() } else if (isToolbarTranslucent) { null @@ -232,7 +238,7 @@ class ScreenStackFragment : } } - if (screen.stackPresentation == Screen.StackPresentation.FORM_SHEET) { + if (screen.usesFormSheetPresentation()) { screen.clipToOutline = true // TODO(@kkafar): without this line there is no drawable / outline & nothing shows...? Determine what's going on here attachShapeToScreen(screen) @@ -241,7 +247,7 @@ class ScreenStackFragment : coordinatorLayout.addView(screen.recycle()) - if (screen.stackPresentation != Screen.StackPresentation.FORM_SHEET) { + if (!screen.usesFormSheetPresentation()) { appBarLayout = context?.let { AppBarLayout(it) }?.apply { // By default AppBarLayout will have a background color set but since we cover the whole layout @@ -348,52 +354,39 @@ class ScreenStackFragment : return when (keyboardState) { is KeyboardNotVisible -> { when (screen.sheetDetents.count()) { - 1 -> if (screen.sheetDetents.first() == Screen.SHEET_FIT_TO_CONTENTS) { - behavior.apply { - state = BottomSheetBehavior.STATE_EXPANDED - screen.contentWrapper.get()?.let { - maxHeight = it.height - } - skipCollapsed = true - isFitToContents = true - } - } else { + 1 -> behavior.apply { - state = BottomSheetBehavior.STATE_EXPANDED - skipCollapsed = true - isFitToContents = true - maxHeight = (screen.sheetDetents.first() * containerHeight).toInt() + val height = + if (screen.isSheetFitToContents()) { + screen.contentWrapper.get()?.height + } else { + (screen.sheetDetents.first() * containerHeight).toInt() + } + useSingleDetent(height = height) } - } 2 -> - behavior.apply { + behavior.useTwoDetents( state = SheetUtils.sheetStateFromDetentIndex( screen.sheetInitialDetentIndex, screen.sheetDetents.count(), - ) - skipCollapsed = false - isFitToContents = true - peekHeight = (screen.sheetDetents[0] * containerHeight).toInt() - maxHeight = (screen.sheetDetents[1] * containerHeight).toInt() - } + ), + firstHeight = (screen.sheetDetents[0] * containerHeight).toInt(), + secondHeight = (screen.sheetDetents[1] * containerHeight).toInt(), + ) 3 -> - behavior.apply { + behavior.useThreeDetents( state = SheetUtils.sheetStateFromDetentIndex( screen.sheetInitialDetentIndex, screen.sheetDetents.count(), - ) - skipCollapsed = false - isFitToContents = false - peekHeight = (screen.sheetDetents[0] * containerHeight).toInt() - expandedOffset = - ((1 - screen.sheetDetents[2]) * containerHeight).toInt() - halfExpandedRatio = - (screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat() - } + ), + firstHeight = (screen.sheetDetents[0] * containerHeight).toInt(), + halfExpandedRatio = (screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat(), + expandedOffsetFromTop = ((1 - screen.sheetDetents[2]) * containerHeight).toInt(), + ) else -> throw IllegalStateException( "[RNScreens] Invalid detent count ${screen.sheetDetents.count()}. Expected at most 3.", @@ -411,27 +404,24 @@ class ScreenStackFragment : when (screen.sheetDetents.count()) { 1 -> behavior.apply { - state = BottomSheetBehavior.STATE_EXPANDED - skipCollapsed = true - isFitToContents = true - maxHeight = newMaxHeight + useSingleDetent(height = newMaxHeight) addBottomSheetCallback(keyboardSheetCallback) } 2 -> behavior.apply { - state = BottomSheetBehavior.STATE_EXPANDED - skipCollapsed = false - isFitToContents = true - maxHeight = newMaxHeight + useTwoDetents( + state = BottomSheetBehavior.STATE_EXPANDED, + secondHeight = newMaxHeight, + ) addBottomSheetCallback(keyboardSheetCallback) } 3 -> behavior.apply { - state = BottomSheetBehavior.STATE_EXPANDED - skipCollapsed = false - isFitToContents = false + useThreeDetents( + state = BottomSheetBehavior.STATE_EXPANDED, + ) maxHeight = newMaxHeight addBottomSheetCallback(keyboardSheetCallback) } @@ -450,30 +440,23 @@ class ScreenStackFragment : behavior.removeBottomSheetCallback(keyboardSheetCallback) when (screen.sheetDetents.count()) { 1 -> - behavior.apply { - skipCollapsed = true - isFitToContents = true - maxHeight = (screen.sheetDetents.first() * containerHeight).toInt() - } + behavior.useSingleDetent( + height = (screen.sheetDetents.first() * containerHeight).toInt(), + forceExpandedState = false, + ) 2 -> - behavior.apply { - skipCollapsed = false - isFitToContents = true - peekHeight = (screen.sheetDetents[0] * containerHeight).toInt() - maxHeight = (screen.sheetDetents[1] * containerHeight).toInt() - } + behavior.useTwoDetents( + firstHeight = (screen.sheetDetents[0] * containerHeight).toInt(), + secondHeight = (screen.sheetDetents[1] * containerHeight).toInt(), + ) 3 -> - behavior.apply { - skipCollapsed = false - isFitToContents = false - peekHeight = (screen.sheetDetents[0] * containerHeight).toInt() - expandedOffset = - ((1 - screen.sheetDetents[2]) * containerHeight).toInt() - halfExpandedRatio = - (screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat() - } + behavior.useThreeDetents( + firstHeight = (screen.sheetDetents[0] * containerHeight).toInt(), + halfExpandedRatio = (screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat(), + expandedOffsetFromTop = ((1 - screen.sheetDetents[2]) * containerHeight).toInt(), + ) else -> throw IllegalStateException( "[RNScreens] Invalid detent count ${screen.sheetDetents.count()}. Expected at most 3.", @@ -595,8 +578,7 @@ class ScreenStackFragment : // ) : CoordinatorLayout(context), ReactCompoundViewGroup, ReactHitSlopView { ) : CoordinatorLayout(context), ReactPointerEventsView { - override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets = - super.onApplyWindowInsets(insets) + override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets = super.onApplyWindowInsets(insets) private val animationListener: Animation.AnimationListener = object : Animation.AnimationListener { diff --git a/android/src/main/java/com/swmansion/rnscreens/bottomsheet/BottomSheetBehaviorExt.kt b/android/src/main/java/com/swmansion/rnscreens/bottomsheet/BottomSheetBehaviorExt.kt new file mode 100644 index 0000000000..5ebfbc3352 --- /dev/null +++ b/android/src/main/java/com/swmansion/rnscreens/bottomsheet/BottomSheetBehaviorExt.kt @@ -0,0 +1,42 @@ +package com.swmansion.rnscreens.bottomsheet + +import android.view.View +import com.google.android.material.bottomsheet.BottomSheetBehavior + +internal fun BottomSheetBehavior.useSingleDetent(height: Int? = null, forceExpandedState: Boolean = true): BottomSheetBehavior { + this.skipCollapsed = true + this.isFitToContents = true + if (forceExpandedState) { + this.state = BottomSheetBehavior.STATE_EXPANDED + } + height?.let { maxHeight = height } + return this +} + +internal fun BottomSheetBehavior.useTwoDetents( + @BottomSheetBehavior.StableState state: Int? = null, + firstHeight: Int? = null, + secondHeight: Int? = null +): BottomSheetBehavior { + skipCollapsed = false + isFitToContents = true + state?.let { this.state = state } + firstHeight?.let { peekHeight = firstHeight } + secondHeight?.let { maxHeight = secondHeight } + return this +} + +internal fun BottomSheetBehavior.useThreeDetents( + @BottomSheetBehavior.StableState state: Int? = null, + firstHeight: Int? = null, + halfExpandedRatio: Float? = null, + expandedOffsetFromTop: Int? = null +): BottomSheetBehavior { + skipCollapsed = false + isFitToContents = false + state?.let { this.state = state } + firstHeight?.let { this.peekHeight = firstHeight } + halfExpandedRatio?.let { this.halfExpandedRatio = halfExpandedRatio } + expandedOffsetFromTop?.let { this.expandedOffset = expandedOffsetFromTop } + return this +} diff --git a/android/src/main/java/com/swmansion/rnscreens/bottomsheet/SheetUtils.kt b/android/src/main/java/com/swmansion/rnscreens/bottomsheet/SheetUtils.kt index 0e31843af5..06bf8a02ec 100644 --- a/android/src/main/java/com/swmansion/rnscreens/bottomsheet/SheetUtils.kt +++ b/android/src/main/java/com/swmansion/rnscreens/bottomsheet/SheetUtils.kt @@ -5,6 +5,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPS import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HALF_EXPANDED import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN +import com.swmansion.rnscreens.Screen object SheetUtils { /** @@ -125,3 +126,10 @@ object SheetUtils { return false } } + +fun Screen.isSheetFitToContents(): Boolean = + stackPresentation === Screen.StackPresentation.FORM_SHEET && + sheetDetents.count() == 1 && + sheetDetents.first() == Screen.SHEET_FIT_TO_CONTENTS + +fun Screen.usesFormSheetPresentation(): Boolean = stackPresentation === Screen.StackPresentation.FORM_SHEET