react-native-unistyles 3.0.0-alpha.36 → 3.0.0-alpha.38

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. package/Unistyles.podspec +1 -1
  2. package/android/CMakeLists.txt +3 -15
  3. package/android/src/main/cxx/NativeUnistylesModule.cpp +1 -1
  4. package/android/src/main/java/com/unistyles/Equatable.kt +61 -0
  5. package/android/src/main/java/com/unistyles/NativePlatform+android.kt +302 -0
  6. package/android/src/main/java/com/unistyles/NativePlatform+insets.kt +148 -0
  7. package/android/src/main/java/com/unistyles/NativePlatform+listener.kt +54 -0
  8. package/android/src/main/java/com/unistyles/UnistylesModule.kt +5 -1
  9. package/cxx/core/UnistyleData.h +1 -1
  10. package/cxx/core/UnistyleWrapper.h +1 -2
  11. package/cxx/core/UnistylesCommitHook.cpp +1 -1
  12. package/cxx/core/UnistylesMountHook.cpp +1 -1
  13. package/cxx/core/UnistylesRegistry.cpp +4 -13
  14. package/cxx/core/UnistylesRegistry.h +1 -2
  15. package/cxx/core/UnistylesState.cpp +10 -7
  16. package/cxx/hybridObjects/HybridStyleSheet.cpp +21 -18
  17. package/cxx/hybridObjects/HybridUnistylesRuntime.cpp +0 -4
  18. package/cxx/hybridObjects/HybridUnistylesRuntime.h +0 -1
  19. package/cxx/parser/Parser.cpp +3 -27
  20. package/cxx/parser/Parser.h +2 -3
  21. package/cxx/shadowTree/ShadowTrafficController.h +9 -5
  22. package/cxx/shadowTree/ShadowTreeManager.cpp +10 -5
  23. package/cxx/shadowTree/ShadowTreeManager.h +1 -1
  24. package/lib/commonjs/core/createUnistylesComponent.native.js +1 -1
  25. package/lib/commonjs/core/createUnistylesComponent.native.js.map +1 -1
  26. package/lib/module/core/createUnistylesComponent.native.js +1 -1
  27. package/lib/module/core/createUnistylesComponent.native.js.map +1 -1
  28. package/package.json +3 -3
  29. package/plugin/common.js +7 -1
  30. package/src/core/createUnistylesComponent.native.tsx +1 -1
  31. package/android/src/main/java/com/unistyles/NativePlatform.kt +0 -184
  32. package/android/src/main/java/com/unistyles/UnistylesModule+insets.kt +0 -8
package/Unistyles.podspec CHANGED
@@ -19,7 +19,7 @@ Pod::Spec.new do |s|
19
19
  ]
20
20
  s.pod_target_xcconfig = {
21
21
  "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
22
- "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES"
22
+ "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES FOLLY_MOBILE"
23
23
  }
24
24
 
25
25
  s.public_header_files = [
@@ -23,7 +23,7 @@ include_directories(
23
23
  ../cxx/shadowTree
24
24
  )
25
25
 
26
- string(APPEND CMAKE_CXX_FLAGS "-DFOLLY_NO_CONFIG")
26
+ string(APPEND CMAKE_CXX_FLAGS "-DFOLLY_NO_CONFIG=1 -DFOLLY_CFG_NO_COROUTINES=1 -DFOLLY_MOBILE=1")
27
27
 
28
28
  set_target_properties(unistyles PROPERTIES
29
29
  CXX_STANDARD 20
@@ -32,17 +32,5 @@ set_target_properties(unistyles PROPERTIES
32
32
  POSITION_INDEPENDENT_CODE ON
33
33
  )
34
34
 
35
- # todo included by nitrogen
36
- # find_package(ReactAndroid REQUIRED CONFIG)
37
- # find_package(fbjni REQUIRED CONFIG)
38
-
39
- #if (ReactAndroid_VERSION_MINOR GREATER_EQUAL 76)
40
- # target_link_libraries(unistyles ReactAndroid::reactnative)
41
- #else()
42
- # target_link_libraries(unistyles
43
- # ReactAndroid::turbomodulejsijni
44
- # ReactAndroid::react_nativemodule_core
45
- # android
46
- # fbjni::fbjni
47
- # )
48
- #endif()
35
+ # For React Native 0.76 and above, we don't need to link anything
36
+ # as NitroModules will automatically add ReactAndroid::reactnative prefab
@@ -49,7 +49,7 @@ jni::local_ref<BindingsInstallerHolder::javaobject> UnistylesModule::getBindings
49
49
  }
50
50
 
51
51
  auto runOnJSThread = [&runtimeExecutor](std::function<void(jsi::Runtime&)>&& callback) {
52
- runtimeExecutor([&](jsi::Runtime &rt) {
52
+ runtimeExecutor([callback = std::move(callback)](jsi::Runtime &rt) {
53
53
  callback(rt);
54
54
  });
55
55
  };
@@ -0,0 +1,61 @@
1
+ package com.unistyles
2
+
3
+ import com.margelo.nitro.unistyles.Dimensions
4
+ import com.margelo.nitro.unistyles.Insets
5
+ import com.margelo.nitro.unistyles.UnistyleDependency
6
+ import com.margelo.nitro.unistyles.UnistylesNativeMiniRuntime
7
+
8
+ fun Dimensions.isEqualTo(other: Dimensions): Boolean {
9
+ return this.width == other.width && this.height == other.height
10
+ }
11
+
12
+ fun Insets.isEqualTo(other: Insets): Boolean {
13
+ return this.top == other.top && this.bottom == other.bottom &&
14
+ this.left == other.left && this.right == other.right &&
15
+ this.ime == other.ime
16
+ }
17
+
18
+ fun NativePlatformAndroid.diffMiniRuntimes(lhs: UnistylesNativeMiniRuntime, rhs: UnistylesNativeMiniRuntime): Array<UnistyleDependency> {
19
+ val dependencies: MutableList<UnistyleDependency> = mutableListOf()
20
+
21
+ if (lhs.colorScheme != rhs.colorScheme) {
22
+ dependencies.add(UnistyleDependency.COLORSCHEME)
23
+ }
24
+
25
+ if (!lhs.screen.isEqualTo(rhs.screen)) {
26
+ dependencies.add(UnistyleDependency.DIMENSIONS)
27
+ }
28
+
29
+ if (lhs.screen.width != rhs.screen.width) {
30
+ dependencies.add(UnistyleDependency.BREAKPOINTS)
31
+ }
32
+
33
+ // no need to check isLandscape, as it's always opposite
34
+ if (lhs.isPortrait != rhs.isPortrait) {
35
+ dependencies.add(UnistyleDependency.ORIENTATION)
36
+ }
37
+
38
+ if (lhs.contentSizeCategory != rhs.contentSizeCategory) {
39
+ dependencies.add(UnistyleDependency.CONTENTSIZECATEGORY)
40
+ }
41
+
42
+ if (!lhs.insets.isEqualTo(rhs.insets)) {
43
+ dependencies.add(UnistyleDependency.INSETS)
44
+ }
45
+
46
+ if (lhs.fontScale != rhs.fontScale) {
47
+ dependencies.add(UnistyleDependency.FONTSCALE)
48
+ }
49
+
50
+ if (!lhs.statusBar.isEqualTo(rhs.statusBar)) {
51
+ dependencies.add(UnistyleDependency.STATUSBAR)
52
+ }
53
+
54
+ if (!lhs.navigationBar.isEqualTo(rhs.navigationBar)) {
55
+ dependencies.add(UnistyleDependency.NAVIGATIONBAR)
56
+ }
57
+
58
+ // rtl and pixel ratio are not dynamic
59
+
60
+ return dependencies.toTypedArray()
61
+ }
@@ -0,0 +1,302 @@
1
+ package com.unistyles
2
+
3
+ import android.content.Context
4
+ import android.content.res.Configuration
5
+ import android.os.Build
6
+ import android.util.DisplayMetrics
7
+ import android.view.View
8
+ import android.view.WindowManager
9
+ import androidx.core.text.TextUtilsCompat
10
+ import androidx.core.view.ViewCompat
11
+ import androidx.core.view.WindowCompat
12
+ import androidx.core.view.WindowInsetsCompat
13
+ import androidx.core.view.WindowInsetsControllerCompat
14
+ import com.facebook.react.bridge.LifecycleEventListener
15
+ import com.facebook.react.bridge.ReactApplicationContext
16
+ import com.margelo.nitro.unistyles.ColorScheme
17
+ import com.margelo.nitro.unistyles.Dimensions
18
+ import com.margelo.nitro.unistyles.HybridNativePlatformSpec
19
+ import com.margelo.nitro.unistyles.Insets
20
+ import com.margelo.nitro.unistyles.Orientation
21
+ import com.margelo.nitro.unistyles.UnistyleDependency
22
+ import com.margelo.nitro.unistyles.UnistylesNativeMiniRuntime
23
+ import java.util.Locale
24
+
25
+ class NativePlatformAndroid(private val reactContext: ReactApplicationContext): HybridNativePlatformSpec(), LifecycleEventListener {
26
+ private val _insets = NativePlatformInsets(reactContext) { this.diffMiniRuntime() }
27
+ private var _miniRuntime: UnistylesNativeMiniRuntime = buildMiniRuntime()
28
+ private val _listener = NativePlatformListener(reactContext) { this.diffMiniRuntime() }
29
+
30
+ init {
31
+ reactContext.addLifecycleEventListener(this)
32
+ }
33
+
34
+ fun invalidate() {
35
+ reactContext.removeLifecycleEventListener(this)
36
+ _listener.invalidate()
37
+ }
38
+
39
+ override fun onHostResume() {
40
+ enableEdgeToEdge()
41
+ _insets.startInsetsListener()
42
+ }
43
+
44
+ override fun onHostPause() {
45
+ _insets.stopInsetsListener()
46
+ }
47
+
48
+ override fun onHostDestroy() {}
49
+
50
+ override val memorySize: Long
51
+ get() = 0
52
+
53
+ override fun getInsets(): Insets {
54
+ return _insets.getInsets()
55
+ }
56
+
57
+ override fun getColorScheme(): ColorScheme {
58
+ val uiMode = reactContext.resources.configuration.uiMode
59
+
60
+ val colorScheme = when (uiMode.and(Configuration.UI_MODE_NIGHT_MASK)) {
61
+ Configuration.UI_MODE_NIGHT_YES -> ColorScheme.DARK
62
+ Configuration.UI_MODE_NIGHT_NO -> ColorScheme.LIGHT
63
+ else -> ColorScheme.UNSPECIFIED
64
+ }
65
+
66
+ return colorScheme
67
+ }
68
+
69
+ override fun getFontScale(): Double {
70
+ return reactContext.resources.configuration.fontScale.toDouble()
71
+ }
72
+
73
+ override fun getPixelRatio(): Double {
74
+ return reactContext.resources.displayMetrics.density.toDouble()
75
+ }
76
+
77
+ override fun getOrientation(): Orientation {
78
+ val orientation = when (reactContext.resources.configuration.orientation) {
79
+ Configuration.ORIENTATION_PORTRAIT -> Orientation.PORTRAIT
80
+ Configuration.ORIENTATION_LANDSCAPE -> Orientation.LANDSCAPE
81
+ else -> Orientation.PORTRAIT
82
+ }
83
+
84
+ return orientation
85
+ }
86
+
87
+ override fun getContentSizeCategory(): String {
88
+ val fontScale = getFontScale()
89
+
90
+ val contentSizeCategory = when {
91
+ fontScale <= 0.85f -> "Small"
92
+ fontScale <= 1.0f -> "Default"
93
+ fontScale <= 1.15f -> "Large"
94
+ fontScale <= 1.3f -> "ExtraLarge"
95
+ fontScale <= 1.5f -> "Huge"
96
+ fontScale <= 1.8 -> "ExtraHuge"
97
+ else -> "ExtraExtraHuge"
98
+ }
99
+
100
+ return contentSizeCategory
101
+ }
102
+
103
+ override fun getScreenDimensions(): Dimensions {
104
+ // function takes in count edge-to-edge layout
105
+ when {
106
+ Build.VERSION.SDK_INT < Build.VERSION_CODES.R -> {
107
+ val windowManager = reactContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
108
+ val metrics = DisplayMetrics()
109
+
110
+ @Suppress("DEPRECATION")
111
+ windowManager.defaultDisplay.getRealMetrics(metrics)
112
+
113
+ val screenWidth = (metrics.widthPixels / metrics.density).toDouble()
114
+ val screenHeight = (metrics.heightPixels / metrics.density).toDouble()
115
+
116
+ return Dimensions(screenWidth, screenHeight)
117
+ }
118
+ else -> {
119
+ val displayMetrics = reactContext.resources.displayMetrics
120
+
121
+ reactContext.currentActivity?.windowManager?.currentWindowMetrics?.bounds?.let {
122
+ val boundsWidth = (it.width() / displayMetrics.density).toDouble()
123
+ val boundsHeight = (it.height() / displayMetrics.density).toDouble()
124
+
125
+ return Dimensions(boundsWidth, boundsHeight)
126
+ } ?: run {
127
+ val screenWidth = (displayMetrics.widthPixels / displayMetrics.density).toDouble()
128
+ val screenHeight = (displayMetrics.heightPixels / displayMetrics.density).toDouble()
129
+
130
+ return Dimensions(screenWidth, screenHeight)
131
+ }
132
+ }
133
+ }
134
+ }
135
+
136
+ override fun getStatusBarDimensions(): Dimensions {
137
+ val screenWidth = getScreenDimensions().width
138
+
139
+ return Dimensions(screenWidth, _insets.getInsets().top)
140
+ }
141
+
142
+ override fun getNavigationBarDimensions(): Dimensions {
143
+ val screenWidth = getScreenDimensions().width
144
+
145
+ return Dimensions(screenWidth, _insets.getInsets().bottom)
146
+ }
147
+
148
+ override fun getPrefersRtlDirection(): Boolean {
149
+ // forced by React Native
150
+ val sharedPrefs = reactContext.getSharedPreferences(
151
+ "com.facebook.react.modules.i18nmanager.I18nUtil",
152
+ Context.MODE_PRIVATE
153
+ )
154
+ val hasForcedRtl = sharedPrefs.getBoolean("RCTI18nUtil_forceRTL", false)
155
+ // user preferences
156
+ val isRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL
157
+
158
+ return hasForcedRtl || isRtl
159
+ }
160
+
161
+ override fun setRootViewBackgroundColor(color: Double) {
162
+ reactContext.currentActivity?.let { activity ->
163
+ activity.window?.decorView?.let { decorView ->
164
+ activity.runOnUiThread {
165
+ decorView.rootView.setBackgroundColor(color.toInt())
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ @Suppress("DEPRECATION")
172
+ override fun setNavigationBarBackgroundColor(color: Double) {
173
+ reactContext.currentActivity?.let { activity ->
174
+ activity.runOnUiThread {
175
+ activity.window.navigationBarColor = color.toInt()
176
+ }
177
+ }
178
+ }
179
+
180
+ override fun setNavigationBarHidden(isHidden: Boolean) {
181
+ reactContext.currentActivity?.let { activity ->
182
+ WindowInsetsControllerCompat(activity.window, activity.window.decorView).apply {
183
+ activity.window?.decorView?.let { decorView ->
184
+ @Suppress("DEPRECATION")
185
+ activity.runOnUiThread {
186
+ if (isHidden) {
187
+ // below Android 11, we need to use window flags to hide the navigation bar
188
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
189
+ decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
190
+ or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
191
+ } else {
192
+ hide(WindowInsetsCompat.Type.navigationBars())
193
+ systemBarsBehavior =
194
+ WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
195
+ }
196
+
197
+ // dispatch new insets to invoke the insets listener
198
+ val newInsets = WindowInsetsCompat.Builder()
199
+ .setInsets(WindowInsetsCompat.Type.navigationBars(), androidx.core.graphics.Insets.of(0, 0, 0, 0))
200
+ .build()
201
+
202
+ ViewCompat.dispatchApplyWindowInsets(activity.findViewById(android.R.id.content), newInsets)
203
+ } else {
204
+ show(WindowInsetsCompat.Type.navigationBars())
205
+ }
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ override fun setStatusBarHidden(isHidden: Boolean) {
213
+ reactContext.currentActivity?.let { activity ->
214
+ WindowInsetsControllerCompat(activity.window, activity.window.decorView).apply {
215
+ activity.window?.let { window ->
216
+ @Suppress("DEPRECATION")
217
+ activity.runOnUiThread {
218
+ if (isHidden) {
219
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
220
+ window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
221
+ window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
222
+ } else {
223
+ hide(WindowInsetsCompat.Type.statusBars())
224
+ }
225
+ } else {
226
+ show(WindowInsetsCompat.Type.statusBars())
227
+ }
228
+ }
229
+ }
230
+ }
231
+ }
232
+ }
233
+
234
+ @Suppress("DEPRECATION")
235
+ override fun setStatusBarBackgroundColor(color: Double) {
236
+ reactContext.currentActivity?.let { activity ->
237
+ activity.runOnUiThread {
238
+ activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
239
+ activity.window.statusBarColor = color.toInt()
240
+ }
241
+ }
242
+ }
243
+
244
+ override fun setImmersiveMode(isEnabled: Boolean) {
245
+ this.setStatusBarHidden(isEnabled)
246
+ this.setNavigationBarHidden(isEnabled)
247
+ }
248
+
249
+ override fun getMiniRuntime(): UnistylesNativeMiniRuntime {
250
+ return _miniRuntime
251
+ }
252
+
253
+ private fun buildMiniRuntime(): UnistylesNativeMiniRuntime {
254
+ val orientation = this.getOrientation()
255
+
256
+ return UnistylesNativeMiniRuntime(
257
+ colorScheme = this.getColorScheme(),
258
+ screen = this.getScreenDimensions(),
259
+ contentSizeCategory = this.getContentSizeCategory(),
260
+ insets = this.getInsets(),
261
+ pixelRatio = this.getPixelRatio(),
262
+ fontScale = this.getFontScale(),
263
+ rtl = this.getPrefersRtlDirection(),
264
+ statusBar = this.getStatusBarDimensions(),
265
+ navigationBar = this.getNavigationBarDimensions(),
266
+ isPortrait = orientation == Orientation.PORTRAIT,
267
+ isLandscape = orientation == Orientation.LANDSCAPE
268
+ )
269
+ }
270
+
271
+ private fun diffMiniRuntime(): Array<UnistyleDependency> {
272
+ val newMiniRuntime = this.buildMiniRuntime()
273
+ val changedDependencies = diffMiniRuntimes(this._miniRuntime, newMiniRuntime)
274
+
275
+ if (changedDependencies.isNotEmpty()) {
276
+ this._miniRuntime = newMiniRuntime
277
+ }
278
+
279
+ return changedDependencies
280
+ }
281
+
282
+ override fun registerPlatformListener(callback: (dependencies: Array<UnistyleDependency>) -> Unit) {
283
+ this._listener.addPlatformListener(callback)
284
+ }
285
+
286
+ override fun registerImeListener(callback: () -> Unit) {
287
+ this._insets.addImeListener(callback)
288
+ }
289
+
290
+ override fun unregisterPlatformListeners() {
291
+ this._listener.removePlatformListeners()
292
+ this._insets.removeImeListeners()
293
+ }
294
+
295
+ private fun enableEdgeToEdge() {
296
+ reactContext.currentActivity?.let { activity ->
297
+ activity.runOnUiThread {
298
+ WindowCompat.setDecorFitsSystemWindows(activity.window, false)
299
+ }
300
+ }
301
+ }
302
+ }
@@ -0,0 +1,148 @@
1
+ package com.unistyles
2
+
3
+ import android.graphics.Rect
4
+ import android.os.Build
5
+ import android.view.View
6
+ import android.view.Window
7
+ import android.view.WindowManager
8
+ import androidx.core.view.ViewCompat
9
+ import androidx.core.view.WindowInsetsAnimationCompat
10
+ import androidx.core.view.WindowInsetsCompat
11
+ import com.facebook.react.bridge.ReactApplicationContext
12
+ import com.margelo.nitro.unistyles.Insets
13
+ import com.margelo.nitro.unistyles.UnistyleDependency
14
+
15
+ typealias CxxImeListener = () -> Unit
16
+
17
+ class NativePlatformInsets(private val reactContext: ReactApplicationContext, private val diffMiniRuntime: () -> Array<UnistyleDependency>) {
18
+ private val _imeListeners: MutableList<CxxImeListener> = mutableListOf()
19
+ private var _insets: Insets = Insets(0.0, 0.0, 0.0, 0.0, 0.0)
20
+
21
+ fun getInsets(): Insets {
22
+ val density = reactContext.resources.displayMetrics.density
23
+
24
+ return Insets(
25
+ this._insets.top / density,
26
+ this._insets.bottom / density,
27
+ this._insets.left / density,
28
+ this._insets.right / density,
29
+ this._insets.ime / density
30
+ )
31
+ }
32
+
33
+ fun setInsets(insetsCompat: WindowInsetsCompat, window: Window, animatedBottomInsets: Double?) {
34
+ // below Android 11, we need to use window flags to detect status bar visibility
35
+ val isStatusBarVisible = when(Build.VERSION.SDK_INT) {
36
+ in 30..Int.MAX_VALUE -> {
37
+ insetsCompat.isVisible(WindowInsetsCompat.Type.statusBars())
38
+ }
39
+ else -> {
40
+ @Suppress("DEPRECATION")
41
+ window.attributes.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN != WindowManager.LayoutParams.FLAG_FULLSCREEN
42
+ }
43
+ }
44
+ // React Native is forcing insets to make status bar translucent
45
+ // so we need to calculate top inset manually, as WindowInsetCompat will always return 0
46
+ val statusBarTopInset = when(isStatusBarVisible) {
47
+ true -> {
48
+ val visibleRect = Rect()
49
+
50
+ window.decorView.getWindowVisibleDisplayFrame(visibleRect)
51
+
52
+ visibleRect.top
53
+ }
54
+ false -> 0
55
+ }
56
+
57
+ val insets = insetsCompat.getInsets(WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout())
58
+
59
+ // Android 10 and below - set bottom insets to 0 while keyboard is visible and use default bottom insets otherwise
60
+ // Android 11 and above - animate bottom insets while keyboard is appearing and disappearing
61
+ val imeInsets = if (Build.VERSION.SDK_INT >= 30) {
62
+ animatedBottomInsets ?: this._insets.ime
63
+ } else {
64
+ val nextBottomInset = insetsCompat.getInsets(WindowInsetsCompat.Type.ime()).bottom - insets.bottom
65
+
66
+ // call new IME event here, as for SDK >= 30 it's called in AnimationCallback
67
+ this@NativePlatformInsets.emitImeEvent()
68
+
69
+ if (nextBottomInset < 0) {
70
+ 0
71
+ } else {
72
+ nextBottomInset
73
+ }
74
+ }
75
+
76
+ this._insets = Insets(
77
+ statusBarTopInset.toDouble(),
78
+ insets.bottom.toDouble(),
79
+ insets.left.toDouble(),
80
+ insets.right.toDouble(),
81
+ imeInsets.toDouble()
82
+ )
83
+
84
+ diffMiniRuntime()
85
+ }
86
+
87
+ fun startInsetsListener() {
88
+ reactContext.currentActivity?.let { activity ->
89
+ activity.findViewById<View>(android.R.id.content)?.let { mainView ->
90
+ ViewCompat.setOnApplyWindowInsetsListener(mainView) { _, insets ->
91
+ setInsets(insets, activity.window, null)
92
+
93
+ insets
94
+ }
95
+
96
+ // IME insets are available from Android 11
97
+ if (Build.VERSION.SDK_INT >= 30) {
98
+ ViewCompat.setWindowInsetsAnimationCallback(
99
+ mainView,
100
+ object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
101
+ override fun onProgress(
102
+ insets: WindowInsetsCompat,
103
+ runningAnimations: List<WindowInsetsAnimationCompat>
104
+ ): WindowInsetsCompat {
105
+ runningAnimations.firstOrNull()?.let {
106
+ val bottomInset = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom.toDouble() - this@NativePlatformInsets._insets.bottom
107
+ val nextBottomInset = if (bottomInset < 0) {
108
+ 0.0
109
+ } else {
110
+ bottomInset
111
+ }
112
+
113
+ this@NativePlatformInsets.setInsets(insets, activity.window, nextBottomInset)
114
+ this@NativePlatformInsets.emitImeEvent()
115
+ }
116
+
117
+ return insets
118
+ }
119
+ }
120
+ )
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ fun emitImeEvent() {
127
+ _imeListeners.forEach { listener ->
128
+ listener()
129
+ }
130
+ }
131
+
132
+ fun stopInsetsListener() {
133
+ reactContext.currentActivity?.let { activity ->
134
+ activity.window?.decorView?.let { view ->
135
+ ViewCompat.setOnApplyWindowInsetsListener(view, null)
136
+ ViewCompat.setWindowInsetsAnimationCallback(view, null)
137
+ }
138
+ }
139
+ }
140
+
141
+ fun addImeListener(listener: CxxImeListener) {
142
+ this._imeListeners.add(listener)
143
+ }
144
+
145
+ fun removeImeListeners() {
146
+ this._imeListeners.clear()
147
+ }
148
+ }
@@ -0,0 +1,54 @@
1
+ package com.unistyles
2
+
3
+ import android.content.BroadcastReceiver
4
+ import android.content.Context
5
+ import android.content.Intent
6
+ import android.content.IntentFilter
7
+ import android.os.Handler
8
+ import android.os.Looper
9
+ import com.facebook.react.bridge.ReactApplicationContext
10
+ import com.margelo.nitro.unistyles.UnistyleDependency
11
+
12
+ typealias CxxDependencyListener = (dependencies: Array<UnistyleDependency>) -> Unit
13
+
14
+ class NativePlatformListener(private val reactContext: ReactApplicationContext, private val diffMiniRuntime: () -> Array<UnistyleDependency>) {
15
+ private val _dependencyListeners: MutableList<CxxDependencyListener> = mutableListOf()
16
+
17
+ private val configurationChangeReceiver = object : BroadcastReceiver() {
18
+ override fun onReceive(context: Context, intent: Intent) {
19
+ Handler(Looper.getMainLooper()).postDelayed({
20
+ this@NativePlatformListener.onConfigChange()
21
+ }, 10)
22
+ }
23
+ }
24
+
25
+ init {
26
+ reactContext.registerReceiver(configurationChangeReceiver, IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED))
27
+ }
28
+
29
+ fun invalidate() {
30
+ reactContext.unregisterReceiver(configurationChangeReceiver)
31
+ }
32
+
33
+ fun addPlatformListener(listener: CxxDependencyListener) {
34
+ this._dependencyListeners.add(listener)
35
+ }
36
+
37
+ fun removePlatformListeners() {
38
+ this._dependencyListeners.clear()
39
+ }
40
+
41
+ private fun emitCxxEvent(dependencies: Array<UnistyleDependency>) {
42
+ this._dependencyListeners.forEach { listener ->
43
+ listener(dependencies)
44
+ }
45
+ }
46
+
47
+ fun onConfigChange() {
48
+ val changedDependencies = diffMiniRuntime()
49
+
50
+ if (changedDependencies.isNotEmpty()) {
51
+ emitCxxEvent(changedDependencies)
52
+ }
53
+ }
54
+ }
@@ -16,7 +16,7 @@ import com.margelo.nitro.unistyles.HybridNativePlatformSpec
16
16
  class UnistylesModule(reactContext: ReactApplicationContext): NativeTurboUnistylesSpec(reactContext), TurboModuleWithJSIBindings {
17
17
  @DoNotStrip
18
18
  private var mHybridData: HybridData?
19
- private val _nativePlatform = NativePlatform(reactContext)
19
+ private val _nativePlatform = NativePlatformAndroid(reactContext)
20
20
 
21
21
  companion object {
22
22
  const val NAME = NativeTurboUnistylesSpec.NAME
@@ -26,6 +26,10 @@ class UnistylesModule(reactContext: ReactApplicationContext): NativeTurboUnistyl
26
26
  mHybridData = initializeHybridData(reactContext)
27
27
  }
28
28
 
29
+ override fun invalidate() {
30
+ _nativePlatform.invalidate()
31
+ }
32
+
29
33
  private fun initializeHybridData(reactContext: ReactApplicationContext): HybridData {
30
34
  val runtimeExecutor = reactContext.catalystInstance?.runtimeExecutor
31
35
  ?: throw IllegalStateException("Unistyles: React Native runtime executor is not available. Please follow installation guides.")
@@ -12,7 +12,7 @@ struct UnistyleData {
12
12
  : unistyle{unistyle}, variants(std::move(variants)), dynamicFunctionMetadata{std::move(arguments)} {}
13
13
 
14
14
  UnistyleData(const UnistyleData&) = delete;
15
- UnistyleData(UnistyleData&& other): unistyle{other.unistyle}, variants(std::move(other.variants)) {}
15
+ UnistyleData(UnistyleData&& other) = delete;
16
16
 
17
17
  core::Unistyle::Shared unistyle;
18
18
  core::Variants variants;
@@ -32,7 +32,6 @@ inline static Unistyle::Shared unistyleFromStaticStyleSheet(jsi::Runtime& rt, js
32
32
  return exoticUnistyle;
33
33
  }
34
34
 
35
-
36
35
  inline static std::vector<Unistyle::Shared> unistylesFromNonExistentNativeState(jsi::Runtime& rt, jsi::Object& value) {
37
36
  auto hasUnistyleName = value.hasProperty(rt, helpers::NAME_STYLE_KEY.c_str());
38
37
 
@@ -71,7 +70,7 @@ inline static jsi::Object generateUnistylesPrototype(
71
70
  auto hostFn = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forUtf8(rt, "getStyle"), 0, [unistyle, unistylesRuntime](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count){
72
71
  auto variants = helpers::variantsToPairs(rt, thisValue.asObject(rt).getProperty(rt, "variants").asObject(rt));
73
72
  auto arguments = helpers::parseDynamicFunctionArguments(rt, thisValue.asObject(rt).getProperty(rt, "arguments").asObject(rt).asArray(rt));
74
-
73
+
75
74
  parser::Parser(unistylesRuntime).rebuildUnistyle(rt, unistyle->parent, unistyle, variants, std::make_optional<std::vector<folly::dynamic>>(arguments));
76
75
 
77
76
  return jsi::Value(rt, unistyle->parsedStyle.value()).asObject(rt);
@@ -31,7 +31,7 @@ RootShadowNode::Unshared core::UnistylesCommitHook::shadowTreeWillCommit(
31
31
  auto& registry = core::UnistylesRegistry::get();
32
32
  auto& shadowLeafUpdates = registry.trafficController.getUpdates();
33
33
 
34
- // oops, not updates from Unistyles yet, skip it!
34
+ // oops, no updates from Unistyles yet, skip it!
35
35
  if (shadowLeafUpdates.size() == 0) {
36
36
  return newRootShadowNode;
37
37
  }
@@ -23,6 +23,6 @@ void core::UnistylesMountHook::shadowTreeDidMount(RootShadowNode::Shared const &
23
23
  auto& registry = core::UnistylesRegistry::get();
24
24
 
25
25
  if (!registry.trafficController.shouldStop()) {
26
- shadow::ShadowTreeManager::updateShadowTree(this->_unistylesRuntime->getRuntime());
26
+ shadow::ShadowTreeManager::updateShadowTree(this->_uiManager->getShadowTreeRegistry());
27
27
  }
28
28
  }