react-native-screens 3.35.0-rc.0 → 3.35.0-rc.1
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.
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
package com.swmansion.rnscreens.utils
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
|
+
import android.content.Context
|
|
4
5
|
import android.util.Log
|
|
5
6
|
import android.view.View
|
|
6
7
|
import androidx.appcompat.widget.Toolbar
|
|
7
8
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
9
|
+
import com.facebook.jni.annotations.DoNotStrip
|
|
8
10
|
import com.facebook.react.bridge.LifecycleEventListener
|
|
9
11
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
10
12
|
import com.facebook.react.uimanager.PixelUtil
|
|
@@ -18,6 +20,7 @@ import java.lang.ref.WeakReference
|
|
|
18
20
|
* See https://github.com/software-mansion/react-native-screens/pull/2169
|
|
19
21
|
* for more detailed description of the issue this code solves.
|
|
20
22
|
*/
|
|
23
|
+
@DoNotStrip
|
|
21
24
|
internal class ScreenDummyLayoutHelper(
|
|
22
25
|
reactContext: ReactApplicationContext,
|
|
23
26
|
) : LifecycleEventListener {
|
|
@@ -46,7 +49,7 @@ internal class ScreenDummyLayoutHelper(
|
|
|
46
49
|
try {
|
|
47
50
|
System.loadLibrary(LIBRARY_NAME)
|
|
48
51
|
} catch (e: UnsatisfiedLinkError) {
|
|
49
|
-
Log.w(TAG, "Failed to load $LIBRARY_NAME")
|
|
52
|
+
Log.w(TAG, "[RNScreens] Failed to load $LIBRARY_NAME library.")
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
weakInstance = WeakReference(this)
|
|
@@ -57,9 +60,13 @@ internal class ScreenDummyLayoutHelper(
|
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
/**
|
|
60
|
-
*
|
|
63
|
+
* Tries to initialize dummy view hierarchy with CoordinatorLayout, AppBarLayout and dummy View.
|
|
61
64
|
* We utilize this to compute header height (app bar layout height) from C++ layer when its needed.
|
|
62
65
|
*
|
|
66
|
+
* This method might fail in case there is activity attached to the react context.
|
|
67
|
+
*
|
|
68
|
+
* This method is called from various threads!
|
|
69
|
+
*
|
|
63
70
|
* @return boolean whether the layout was initialised or not
|
|
64
71
|
*/
|
|
65
72
|
private fun maybeInitDummyLayoutWithHeader(reactContext: ReactApplicationContext): Boolean {
|
|
@@ -67,6 +74,7 @@ internal class ScreenDummyLayoutHelper(
|
|
|
67
74
|
return true
|
|
68
75
|
}
|
|
69
76
|
|
|
77
|
+
// Possible data race here - activity is injected into context on UI thread.
|
|
70
78
|
if (!reactContext.hasCurrentActivity()) {
|
|
71
79
|
return false
|
|
72
80
|
}
|
|
@@ -74,8 +82,25 @@ internal class ScreenDummyLayoutHelper(
|
|
|
74
82
|
// We need to use activity here, as react context does not have theme attributes required by
|
|
75
83
|
// AppBarLayout attached leading to crash.
|
|
76
84
|
val contextWithTheme =
|
|
77
|
-
requireNotNull(reactContext.currentActivity) { "[RNScreens] Attempt to use context detached from activity" }
|
|
85
|
+
requireNotNull(reactContext.currentActivity) { "[RNScreens] Attempt to use context detached from activity. This could happen only due to race-condition." }
|
|
86
|
+
|
|
87
|
+
synchronized(this) {
|
|
88
|
+
// The layout could have been initialised when this thread waited for access to critical section.
|
|
89
|
+
if (isLayoutInitialized) {
|
|
90
|
+
return true
|
|
91
|
+
}
|
|
92
|
+
initDummyLayoutWithHeader(contextWithTheme)
|
|
93
|
+
}
|
|
94
|
+
return true
|
|
95
|
+
}
|
|
78
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Initialises the dummy layout. This method is **not** thread-safe.
|
|
99
|
+
*
|
|
100
|
+
* @param contextWithTheme this function expects the context to have theme attributes required
|
|
101
|
+
* to initialize the AppBarLayout.
|
|
102
|
+
*/
|
|
103
|
+
private fun initDummyLayoutWithHeader(contextWithTheme: Context) {
|
|
79
104
|
coordinatorLayout = CoordinatorLayout(contextWithTheme)
|
|
80
105
|
|
|
81
106
|
appBarLayout =
|
|
@@ -119,7 +144,6 @@ internal class ScreenDummyLayoutHelper(
|
|
|
119
144
|
}
|
|
120
145
|
|
|
121
146
|
isLayoutInitialized = true
|
|
122
|
-
return true
|
|
123
147
|
}
|
|
124
148
|
|
|
125
149
|
/**
|
|
@@ -129,6 +153,7 @@ internal class ScreenDummyLayoutHelper(
|
|
|
129
153
|
* @param fontSize font size value as passed from JS
|
|
130
154
|
* @return header height in dp as consumed by Yoga
|
|
131
155
|
*/
|
|
156
|
+
@DoNotStrip
|
|
132
157
|
private fun computeDummyLayout(
|
|
133
158
|
fontSize: Int,
|
|
134
159
|
isTitleEmpty: Boolean,
|
|
@@ -141,7 +166,7 @@ internal class ScreenDummyLayoutHelper(
|
|
|
141
166
|
// is still null at this execution point. We don't wanna crash in such case, thus returning zeroed height.
|
|
142
167
|
Log.e(
|
|
143
168
|
TAG,
|
|
144
|
-
"[RNScreens] Failed to late-init layout while computing header height. This is a race-condition-bug in react-native-screens, please file an issue at https://github.com/software-mansion/react-native-screens/issues"
|
|
169
|
+
"[RNScreens] Failed to late-init layout while computing header height. This is most likely a race-condition-bug in react-native-screens, please file an issue at https://github.com/software-mansion/react-native-screens/issues"
|
|
145
170
|
)
|
|
146
171
|
return 0.0f
|
|
147
172
|
}
|
|
@@ -210,22 +235,34 @@ internal class ScreenDummyLayoutHelper(
|
|
|
210
235
|
// dummy view hierarchy.
|
|
211
236
|
private var weakInstance = WeakReference<ScreenDummyLayoutHelper>(null)
|
|
212
237
|
|
|
238
|
+
@DoNotStrip
|
|
213
239
|
@JvmStatic
|
|
214
240
|
fun getInstance(): ScreenDummyLayoutHelper? = weakInstance.get()
|
|
215
241
|
}
|
|
216
242
|
|
|
243
|
+
// This value is fetched / stored from UI and background thread. Volatile here ensures
|
|
244
|
+
// that updates are visible to the other thread.
|
|
245
|
+
@Volatile
|
|
217
246
|
private var isLayoutInitialized = false
|
|
218
247
|
|
|
219
248
|
override fun onHostResume() {
|
|
220
249
|
// This is the earliest we have guarantee that the context has a reference to an activity.
|
|
221
250
|
val reactContext = requireReactContext { "[RNScreens] ReactContext missing in onHostResume! This should not happen." }
|
|
222
|
-
|
|
223
|
-
|
|
251
|
+
|
|
252
|
+
// There are some exotic edge cases where activity might not be present in context
|
|
253
|
+
// at this point, e.g. when reloading RN in development after an error was reported with redbox.
|
|
254
|
+
if (maybeInitDummyLayoutWithHeader(reactContext)) {
|
|
255
|
+
reactContext.removeLifecycleEventListener(this)
|
|
256
|
+
} else {
|
|
257
|
+
Log.w(TAG, "[RNScreens] Failed to initialise dummy layout in onHostResume.")
|
|
258
|
+
}
|
|
224
259
|
}
|
|
225
260
|
|
|
226
261
|
override fun onHostPause() = Unit
|
|
227
262
|
|
|
228
|
-
override fun onHostDestroy()
|
|
263
|
+
override fun onHostDestroy() {
|
|
264
|
+
reactContextRef.get()?.removeLifecycleEventListener(this)
|
|
265
|
+
}
|
|
229
266
|
}
|
|
230
267
|
|
|
231
268
|
private data class CacheKey(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-screens",
|
|
3
|
-
"version": "3.35.0-rc.
|
|
3
|
+
"version": "3.35.0-rc.1",
|
|
4
4
|
"description": "Native navigation primitives for your React Native app.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"submodules": "git submodule update --init --recursive && (cd react-navigation && yarn)",
|