react-native-screens 4.2.0 → 4.3.0
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/src/main/java/com/swmansion/rnscreens/InsetsObserverProxy.kt +47 -7
- package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +4 -0
- package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +20 -3
- package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/DimmingFragment.kt +2 -2
- package/package.json +1 -1
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
package com.swmansion.rnscreens
|
|
2
2
|
|
|
3
|
+
import android.util.Log
|
|
3
4
|
import android.view.View
|
|
4
5
|
import androidx.core.view.OnApplyWindowInsetsListener
|
|
5
6
|
import androidx.core.view.ViewCompat
|
|
6
7
|
import androidx.core.view.WindowInsetsCompat
|
|
8
|
+
import com.facebook.react.bridge.LifecycleEventListener
|
|
9
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
10
|
import java.lang.ref.WeakReference
|
|
8
11
|
|
|
9
|
-
object InsetsObserverProxy : OnApplyWindowInsetsListener {
|
|
12
|
+
object InsetsObserverProxy : OnApplyWindowInsetsListener, LifecycleEventListener {
|
|
10
13
|
private val listeners: ArrayList<OnApplyWindowInsetsListener> = arrayListOf()
|
|
11
14
|
private var eventSourceView: WeakReference<View> = WeakReference(null)
|
|
12
15
|
|
|
@@ -15,8 +18,16 @@ object InsetsObserverProxy : OnApplyWindowInsetsListener {
|
|
|
15
18
|
// whether this observer has been initially registered.
|
|
16
19
|
private var hasBeenRegistered: Boolean = false
|
|
17
20
|
|
|
21
|
+
// Mainly debug variable to log warnings in case we missed some code path regarding
|
|
22
|
+
// context lifetime handling.
|
|
23
|
+
private var isObservingContextLifetime: Boolean = false
|
|
24
|
+
|
|
18
25
|
private var shouldForwardInsetsToView = true
|
|
19
26
|
|
|
27
|
+
// Allow only when we have not been registered yet or the view we're observing has been
|
|
28
|
+
// invalidated due to some lifecycle we have not observed.
|
|
29
|
+
private val allowRegistration get() = !hasBeenRegistered || eventSourceView.get() == null
|
|
30
|
+
|
|
20
31
|
override fun onApplyWindowInsets(
|
|
21
32
|
v: View,
|
|
22
33
|
insets: WindowInsetsCompat,
|
|
@@ -34,6 +45,34 @@ object InsetsObserverProxy : OnApplyWindowInsetsListener {
|
|
|
34
45
|
return rollingInsets
|
|
35
46
|
}
|
|
36
47
|
|
|
48
|
+
// Call this method to ensure that the observer proxy is
|
|
49
|
+
// unregistered when apps is destroyed or we change activity.
|
|
50
|
+
fun registerWithContext(context: ReactApplicationContext) {
|
|
51
|
+
if (isObservingContextLifetime) {
|
|
52
|
+
Log.w(
|
|
53
|
+
"[RNScreens]",
|
|
54
|
+
"InsetObserverProxy registers on new context while it has not been invalidated on the old one. Please report this as issue at https://github.com/software-mansion/react-native-screens/issues",
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
isObservingContextLifetime = true
|
|
59
|
+
context.addLifecycleEventListener(this)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
override fun onHostResume() = Unit
|
|
63
|
+
|
|
64
|
+
override fun onHostPause() = Unit
|
|
65
|
+
|
|
66
|
+
override fun onHostDestroy() {
|
|
67
|
+
val observedView = getObservedView()
|
|
68
|
+
if (hasBeenRegistered && observedView != null) {
|
|
69
|
+
ViewCompat.setOnApplyWindowInsetsListener(observedView, null)
|
|
70
|
+
hasBeenRegistered = false
|
|
71
|
+
eventSourceView.clear()
|
|
72
|
+
}
|
|
73
|
+
isObservingContextLifetime = false
|
|
74
|
+
}
|
|
75
|
+
|
|
37
76
|
fun addOnApplyWindowInsetsListener(listener: OnApplyWindowInsetsListener) {
|
|
38
77
|
listeners.add(listener)
|
|
39
78
|
}
|
|
@@ -42,16 +81,17 @@ object InsetsObserverProxy : OnApplyWindowInsetsListener {
|
|
|
42
81
|
listeners.remove(listener)
|
|
43
82
|
}
|
|
44
83
|
|
|
45
|
-
|
|
46
|
-
|
|
84
|
+
/**
|
|
85
|
+
* @return boolean whether the proxy registered as a listener on a view
|
|
86
|
+
*/
|
|
87
|
+
fun registerOnView(view: View): Boolean {
|
|
88
|
+
if (allowRegistration) {
|
|
47
89
|
ViewCompat.setOnApplyWindowInsetsListener(view, this)
|
|
48
90
|
eventSourceView = WeakReference(view)
|
|
49
91
|
hasBeenRegistered = true
|
|
50
|
-
|
|
51
|
-
throw IllegalStateException(
|
|
52
|
-
"[RNScreens] Attempt to register InsetsObserverProxy on $view while it has been already registered on ${getObservedView()}",
|
|
53
|
-
)
|
|
92
|
+
return true
|
|
54
93
|
}
|
|
94
|
+
return false
|
|
55
95
|
}
|
|
56
96
|
|
|
57
97
|
fun unregister() {
|
|
@@ -29,6 +29,10 @@ class RNScreensPackage : TurboReactPackage() {
|
|
|
29
29
|
screenDummyLayoutHelper = ScreenDummyLayoutHelper(reactContext)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// Proxy needs to register for lifecycle events in order to unregister itself
|
|
33
|
+
// on activity restarts.
|
|
34
|
+
InsetsObserverProxy.registerWithContext(reactContext)
|
|
35
|
+
|
|
32
36
|
return listOf<ViewManager<*, *>>(
|
|
33
37
|
ScreenContainerViewManager(),
|
|
34
38
|
ScreenViewManager(),
|
|
@@ -17,6 +17,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
|
17
17
|
import com.facebook.react.bridge.GuardedRunnable
|
|
18
18
|
import com.facebook.react.bridge.ReactContext
|
|
19
19
|
import com.facebook.react.uimanager.PixelUtil
|
|
20
|
+
import com.facebook.react.uimanager.ReactClippingViewGroup
|
|
20
21
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
21
22
|
import com.facebook.react.uimanager.UIManagerModule
|
|
22
23
|
import com.facebook.react.uimanager.events.EventDispatcher
|
|
@@ -383,6 +384,7 @@ class Screen(
|
|
|
383
384
|
parent?.let {
|
|
384
385
|
for (i in 0 until it.childCount) {
|
|
385
386
|
val child = it.getChildAt(i)
|
|
387
|
+
|
|
386
388
|
if (parent is SwipeRefreshLayout && child is ImageView) {
|
|
387
389
|
// SwipeRefreshLayout class which has CircleImageView as a child,
|
|
388
390
|
// does not handle `startViewTransition` properly.
|
|
@@ -394,20 +396,35 @@ class Screen(
|
|
|
394
396
|
} else {
|
|
395
397
|
child?.let { view -> it.startViewTransition(view) }
|
|
396
398
|
}
|
|
399
|
+
|
|
397
400
|
if (child is ScreenStackHeaderConfig) {
|
|
398
401
|
// we want to start transition on children of the toolbar too,
|
|
399
402
|
// which is not a child of ScreenStackHeaderConfig
|
|
400
403
|
startTransitionRecursive(child.toolbar)
|
|
401
404
|
}
|
|
405
|
+
|
|
402
406
|
if (child is ViewGroup) {
|
|
403
407
|
// The children are miscounted when there's removeClippedSubviews prop
|
|
404
408
|
// set to true (which is the default for FlatLists).
|
|
405
409
|
// Unless the child is a ScrollView it's safe to assume that it's true
|
|
406
410
|
// and add a simple view for each possibly clipped item to make it work as expected.
|
|
407
411
|
// See https://github.com/software-mansion/react-native-screens/pull/2495
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
412
|
+
|
|
413
|
+
if (child is ReactClippingViewGroup &&
|
|
414
|
+
child.removeClippedSubviews &&
|
|
415
|
+
child !is ReactScrollView &&
|
|
416
|
+
child !is ReactHorizontalScrollView
|
|
417
|
+
) {
|
|
418
|
+
// We need to workaround the issue until our changes land in core.
|
|
419
|
+
// Some views do not accept any children or have set amount and they throw
|
|
420
|
+
// when we want to brute-forcefully manipulate that.
|
|
421
|
+
// Is this ugly? Very. Do we have better option before changes land in core?
|
|
422
|
+
// I'm not aware of any.
|
|
423
|
+
try {
|
|
424
|
+
for (j in 0 until child.childCount) {
|
|
425
|
+
child.addView(View(context))
|
|
426
|
+
}
|
|
427
|
+
} catch (_: Exception) {
|
|
411
428
|
}
|
|
412
429
|
}
|
|
413
430
|
startTransitionRecursive(child)
|
|
@@ -208,7 +208,7 @@ class DimmingFragment(
|
|
|
208
208
|
override fun onStart() {
|
|
209
209
|
// This is the earliest we can access child fragment manager & present another fragment
|
|
210
210
|
super.onStart()
|
|
211
|
-
insetsProxy.registerOnView(
|
|
211
|
+
insetsProxy.registerOnView(requireDecorView())
|
|
212
212
|
presentNestedFragment()
|
|
213
213
|
}
|
|
214
214
|
|
|
@@ -307,7 +307,7 @@ class DimmingFragment(
|
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
-
private fun
|
|
310
|
+
private fun requireDecorView(): View =
|
|
311
311
|
checkNotNull(screen.reactContext.currentActivity) { "[RNScreens] Attempt to access activity on detached context" }
|
|
312
312
|
.window.decorView
|
|
313
313
|
|
package/package.json
CHANGED