react-native-screens 4.7.0-beta.0 → 4.7.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +19 -1
- package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +70 -3
- package/android/src/main/java/com/swmansion/rnscreens/ScreenContentWrapper.kt +2 -2
- package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +1 -7
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +8 -4
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +150 -21
- package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/BottomSheetBehaviorExt.kt +3 -1
- package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/DimmingView.kt +3 -1
- package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/DimmingViewManager.kt +165 -0
- package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/SheetDelegate.kt +119 -0
- package/android/src/main/java/com/swmansion/rnscreens/events/ScreenEventDelegate.kt +48 -0
- package/android/src/main/java/com/swmansion/rnscreens/transition/ExternalBoundaryValuesEvaluator.kt +38 -0
- package/ios/RNSScreen.h +17 -4
- package/ios/RNSScreen.mm +32 -13
- package/ios/RNSScreenContentWrapper.h +2 -2
- package/ios/RNSScreenContentWrapper.mm +68 -11
- package/ios/RNSScreenStack.mm +12 -1
- package/package.json +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/DimmingFragment.kt +0 -493
package/android/build.gradle
CHANGED
|
@@ -95,6 +95,14 @@ def safeAppExtGet(prop, fallback) {
|
|
|
95
95
|
appProject?.ext?.has(prop) ? appProject.ext.get(prop) : fallback
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
def reactNativeRootDir = resolveReactNativeDirectory()
|
|
99
|
+
|
|
100
|
+
def reactProperties = new Properties()
|
|
101
|
+
file("$reactNativeRootDir/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) }
|
|
102
|
+
|
|
103
|
+
def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME")
|
|
104
|
+
def REACT_NATIVE_MINOR_VERSION = REACT_NATIVE_VERSION.startsWith("0.0.0-") ? 1000 : REACT_NATIVE_VERSION.split("\\.")[1].toInteger()
|
|
105
|
+
|
|
98
106
|
def IS_NEW_ARCHITECTURE_ENABLED = isNewArchitectureEnabled()
|
|
99
107
|
|
|
100
108
|
android {
|
|
@@ -166,6 +174,7 @@ android {
|
|
|
166
174
|
sourceSets.main {
|
|
167
175
|
ext.androidResDir = "src/main/res"
|
|
168
176
|
java {
|
|
177
|
+
// Architecture specific
|
|
169
178
|
if (IS_NEW_ARCHITECTURE_ENABLED) {
|
|
170
179
|
srcDirs += [
|
|
171
180
|
"src/fabric/java",
|
|
@@ -175,6 +184,15 @@ android {
|
|
|
175
184
|
"src/paper/java",
|
|
176
185
|
]
|
|
177
186
|
}
|
|
187
|
+
|
|
188
|
+
// Background color resolving
|
|
189
|
+
if (REACT_NATIVE_MINOR_VERSION <= 74) {
|
|
190
|
+
srcDirs += "src/versioned/backgroundcolor/74"
|
|
191
|
+
} else if (REACT_NATIVE_MINOR_VERSION <= 76) {
|
|
192
|
+
srcDirs += "src/versioned/backgroundcolor/76"
|
|
193
|
+
} else {
|
|
194
|
+
srcDirs += "src/versioned/backgroundcolor/latest"
|
|
195
|
+
}
|
|
178
196
|
}
|
|
179
197
|
res {
|
|
180
198
|
if (safeExtGet(['compileSdkVersion', 'compileSdk'], rnsDefaultCompileSdkVersion) >= 33) {
|
|
@@ -188,7 +206,7 @@ android {
|
|
|
188
206
|
|
|
189
207
|
repositories {
|
|
190
208
|
maven {
|
|
191
|
-
url "${
|
|
209
|
+
url "${reactNativeRootDir}/android"
|
|
192
210
|
}
|
|
193
211
|
|
|
194
212
|
mavenCentral()
|
|
@@ -5,6 +5,7 @@ import android.content.pm.ActivityInfo
|
|
|
5
5
|
import android.graphics.Paint
|
|
6
6
|
import android.os.Parcelable
|
|
7
7
|
import android.util.SparseArray
|
|
8
|
+
import android.view.MotionEvent
|
|
8
9
|
import android.view.View
|
|
9
10
|
import android.view.ViewGroup
|
|
10
11
|
import android.view.WindowManager
|
|
@@ -17,6 +18,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
|
17
18
|
import com.facebook.react.bridge.GuardedRunnable
|
|
18
19
|
import com.facebook.react.bridge.ReactContext
|
|
19
20
|
import com.facebook.react.uimanager.PixelUtil
|
|
21
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
20
22
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
21
23
|
import com.facebook.react.uimanager.UIManagerModule
|
|
22
24
|
import com.facebook.react.uimanager.events.EventDispatcher
|
|
@@ -24,13 +26,16 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
|
24
26
|
import com.google.android.material.shape.CornerFamily
|
|
25
27
|
import com.google.android.material.shape.MaterialShapeDrawable
|
|
26
28
|
import com.google.android.material.shape.ShapeAppearanceModel
|
|
29
|
+
import com.swmansion.rnscreens.bottomsheet.isSheetFitToContents
|
|
30
|
+
import com.swmansion.rnscreens.bottomsheet.usesFormSheetPresentation
|
|
27
31
|
import com.swmansion.rnscreens.events.HeaderHeightChangeEvent
|
|
28
32
|
import com.swmansion.rnscreens.events.SheetDetentChangedEvent
|
|
33
|
+
import com.swmansion.rnscreens.ext.parentAsViewGroup
|
|
29
34
|
import java.lang.ref.WeakReference
|
|
30
35
|
|
|
31
36
|
@SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated.
|
|
32
37
|
class Screen(
|
|
33
|
-
val reactContext:
|
|
38
|
+
val reactContext: ThemedReactContext,
|
|
34
39
|
) : FabricEnabledViewGroup(reactContext),
|
|
35
40
|
ScreenContentWrapper.OnLayoutCallback {
|
|
36
41
|
val fragment: Fragment?
|
|
@@ -81,6 +86,13 @@ class Screen(
|
|
|
81
86
|
var sheetClosesOnTouchOutside = true
|
|
82
87
|
var sheetElevation: Float = 24F
|
|
83
88
|
|
|
89
|
+
/**
|
|
90
|
+
* When using form sheet presentation we want to delay enter transition **on Paper** in order
|
|
91
|
+
* to wait for initial layout from React, otherwise the animator-based animation will look
|
|
92
|
+
* glitchy. *This is not needed on Fabric*.
|
|
93
|
+
*/
|
|
94
|
+
var shouldTriggerPostponedTransitionAfterLayout = false
|
|
95
|
+
|
|
84
96
|
var footer: ScreenFooter? = null
|
|
85
97
|
set(value) {
|
|
86
98
|
if (value == null && field != null) {
|
|
@@ -110,7 +122,7 @@ class Screen(
|
|
|
110
122
|
* `fitToContents` for formSheets, as this is first entry point where we can acquire
|
|
111
123
|
* height of our content.
|
|
112
124
|
*/
|
|
113
|
-
override fun
|
|
125
|
+
override fun onContentWrapperLayout(
|
|
114
126
|
changed: Boolean,
|
|
115
127
|
left: Int,
|
|
116
128
|
top: Int,
|
|
@@ -119,12 +131,23 @@ class Screen(
|
|
|
119
131
|
) {
|
|
120
132
|
val height = bottom - top
|
|
121
133
|
|
|
122
|
-
if (
|
|
134
|
+
if (isSheetFitToContents()) {
|
|
123
135
|
sheetBehavior?.let {
|
|
124
136
|
if (it.maxHeight != height) {
|
|
125
137
|
it.maxHeight = height
|
|
126
138
|
}
|
|
127
139
|
}
|
|
140
|
+
|
|
141
|
+
if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
|
142
|
+
// On old architecture we delay enter transition in order to wait for initial frame.
|
|
143
|
+
shouldTriggerPostponedTransitionAfterLayout = true
|
|
144
|
+
val parent = parentAsViewGroup()
|
|
145
|
+
if (parent != null && !parent.isInLayout) {
|
|
146
|
+
// There are reported cases (irreproducible) when Screen is not laid out after
|
|
147
|
+
// maxHeight is set on behaviour.
|
|
148
|
+
parent.requestLayout()
|
|
149
|
+
}
|
|
150
|
+
}
|
|
128
151
|
}
|
|
129
152
|
}
|
|
130
153
|
|
|
@@ -162,6 +185,17 @@ class Screen(
|
|
|
162
185
|
|
|
163
186
|
footer?.onParentLayout(changed, l, t, r, b, container!!.height)
|
|
164
187
|
notifyHeaderHeightChange(t)
|
|
188
|
+
|
|
189
|
+
if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
|
190
|
+
maybeTriggerPostponedTransition()
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private fun maybeTriggerPostponedTransition() {
|
|
196
|
+
if (shouldTriggerPostponedTransitionAfterLayout) {
|
|
197
|
+
shouldTriggerPostponedTransitionAfterLayout = false
|
|
198
|
+
fragment?.startPostponedEnterTransition()
|
|
165
199
|
}
|
|
166
200
|
}
|
|
167
201
|
|
|
@@ -377,6 +411,28 @@ class Screen(
|
|
|
377
411
|
}
|
|
378
412
|
}
|
|
379
413
|
|
|
414
|
+
fun endRemovalTransition() {
|
|
415
|
+
if (!isBeingRemoved) {
|
|
416
|
+
return
|
|
417
|
+
}
|
|
418
|
+
isBeingRemoved = false
|
|
419
|
+
endTransitionRecursive(this)
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private fun endTransitionRecursive(parent: ViewGroup) {
|
|
423
|
+
parent.children.forEach { childView ->
|
|
424
|
+
parent.endViewTransition(childView)
|
|
425
|
+
|
|
426
|
+
if (childView is ScreenStackHeaderConfig) {
|
|
427
|
+
endTransitionRecursive(childView.toolbar)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (childView is ViewGroup) {
|
|
431
|
+
endTransitionRecursive(childView)
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
380
436
|
private fun startTransitionRecursive(parent: ViewGroup?) {
|
|
381
437
|
parent?.let {
|
|
382
438
|
for (i in 0 until it.childCount) {
|
|
@@ -407,6 +463,17 @@ class Screen(
|
|
|
407
463
|
}
|
|
408
464
|
}
|
|
409
465
|
|
|
466
|
+
// We do not want to perform any action, therefore do not need to override the associated method.
|
|
467
|
+
@SuppressLint("ClickableViewAccessibility")
|
|
468
|
+
override fun onTouchEvent(event: MotionEvent?): Boolean =
|
|
469
|
+
if (usesFormSheetPresentation()) {
|
|
470
|
+
// If we're a form sheet we want to consume the gestures to prevent
|
|
471
|
+
// DimmingView's callback from triggering when clicking on the sheet itself.
|
|
472
|
+
true
|
|
473
|
+
} else {
|
|
474
|
+
super.onTouchEvent(event)
|
|
475
|
+
}
|
|
476
|
+
|
|
410
477
|
private fun notifyHeaderHeightChange(headerHeight: Int) {
|
|
411
478
|
val screenContext = context as ReactContext
|
|
412
479
|
val surfaceId = UIManagerHelper.getSurfaceId(screenContext)
|
|
@@ -17,7 +17,7 @@ class ScreenContentWrapper(
|
|
|
17
17
|
internal var delegate: OnLayoutCallback? = null
|
|
18
18
|
|
|
19
19
|
interface OnLayoutCallback {
|
|
20
|
-
fun
|
|
20
|
+
fun onContentWrapperLayout(
|
|
21
21
|
changed: Boolean,
|
|
22
22
|
left: Int,
|
|
23
23
|
top: Int,
|
|
@@ -33,6 +33,6 @@ class ScreenContentWrapper(
|
|
|
33
33
|
right: Int,
|
|
34
34
|
bottom: Int,
|
|
35
35
|
) {
|
|
36
|
-
delegate?.
|
|
36
|
+
delegate?.onContentWrapperLayout(changed, left, top, right, bottom)
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -15,7 +15,6 @@ import com.facebook.react.bridge.UiThreadUtil
|
|
|
15
15
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
16
16
|
import com.facebook.react.uimanager.events.Event
|
|
17
17
|
import com.facebook.react.uimanager.events.EventDispatcher
|
|
18
|
-
import com.swmansion.rnscreens.bottomsheet.DimmingFragment
|
|
19
18
|
import com.swmansion.rnscreens.events.HeaderBackButtonClickedEvent
|
|
20
19
|
import com.swmansion.rnscreens.events.ScreenAppearEvent
|
|
21
20
|
import com.swmansion.rnscreens.events.ScreenDisappearEvent
|
|
@@ -290,12 +289,7 @@ open class ScreenFragment :
|
|
|
290
289
|
// since we subscribe to parent's animation start/end and dispatch events in child from there
|
|
291
290
|
// check for `isTransitioning` should be enough since the child's animation should take only
|
|
292
291
|
// 20ms due to always being `StackAnimation.NONE` when nested stack being pushed
|
|
293
|
-
val parent =
|
|
294
|
-
if (parentFragment is DimmingFragment) {
|
|
295
|
-
parentFragment?.parentFragment
|
|
296
|
-
} else {
|
|
297
|
-
parentFragment
|
|
298
|
-
}
|
|
292
|
+
val parent = parentFragment
|
|
299
293
|
if (parent == null || (parent is ScreenFragment && !parent.isTransitioning)) {
|
|
300
294
|
// onViewAnimationStart/End is triggered from View#onAnimationStart/End method of the fragment's root
|
|
301
295
|
// view. We override an appropriate method of the StackFragment's
|
|
@@ -7,11 +7,10 @@ import android.view.View
|
|
|
7
7
|
import com.facebook.react.bridge.ReactContext
|
|
8
8
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
9
9
|
import com.swmansion.rnscreens.Screen.StackAnimation
|
|
10
|
-
import com.swmansion.rnscreens.bottomsheet.
|
|
10
|
+
import com.swmansion.rnscreens.bottomsheet.isSheetFitToContents
|
|
11
11
|
import com.swmansion.rnscreens.events.StackFinishTransitioningEvent
|
|
12
12
|
import java.util.Collections
|
|
13
13
|
import kotlin.collections.ArrayList
|
|
14
|
-
import kotlin.collections.HashSet
|
|
15
14
|
|
|
16
15
|
class ScreenStack(
|
|
17
16
|
context: Context?,
|
|
@@ -50,7 +49,7 @@ class ScreenStack(
|
|
|
50
49
|
|
|
51
50
|
override fun adapt(screen: Screen): ScreenStackFragmentWrapper =
|
|
52
51
|
when (screen.stackPresentation) {
|
|
53
|
-
Screen.StackPresentation.FORM_SHEET ->
|
|
52
|
+
Screen.StackPresentation.FORM_SHEET -> ScreenStackFragment(screen)
|
|
54
53
|
else -> ScreenStackFragment(screen)
|
|
55
54
|
}
|
|
56
55
|
|
|
@@ -242,7 +241,6 @@ class ScreenStack(
|
|
|
242
241
|
}
|
|
243
242
|
}
|
|
244
243
|
}
|
|
245
|
-
|
|
246
244
|
// animation logic end
|
|
247
245
|
goingForward = shouldUseOpenAnimation
|
|
248
246
|
|
|
@@ -302,6 +300,12 @@ class ScreenStack(
|
|
|
302
300
|
}
|
|
303
301
|
}
|
|
304
302
|
} else if (newTop != null && !newTop.fragment.isAdded) {
|
|
303
|
+
if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED && newTop.screen.isSheetFitToContents()) {
|
|
304
|
+
// On old architecture the content wrapper might not have received its frame yet,
|
|
305
|
+
// which is required to determine height of the sheet after animation. Therefore
|
|
306
|
+
// we delay the transition and trigger it after views receive the layout.
|
|
307
|
+
newTop.fragment.postponeEnterTransition()
|
|
308
|
+
}
|
|
305
309
|
it.add(id, newTop.fragment)
|
|
306
310
|
}
|
|
307
311
|
topScreenWrapper = newTop as? ScreenStackFragmentWrapper
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
package com.swmansion.rnscreens
|
|
2
2
|
|
|
3
|
+
import android.animation.Animator
|
|
4
|
+
import android.animation.AnimatorSet
|
|
5
|
+
import android.animation.ValueAnimator
|
|
3
6
|
import android.annotation.SuppressLint
|
|
4
7
|
import android.content.Context
|
|
5
8
|
import android.graphics.Color
|
|
@@ -16,7 +19,6 @@ import android.view.WindowInsets
|
|
|
16
19
|
import android.view.WindowManager
|
|
17
20
|
import android.view.animation.Animation
|
|
18
21
|
import android.view.animation.AnimationSet
|
|
19
|
-
import android.view.animation.AnimationUtils
|
|
20
22
|
import android.view.animation.Transformation
|
|
21
23
|
import android.view.inputmethod.InputMethodManager
|
|
22
24
|
import android.widget.LinearLayout
|
|
@@ -27,6 +29,7 @@ import androidx.core.view.WindowInsetsCompat
|
|
|
27
29
|
import com.facebook.react.uimanager.PixelUtil
|
|
28
30
|
import com.facebook.react.uimanager.PointerEvents
|
|
29
31
|
import com.facebook.react.uimanager.ReactPointerEventsView
|
|
32
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
30
33
|
import com.google.android.material.appbar.AppBarLayout
|
|
31
34
|
import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior
|
|
32
35
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
@@ -34,14 +37,20 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCa
|
|
|
34
37
|
import com.google.android.material.shape.CornerFamily
|
|
35
38
|
import com.google.android.material.shape.MaterialShapeDrawable
|
|
36
39
|
import com.google.android.material.shape.ShapeAppearanceModel
|
|
40
|
+
import com.swmansion.rnscreens.bottomsheet.DimmingViewManager
|
|
41
|
+
import com.swmansion.rnscreens.bottomsheet.SheetDelegate
|
|
37
42
|
import com.swmansion.rnscreens.bottomsheet.SheetUtils
|
|
38
43
|
import com.swmansion.rnscreens.bottomsheet.isSheetFitToContents
|
|
39
44
|
import com.swmansion.rnscreens.bottomsheet.useSingleDetent
|
|
40
45
|
import com.swmansion.rnscreens.bottomsheet.useThreeDetents
|
|
41
46
|
import com.swmansion.rnscreens.bottomsheet.useTwoDetents
|
|
42
47
|
import com.swmansion.rnscreens.bottomsheet.usesFormSheetPresentation
|
|
48
|
+
import com.swmansion.rnscreens.events.ScreenDismissedEvent
|
|
49
|
+
import com.swmansion.rnscreens.events.ScreenEventDelegate
|
|
43
50
|
import com.swmansion.rnscreens.ext.recycle
|
|
51
|
+
import com.swmansion.rnscreens.transition.ExternalBoundaryValuesEvaluator
|
|
44
52
|
import com.swmansion.rnscreens.utils.DeviceUtils
|
|
53
|
+
import com.swmansion.rnscreens.utils.resolveBackgroundColor
|
|
45
54
|
|
|
46
55
|
sealed class KeyboardState
|
|
47
56
|
|
|
@@ -76,6 +85,13 @@ class ScreenStackFragment :
|
|
|
76
85
|
return container
|
|
77
86
|
}
|
|
78
87
|
|
|
88
|
+
private val dimmingDelegate =
|
|
89
|
+
lazy(LazyThreadSafetyMode.NONE) {
|
|
90
|
+
DimmingViewManager(screen.reactContext, screen)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private var sheetDelegate: SheetDelegate? = null
|
|
94
|
+
|
|
79
95
|
@SuppressLint("ValidFragment")
|
|
80
96
|
constructor(screenView: Screen) : super(screenView)
|
|
81
97
|
|
|
@@ -131,7 +147,12 @@ class ScreenStackFragment :
|
|
|
131
147
|
|
|
132
148
|
override fun onViewAnimationEnd() {
|
|
133
149
|
super.onViewAnimationEnd()
|
|
150
|
+
|
|
151
|
+
// Rely on guards inside the callee to detect whether this was indeed appear transition.
|
|
134
152
|
notifyViewAppearTransitionEnd()
|
|
153
|
+
|
|
154
|
+
// Rely on guards inside the callee to detect whether this was indeed removal transition.
|
|
155
|
+
screen.endRemovalTransition()
|
|
135
156
|
}
|
|
136
157
|
|
|
137
158
|
private fun notifyViewAppearTransitionEnd() {
|
|
@@ -176,7 +197,7 @@ class ScreenStackFragment :
|
|
|
176
197
|
}
|
|
177
198
|
|
|
178
199
|
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
|
179
|
-
|
|
200
|
+
dismissSelf()
|
|
180
201
|
}
|
|
181
202
|
}
|
|
182
203
|
|
|
@@ -186,18 +207,17 @@ class ScreenStackFragment :
|
|
|
186
207
|
) = Unit
|
|
187
208
|
}
|
|
188
209
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
)
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
AnimationUtils.loadAnimation(context, R.anim.rns_slide_out_to_bottom)
|
|
210
|
+
/**
|
|
211
|
+
* Currently this method dispatches event to JS where state is recomputed and fragment
|
|
212
|
+
* gets removed in the result of incoming state update.
|
|
213
|
+
*/
|
|
214
|
+
internal fun dismissSelf() {
|
|
215
|
+
if (!this.isRemoving || !this.isDetached) {
|
|
216
|
+
val reactContext = screen.reactContext
|
|
217
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
218
|
+
UIManagerHelper
|
|
219
|
+
.getEventDispatcherForReactTag(reactContext, screen.id)
|
|
220
|
+
?.dispatchEvent(ScreenDismissedEvent(surfaceId, screen.id))
|
|
201
221
|
}
|
|
202
222
|
}
|
|
203
223
|
|
|
@@ -205,6 +225,10 @@ class ScreenStackFragment :
|
|
|
205
225
|
screen.onSheetCornerRadiusChange()
|
|
206
226
|
}
|
|
207
227
|
|
|
228
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
229
|
+
super.onCreate(savedInstanceState)
|
|
230
|
+
}
|
|
231
|
+
|
|
208
232
|
override fun onCreateView(
|
|
209
233
|
inflater: LayoutInflater,
|
|
210
234
|
container: ViewGroup?,
|
|
@@ -262,6 +286,90 @@ class ScreenStackFragment :
|
|
|
262
286
|
return coordinatorLayout
|
|
263
287
|
}
|
|
264
288
|
|
|
289
|
+
override fun onViewCreated(
|
|
290
|
+
view: View,
|
|
291
|
+
savedInstanceState: Bundle?,
|
|
292
|
+
) {
|
|
293
|
+
super.onViewCreated(view, savedInstanceState)
|
|
294
|
+
|
|
295
|
+
if (!screen.usesFormSheetPresentation()) {
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
sheetDelegate = SheetDelegate(screen)
|
|
300
|
+
|
|
301
|
+
assert(view == coordinatorLayout)
|
|
302
|
+
dimmingDelegate.value.onViewHierarchyCreated(screen, coordinatorLayout)
|
|
303
|
+
dimmingDelegate.value.onBehaviourAttached(screen, screen.sheetBehavior!!)
|
|
304
|
+
|
|
305
|
+
val container = screen.container!!
|
|
306
|
+
coordinatorLayout.measure(
|
|
307
|
+
View.MeasureSpec.makeMeasureSpec(container.width, View.MeasureSpec.EXACTLY),
|
|
308
|
+
View.MeasureSpec.makeMeasureSpec(container.height, View.MeasureSpec.EXACTLY),
|
|
309
|
+
)
|
|
310
|
+
coordinatorLayout.layout(0, 0, container.width, container.height)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
override fun onCreateAnimation(
|
|
314
|
+
transit: Int,
|
|
315
|
+
enter: Boolean,
|
|
316
|
+
nextAnim: Int,
|
|
317
|
+
): Animation? {
|
|
318
|
+
// Ensure onCreateAnimator is called
|
|
319
|
+
return null
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
override fun onCreateAnimator(
|
|
323
|
+
transit: Int,
|
|
324
|
+
enter: Boolean,
|
|
325
|
+
nextAnim: Int,
|
|
326
|
+
): Animator? {
|
|
327
|
+
if (!screen.usesFormSheetPresentation()) {
|
|
328
|
+
// Use animation defined while defining transaction in screen stack
|
|
329
|
+
return null
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
val animatorSet = AnimatorSet()
|
|
333
|
+
|
|
334
|
+
if (enter) {
|
|
335
|
+
val alphaAnimator =
|
|
336
|
+
ValueAnimator.ofFloat(0f, dimmingDelegate.value.maxAlpha).apply {
|
|
337
|
+
addUpdateListener { anim ->
|
|
338
|
+
val animatedValue = anim.animatedValue as? Float
|
|
339
|
+
animatedValue?.let { dimmingDelegate.value.dimmingView.alpha = it }
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
val startValueCallback = { initialStartValue: Number? -> screen.height.toFloat() }
|
|
343
|
+
val evaluator = ExternalBoundaryValuesEvaluator(startValueCallback, { 0f })
|
|
344
|
+
val slideAnimator =
|
|
345
|
+
ValueAnimator.ofObject(evaluator, screen.height.toFloat(), 0f).apply {
|
|
346
|
+
addUpdateListener { anim ->
|
|
347
|
+
val animatedValue = anim.animatedValue as? Float
|
|
348
|
+
animatedValue?.let { screen.translationY = it }
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
animatorSet.play(alphaAnimator).with(slideAnimator)
|
|
352
|
+
} else {
|
|
353
|
+
val alphaAnimator =
|
|
354
|
+
ValueAnimator.ofFloat(dimmingDelegate.value.dimmingView.alpha, 0f).apply {
|
|
355
|
+
addUpdateListener { anim ->
|
|
356
|
+
val animatedValue = anim.animatedValue as? Float
|
|
357
|
+
animatedValue?.let { dimmingDelegate.value.dimmingView.alpha = it }
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
val slideAnimator =
|
|
361
|
+
ValueAnimator.ofFloat(0f, (coordinatorLayout.bottom - screen.top).toFloat()).apply {
|
|
362
|
+
addUpdateListener { anim ->
|
|
363
|
+
val animatedValue = anim.animatedValue as? Float
|
|
364
|
+
animatedValue?.let { screen.translationY = it }
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
animatorSet.play(alphaAnimator).with(slideAnimator)
|
|
368
|
+
}
|
|
369
|
+
animatorSet.addListener(ScreenEventDelegate(this))
|
|
370
|
+
return animatorSet
|
|
371
|
+
}
|
|
372
|
+
|
|
265
373
|
/**
|
|
266
374
|
* This method might return slightly different values depending on code path,
|
|
267
375
|
* but during testing I've found this effect negligible. For practical purposes
|
|
@@ -348,7 +456,10 @@ class ScreenStackFragment :
|
|
|
348
456
|
behavior.apply {
|
|
349
457
|
val height =
|
|
350
458
|
if (screen.isSheetFitToContents()) {
|
|
351
|
-
screen.contentWrapper
|
|
459
|
+
screen.contentWrapper
|
|
460
|
+
.get()
|
|
461
|
+
?.height
|
|
462
|
+
.takeIf { screen.contentWrapper.get()?.isLaidOut == true }
|
|
352
463
|
} else {
|
|
353
464
|
(screen.sheetDetents.first() * containerHeight).toInt()
|
|
354
465
|
}
|
|
@@ -456,10 +567,30 @@ class ScreenStackFragment :
|
|
|
456
567
|
}
|
|
457
568
|
}
|
|
458
569
|
|
|
570
|
+
private fun createBottomSheetBehaviour(): BottomSheetBehavior<Screen> = BottomSheetBehavior<Screen>()
|
|
571
|
+
|
|
459
572
|
// In general it would be great to create BottomSheetBehaviour only via this method as it runs some
|
|
460
573
|
// side effects.
|
|
461
|
-
|
|
462
|
-
configureBottomSheetBehaviour(
|
|
574
|
+
private fun createAndConfigureBottomSheetBehaviour(): BottomSheetBehavior<Screen> =
|
|
575
|
+
configureBottomSheetBehaviour(createBottomSheetBehaviour())
|
|
576
|
+
|
|
577
|
+
private fun resolveBackgroundColor(screen: Screen): Int? {
|
|
578
|
+
val screenColor =
|
|
579
|
+
(screen.background as? ColorDrawable?)?.color
|
|
580
|
+
?: (screen.background as? MaterialShapeDrawable?)?.tintList?.defaultColor
|
|
581
|
+
|
|
582
|
+
if (screenColor != null) {
|
|
583
|
+
return screenColor
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
val contentWrapper = screen.contentWrapper.get()
|
|
587
|
+
if (contentWrapper == null) {
|
|
588
|
+
return null
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
val contentWrapperColor = contentWrapper.resolveBackgroundColor()
|
|
592
|
+
return contentWrapperColor
|
|
593
|
+
}
|
|
463
594
|
|
|
464
595
|
private fun attachShapeToScreen(screen: Screen) {
|
|
465
596
|
val cornerSize = PixelUtil.toPixelFromDIP(screen.sheetCornerRadius)
|
|
@@ -471,10 +602,8 @@ class ScreenStackFragment :
|
|
|
471
602
|
setTopRightCorner(CornerFamily.ROUNDED, cornerSize)
|
|
472
603
|
}.build()
|
|
473
604
|
val shape = MaterialShapeDrawable(shapeAppearanceModel)
|
|
474
|
-
val
|
|
475
|
-
|
|
476
|
-
?: (screen.background as? MaterialShapeDrawable?)?.tintList?.defaultColor
|
|
477
|
-
shape.setTint(currentColor ?: Color.TRANSPARENT)
|
|
605
|
+
val backgroundColor = resolveBackgroundColor(screen)
|
|
606
|
+
shape.setTint(backgroundColor ?: Color.TRANSPARENT)
|
|
478
607
|
screen.background = shape
|
|
479
608
|
}
|
|
480
609
|
|
|
@@ -11,7 +11,7 @@ import com.facebook.react.uimanager.ReactPointerEventsView
|
|
|
11
11
|
import com.swmansion.rnscreens.ext.equalWithRespectToEps
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Serves as dimming view that can be used as background for some view that not fully
|
|
14
|
+
* Serves as dimming view that can be used as background for some view that does not fully fill
|
|
15
15
|
* the viewport.
|
|
16
16
|
*
|
|
17
17
|
* This dimming view has one more additional feature: it blocks gestures if its alpha > 0.
|
|
@@ -40,6 +40,8 @@ class DimmingView(
|
|
|
40
40
|
b: Int,
|
|
41
41
|
) = Unit
|
|
42
42
|
|
|
43
|
+
// We do not want to have any action defined here. We just want listeners notified that the click happened.
|
|
44
|
+
@SuppressLint("ClickableViewAccessibility")
|
|
43
45
|
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
|
44
46
|
if (blockGestures) {
|
|
45
47
|
callOnClick()
|