@iternio/react-native-auto-play 0.3.7 → 0.3.9

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.
Files changed (45) hide show
  1. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AndroidAutoService.kt +4 -17
  2. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AndroidAutoSession.kt +21 -32
  3. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAutoPlay.kt +7 -0
  4. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridCluster.kt +6 -0
  5. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/VirtualRenderer.kt +64 -62
  6. package/ios/hybrid/HybridAutoPlay.swift +7 -0
  7. package/ios/hybrid/HybridCluster.swift +22 -8
  8. package/lib/AutoPlayHeadlessJsTask.d.ts +2 -1
  9. package/lib/AutoPlayHeadlessJsTask.js +4 -5
  10. package/lib/components/SafeAreaInsetsContext.d.ts +1 -1
  11. package/lib/components/SafeAreaInsetsContext.js +1 -1
  12. package/lib/hooks/useAndroidAutoTelemetry.js +2 -1
  13. package/lib/hooks/useFocusedEffect.js +1 -1
  14. package/lib/hooks/useVoiceInput.d.ts +1 -1
  15. package/lib/hooks/useVoiceInput.js +1 -1
  16. package/lib/hybrid/HybridAndroidAutoTelemetry.android.d.ts +2 -0
  17. package/lib/hybrid/HybridAndroidAutoTelemetry.android.js +2 -0
  18. package/lib/hybrid/HybridAndroidAutoTelemetry.d.ts +2 -0
  19. package/lib/hybrid/HybridAndroidAutoTelemetry.js +1 -0
  20. package/lib/hybrid/HybridAutoPlay.d.ts +2 -0
  21. package/lib/hybrid/HybridAutoPlay.js +2 -0
  22. package/lib/index.d.ts +4 -4
  23. package/lib/index.js +5 -5
  24. package/lib/scenes/CarPlayDashboardScene.d.ts +3 -3
  25. package/lib/scenes/CarPlayDashboardScene.js +1 -1
  26. package/lib/templates/MapTemplate.d.ts +1 -1
  27. package/lib/templates/MapTemplate.js +1 -1
  28. package/lib/templates/MessageTemplate.d.ts +1 -1
  29. package/lib/templates/MessageTemplate.js +1 -1
  30. package/lib/templates/Template.js +1 -1
  31. package/package.json +6 -4
  32. package/src/AutoPlayHeadlessJsTask.ts +12 -9
  33. package/src/components/SafeAreaInsetsContext.tsx +2 -1
  34. package/src/hooks/useAndroidAutoTelemetry.ts +2 -1
  35. package/src/hooks/useFocusedEffect.ts +1 -1
  36. package/src/hooks/useVoiceInput.ts +2 -1
  37. package/src/hybrid/HybridAndroidAutoTelemetry.android.ts +5 -0
  38. package/src/hybrid/HybridAndroidAutoTelemetry.ts +3 -0
  39. package/src/hybrid/HybridAutoPlay.ts +4 -0
  40. package/src/index.ts +5 -9
  41. package/src/scenes/CarPlayDashboardScene.ts +1 -1
  42. package/src/templates/MapTemplate.ts +2 -1
  43. package/src/templates/MessageTemplate.ts +7 -7
  44. package/src/templates/Template.ts +1 -1
  45. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/utils/ReactContextResolver.kt +0 -36
@@ -23,17 +23,11 @@ import androidx.core.app.NotificationCompat
23
23
  import androidx.core.app.NotificationManagerCompat
24
24
  import androidx.lifecycle.DefaultLifecycleObserver
25
25
  import androidx.lifecycle.LifecycleOwner
26
- import com.facebook.react.ReactApplication
27
26
  import com.facebook.react.bridge.LifecycleEventListener
28
- import com.facebook.react.bridge.ReactContext
27
+ import com.margelo.nitro.NitroModules
29
28
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.AppInfo
30
- import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.ReactContextResolver
31
- import kotlinx.coroutines.CoroutineScope
32
- import kotlinx.coroutines.Dispatchers
33
- import kotlinx.coroutines.launch
34
29
 
35
30
  class AndroidAutoService : CarAppService() {
36
- private lateinit var reactContext: ReactContext
37
31
  private lateinit var notificationManager: NotificationManager
38
32
 
39
33
  private var isServiceBound = false
@@ -54,10 +48,7 @@ class AndroidAutoService : CarAppService() {
54
48
  super.onCreate()
55
49
  instance = this
56
50
 
57
- CoroutineScope(Dispatchers.Main).launch {
58
- reactContext = ReactContextResolver.getReactContext(application as ReactApplication)
59
- reactContext.addLifecycleEventListener(reactLifecycleObserver)
60
- }
51
+ NitroModules.applicationContext?.addLifecycleEventListener(reactLifecycleObserver)
61
52
 
62
53
  notificationManager = getSystemService(NotificationManager::class.java)
63
54
  val appLabel = AppInfo.getApplicationLabel(this)
@@ -72,7 +63,7 @@ class AndroidAutoService : CarAppService() {
72
63
  }
73
64
 
74
65
  override fun onCreateSession(sessionInfo: SessionInfo): Session {
75
- val session = AndroidAutoSession(sessionInfo, application as ReactApplication)
66
+ val session = AndroidAutoSession(sessionInfo)
76
67
 
77
68
  if (sessionInfo.displayType == SessionInfo.DISPLAY_TYPE_CLUSTER) {
78
69
  return session
@@ -89,11 +80,7 @@ class AndroidAutoService : CarAppService() {
89
80
 
90
81
  stopForeground(STOP_FOREGROUND_REMOVE)
91
82
 
92
- if (!this::reactContext.isInitialized) {
93
- return
94
- }
95
-
96
- reactContext.removeLifecycleEventListener(reactLifecycleObserver)
83
+ NitroModules.applicationContext?.removeLifecycleEventListener(reactLifecycleObserver)
97
84
  }
98
85
 
99
86
  private val reactLifecycleObserver = object : LifecycleEventListener {
@@ -12,21 +12,16 @@ import androidx.car.app.model.MessageTemplate
12
12
  import androidx.car.app.model.Template
13
13
  import androidx.lifecycle.DefaultLifecycleObserver
14
14
  import androidx.lifecycle.LifecycleOwner
15
- import com.facebook.react.ReactApplication
16
15
  import com.facebook.react.bridge.LifecycleEventListener
17
- import com.facebook.react.bridge.ReactContext
16
+ import com.margelo.nitro.NitroModules
18
17
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.template.AndroidAutoTemplate
19
18
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.template.MapTemplate
20
19
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.AppInfo
21
- import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.ReactContextResolver
22
- import kotlinx.coroutines.CoroutineScope
23
- import kotlinx.coroutines.Dispatchers
24
- import kotlinx.coroutines.launch
25
20
  import java.util.UUID
26
21
  import java.util.concurrent.ConcurrentHashMap
27
22
  import java.util.concurrent.CopyOnWriteArrayList
28
23
 
29
- class AndroidAutoSession(sessionInfo: SessionInfo, private val reactApplication: ReactApplication) :
24
+ class AndroidAutoSession(sessionInfo: SessionInfo) :
30
25
  Session() {
31
26
 
32
27
  private val isCluster = sessionInfo.displayType == SessionInfo.DISPLAY_TYPE_CLUSTER
@@ -89,34 +84,29 @@ class AndroidAutoSession(sessionInfo: SessionInfo, private val reactApplication:
89
84
  }
90
85
 
91
86
  lifecycle.addObserver(sessionLifecycleObserver)
87
+ NitroModules.applicationContext?.addLifecycleEventListener(reactLifecycleObserver)
92
88
 
93
- CoroutineScope(Dispatchers.Main).launch {
94
- reactContext = ReactContextResolver.getReactContext(reactApplication)
95
- reactContext.addLifecycleEventListener(reactLifecycleObserver)
96
-
97
- // TODO this is not required for templates that host a component, check if we need this for non-rendering templates
98
- /*
99
- val appRegistry = reactContext.getJSModule(AppRegistry::class.java)
100
- ?: throw ClassNotFoundException("could not get AppRegistry instance")
101
- val jsAppModuleName = if (isCluster) "AndroidAutoCluster" else "AndroidAuto"
102
- val appParams = WritableNativeMap().apply {
103
- putMap("initialProps", Arguments.createMap().apply {
104
- putString("id", clusterTemplateId)
105
- })
106
- }
107
-
108
- appRegistry.runApplication(jsAppModuleName, appParams)
109
- */
110
-
111
- if (clusterId != null) {
112
- HybridCluster.emit(ClusterEventName.DIDCONNECTWITHWINDOW, clusterId)
113
- return@launch
114
- }
115
-
89
+ if (clusterId == null) {
116
90
  HybridAutoPlay.emit(EventName.DIDCONNECT)
91
+ } else {
92
+ HybridCluster.emit(ClusterEventName.DIDCONNECTWITHWINDOW, clusterId)
117
93
  }
118
94
 
119
95
  return screen
96
+
97
+ // TODO this is not required for templates that host a component, check if we need this for non-rendering templates
98
+ /*
99
+ val appRegistry = reactContext.getJSModule(AppRegistry::class.java)
100
+ ?: throw ClassNotFoundException("could not get AppRegistry instance")
101
+ val jsAppModuleName = if (isCluster) "AndroidAutoCluster" else "AndroidAuto"
102
+ val appParams = WritableNativeMap().apply {
103
+ putMap("initialProps", Arguments.createMap().apply {
104
+ putString("id", clusterTemplateId)
105
+ })
106
+ }
107
+
108
+ appRegistry.runApplication(jsAppModuleName, appParams)
109
+ */
120
110
  }
121
111
 
122
112
  override fun onCarConfigurationChanged(configuration: Configuration) {
@@ -129,7 +119,7 @@ class AndroidAutoSession(sessionInfo: SessionInfo, private val reactApplication:
129
119
  }
130
120
 
131
121
  val marker = AndroidAutoScreen.getScreen(ROOT_SESSION)?.marker ?: return
132
- val config = AndroidAutoTemplate.getConfig(marker) as MapTemplateConfig? ?: return
122
+ val config = AndroidAutoTemplate.getConfig(marker) as? MapTemplateConfig? ?: return
133
123
 
134
124
  if (config.onAppearanceDidChange != null) {
135
125
  config.onAppearanceDidChange(colorScheme)
@@ -247,7 +237,6 @@ class AndroidAutoSession(sessionInfo: SessionInfo, private val reactApplication:
247
237
  const val TAG = "AndroidAutoSession"
248
238
  const val ROOT_SESSION = "AutoPlayRoot"
249
239
 
250
- private lateinit var reactContext: ReactContext
251
240
  private val sessions = ConcurrentHashMap<String, ScreenContext>()
252
241
 
253
242
  private val clusterSessions = CopyOnWriteArrayList<String>()
@@ -9,6 +9,13 @@ import java.util.concurrent.ConcurrentHashMap
9
9
  import java.util.concurrent.CopyOnWriteArrayList
10
10
 
11
11
  class HybridAutoPlay : HybridAutoPlaySpec() {
12
+ init {
13
+ listeners.clear()
14
+ renderStateListeners.clear()
15
+ voiceInputListeners.clear()
16
+ safeAreaInsetsListeners.clear()
17
+ }
18
+
12
19
  override fun addListener(
13
20
  eventType: EventName, callback: () -> Unit
14
21
  ): () -> Unit {
@@ -6,6 +6,12 @@ import java.util.concurrent.ConcurrentHashMap
6
6
  import java.util.concurrent.CopyOnWriteArrayList
7
7
 
8
8
  class HybridCluster : HybridClusterSpec() {
9
+ init {
10
+ listeners.clear()
11
+ eventQueue.clear()
12
+ colorSchemeListeners.clear()
13
+ }
14
+
9
15
  override fun addListener(
10
16
  eventType: ClusterEventName, callback: (String) -> Unit
11
17
  ): () -> Unit {
@@ -2,12 +2,11 @@ package com.margelo.nitro.swe.iternio.reactnativeautoplay
2
2
 
3
3
  import android.app.Presentation
4
4
  import android.content.Context
5
- import android.view.ContextThemeWrapper
6
5
  import android.graphics.Color
7
6
  import android.graphics.Rect
8
7
  import android.hardware.display.DisplayManager
8
+ import android.hardware.display.VirtualDisplay
9
9
  import android.os.Bundle
10
- import android.util.Log
11
10
  import android.view.Display
12
11
  import android.view.LayoutInflater
13
12
  import android.view.View
@@ -15,11 +14,11 @@ import android.view.ViewGroup
15
14
  import android.view.ViewTreeObserver
16
15
  import android.widget.FrameLayout
17
16
  import android.widget.TextView
17
+ import androidx.annotation.MainThread
18
18
  import androidx.car.app.AppManager
19
19
  import androidx.car.app.CarContext
20
20
  import androidx.car.app.SurfaceCallback
21
21
  import androidx.car.app.SurfaceContainer
22
- import com.facebook.react.ReactApplication
23
22
  import com.facebook.react.bridge.Arguments
24
23
  import com.facebook.react.bridge.ReactContext
25
24
  import com.facebook.react.fabric.FabricUIManager
@@ -28,13 +27,11 @@ import com.facebook.react.runtime.ReactSurfaceView
28
27
  import com.facebook.react.uimanager.DisplayMetricsHolder
29
28
  import com.facebook.react.uimanager.UIManagerHelper
30
29
  import com.facebook.react.uimanager.common.UIManagerType
30
+ import com.margelo.nitro.NitroModules
31
31
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.template.AndroidAutoTemplate
32
32
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.AppInfo
33
33
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.Debouncer
34
- import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.ReactContextResolver
35
- import kotlinx.coroutines.CoroutineScope
36
- import kotlinx.coroutines.Dispatchers
37
- import kotlinx.coroutines.launch
34
+ import java.util.concurrent.ConcurrentHashMap
38
35
  import kotlin.math.floor
39
36
 
40
37
  class VirtualRenderer(
@@ -42,14 +39,7 @@ class VirtualRenderer(
42
39
  private val moduleName: String,
43
40
  private val isCluster: Boolean = false
44
41
  ) {
45
- private lateinit var fabricUiManager: FabricUIManager
46
-
47
- private fun isUiManagerInitialized(): Boolean {
48
- return ::fabricUiManager.isInitialized
49
- }
50
-
51
- private lateinit var display: Display
52
- private lateinit var reactContext: ReactContext
42
+ private var virtualDisplay: VirtualDisplay? = null
53
43
 
54
44
  private lateinit var reactSurfaceImpl: ReactSurfaceImpl
55
45
  private var reactSurfaceView: ReactSurfaceView? = null
@@ -66,20 +56,13 @@ class VirtualRenderer(
66
56
  private val virtualScreenDensity = context.resources.displayMetrics.density
67
57
  val scale = BuildConfig.SCALE_FACTOR * virtualScreenDensity
68
58
 
59
+ private fun isSurfaceReady(surfaceContainer: SurfaceContainer): Boolean {
60
+ return surfaceContainer.surface != null && surfaceContainer.dpi != 0 && surfaceContainer.height != 0 && surfaceContainer.width != 0
61
+ }
62
+
69
63
  init {
70
64
  virtualRenderer[moduleName] = this
71
65
 
72
- CoroutineScope(Dispatchers.Main).launch {
73
- reactContext =
74
- ReactContextResolver.getReactContext(context.applicationContext as ReactApplication)
75
-
76
- fabricUiManager = UIManagerHelper.getUIManager(
77
- reactContext, UIManagerType.FABRIC
78
- ) as FabricUIManager
79
-
80
- initRenderer()
81
- }
82
-
83
66
  context.getCarService(AppManager::class.java).setSurfaceCallback(object : SurfaceCallback {
84
67
  val areaDebouncer = Debouncer(200)
85
68
 
@@ -90,13 +73,12 @@ class VirtualRenderer(
90
73
  var visibleArea = Rect(0, 0, 0, 0)
91
74
 
92
75
  override fun onSurfaceAvailable(surfaceContainer: SurfaceContainer) {
93
- if (surfaceContainer.surface == null) {
94
- Log.w(TAG, "surface is null")
76
+ if (!isSurfaceReady(surfaceContainer)) {
95
77
  return
96
78
  }
97
79
 
98
80
  val manager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
99
- val virtualDisplay = manager.createVirtualDisplay(
81
+ virtualDisplay = manager.createVirtualDisplay(
100
82
  moduleName,
101
83
  surfaceContainer.width,
102
84
  surfaceContainer.height,
@@ -105,11 +87,16 @@ class VirtualRenderer(
105
87
  DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION,
106
88
  )
107
89
 
108
- display = virtualDisplay.display
90
+ val display = virtualDisplay?.display ?: return
109
91
  height = surfaceContainer.height
110
92
  width = surfaceContainer.width
111
93
 
112
- initRenderer()
94
+ initRenderer(display)
95
+ }
96
+
97
+ override fun onSurfaceDestroyed(surfaceContainer: SurfaceContainer) {
98
+ virtualDisplay?.release()
99
+ virtualDisplay = null
113
100
  }
114
101
 
115
102
  override fun onScroll(distanceX: Float, distanceY: Float) {
@@ -247,10 +234,12 @@ class VirtualRenderer(
247
234
  return AndroidAutoTemplate.getTypedConfig<MapTemplateConfig>(marker)
248
235
  }
249
236
 
250
- private fun initRenderer() {
251
- if (!this::display.isInitialized) {
252
- return
253
- }
237
+ private fun initRenderer(display: Display) {
238
+ val reactContext = NitroModules.applicationContext ?: return
239
+
240
+ val fabricUiManager = UIManagerHelper.getUIManager(
241
+ reactContext, UIManagerType.FABRIC
242
+ ) as? FabricUIManager? ?: return
254
243
 
255
244
  val initialProperties = Bundle().apply {
256
245
  putString("id", moduleName)
@@ -270,20 +259,21 @@ class VirtualRenderer(
270
259
  val mainScreenDensity = DisplayMetricsHolder.getScreenDisplayMetrics().density
271
260
  val reactNativeScale = virtualScreenDensity / mainScreenDensity * BuildConfig.SCALE_FACTOR
272
261
 
273
- if (!isUiManagerInitialized()) {
274
- // this makes sure we have all required instances
275
- // no matter if the app is launched on the phone or AA first
276
- return
277
- }
278
-
279
262
  FabricMapPresentation(
280
- context, display, height, width, initialProperties, reactNativeScale
263
+ reactContext,
264
+ display,
265
+ fabricUiManager,
266
+ height,
267
+ width,
268
+ initialProperties,
269
+ reactNativeScale
281
270
  ).show()
282
271
  }
283
272
 
284
273
  inner class FabricMapPresentation(
285
- private val context: CarContext,
274
+ private val context: ReactContext,
286
275
  display: Display,
276
+ private val fabricUiManager: FabricUIManager,
287
277
  private val height: Int,
288
278
  private val width: Int,
289
279
  private val initialProperties: Bundle,
@@ -292,11 +282,8 @@ class VirtualRenderer(
292
282
  override fun onCreate(savedInstanceState: Bundle?) {
293
283
  super.onCreate(savedInstanceState)
294
284
 
295
- val appTheme = context.applicationContext.applicationInfo.theme
296
- val themedContext = ContextThemeWrapper(context.applicationContext, appTheme)
297
-
298
285
  if (!this@VirtualRenderer::reactSurfaceImpl.isInitialized) {
299
- reactSurfaceImpl = ReactSurfaceImpl(themedContext, moduleName, initialProperties)
286
+ reactSurfaceImpl = ReactSurfaceImpl(context, moduleName, initialProperties)
300
287
  }
301
288
 
302
289
  var splashScreenView: View? = null
@@ -305,9 +292,9 @@ class VirtualRenderer(
305
292
  (it.parent as ViewGroup).removeView(it)
306
293
  } ?: run {
307
294
  splashScreenView =
308
- if (isCluster) getClusterSplashScreen(themedContext, height, width) else null
295
+ if (isCluster) getClusterSplashScreen(context, height, width) else null
309
296
 
310
- val surfaceView = ReactSurfaceView(themedContext, reactSurfaceImpl).apply {
297
+ val surfaceView = ReactSurfaceView(context, reactSurfaceImpl).apply {
311
298
  layoutParams = FrameLayout.LayoutParams(
312
299
  (width / reactNativeScale).toInt(), (height / reactNativeScale).toInt()
313
300
  )
@@ -335,7 +322,7 @@ class VirtualRenderer(
335
322
  )
336
323
 
337
324
  // remove ui-managers lifecycle listener to not stop rendering when app is not in foreground/phone screen is off
338
- reactContext.removeLifecycleEventListener(fabricUiManager)
325
+ context.removeLifecycleEventListener(fabricUiManager)
339
326
  // trigger ui-managers onHostResume to make sure the surface is rendered properly even when AA only is starting without the phone app
340
327
  fabricUiManager.onHostResume()
341
328
 
@@ -343,7 +330,7 @@ class VirtualRenderer(
343
330
  }
344
331
 
345
332
 
346
- val rootContainer = FrameLayout(themedContext).apply {
333
+ val rootContainer = FrameLayout(context).apply {
347
334
  layoutParams = FrameLayout.LayoutParams(
348
335
  FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT
349
336
  )
@@ -365,8 +352,7 @@ class VirtualRenderer(
365
352
  ): View {
366
353
  val root = FrameLayout(context).apply {
367
354
  layoutParams = ViewGroup.LayoutParams(
368
- ViewGroup.LayoutParams.MATCH_PARENT,
369
- ViewGroup.LayoutParams.MATCH_PARENT
355
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
370
356
  )
371
357
  }
372
358
 
@@ -407,24 +393,40 @@ class VirtualRenderer(
407
393
  })
408
394
  }
409
395
 
396
+ @MainThread
397
+ private fun stop() {
398
+ virtualDisplay?.release()
399
+ virtualDisplay = null
400
+
401
+ val uiManager = NitroModules.applicationContext?.let {
402
+ UIManagerHelper.getUIManager(
403
+ it, UIManagerType.FABRIC
404
+ ) as? FabricUIManager?
405
+ }
406
+
407
+ try {
408
+ reactSurfaceId?.let {
409
+ uiManager?.stopSurface(it)
410
+ }
411
+ } catch (_: AssertionError) {
412
+ // Fabric already invalidated
413
+ } finally {
414
+ reactSurfaceId = null
415
+ virtualRenderer.remove(moduleName)
416
+ }
417
+ }
418
+
410
419
  companion object {
411
420
  const val TAG = "VirtualRenderer"
412
421
 
413
- private val virtualRenderer = mutableMapOf<String, VirtualRenderer>()
422
+ private val virtualRenderer = ConcurrentHashMap<String, VirtualRenderer>()
414
423
 
415
424
  fun hasRenderer(moduleId: String): Boolean {
416
425
  return virtualRenderer.contains(moduleId)
417
426
  }
418
427
 
419
428
  fun removeRenderer(moduleId: String) {
420
- val renderer = virtualRenderer[moduleId]
421
-
422
- if (renderer?.isUiManagerInitialized() == true) {
423
- renderer.reactSurfaceId?.let {
424
- renderer.fabricUiManager.stopSurface(it)
425
- }
426
- }
427
-
429
+ virtualRenderer[moduleId]?.stop()
428
430
  virtualRenderer.remove(moduleId)
429
431
  }
430
432
  }
@@ -21,6 +21,13 @@ class HybridAutoPlay: HybridAutoPlaySpec {
21
21
  private static var renderStateListeners = [String: [RenderStateListener]]()
22
22
  private static var safeAreaInsetsListeners = [String: [SafeAreaListener]]()
23
23
 
24
+ override init() {
25
+ HybridAutoPlay.listeners.removeAll()
26
+ HybridAutoPlay.renderStateListeners.removeAll()
27
+ HybridAutoPlay.safeAreaInsetsListeners.removeAll()
28
+ super.init()
29
+ }
30
+
24
31
  func addListener(eventType: EventName, callback: @escaping () -> Void)
25
32
  throws -> () -> Void
26
33
  {
@@ -31,6 +31,16 @@ class HybridCluster: HybridClusterSpec {
31
31
  String: (_: String, _: Bool) -> Void
32
32
  ]()
33
33
 
34
+ override init() {
35
+ HybridCluster.listeners.removeAll()
36
+ HybridCluster.eventQueue.removeAll()
37
+ HybridCluster.colorSchemeListeners.removeAll()
38
+ HybridCluster.zoomListeners.removeAll()
39
+ HybridCluster.compassListeners.removeAll()
40
+ HybridCluster.speedLimitListeners.removeAll()
41
+ super.init()
42
+ }
43
+
34
44
  func addListener(
35
45
  eventType: ClusterEventName,
36
46
  callback: @escaping (_ clusterId: String) -> Void
@@ -57,12 +67,14 @@ class HybridCluster: HybridClusterSpec {
57
67
  return Promise.async {
58
68
  if #available(iOS 15.4, *) {
59
69
  try await MainActor.run {
60
- guard let scene = SceneStore.getClusterScene(
61
- clusterId: clusterId
62
- ) else {
70
+ guard
71
+ let scene = SceneStore.getClusterScene(
72
+ clusterId: clusterId
73
+ )
74
+ else {
63
75
  return
64
76
  }
65
-
77
+
66
78
  try scene.initRootView()
67
79
  }
68
80
  }
@@ -80,12 +92,14 @@ class HybridCluster: HybridClusterSpec {
80
92
  [NitroAttributedString]
81
93
  ) throws {
82
94
  if #available(iOS 15.4, *) {
83
- guard let scene = SceneStore.getClusterScene(
84
- clusterId: clusterId
85
- ) else {
95
+ guard
96
+ let scene = SceneStore.getClusterScene(
97
+ clusterId: clusterId
98
+ )
99
+ else {
86
100
  return
87
101
  }
88
-
102
+
89
103
  scene.setAttributedInactiveDescriptionVariants(
90
104
  attributedInactiveDescriptionVariants:
91
105
  attributedInactiveDescriptionVariants
@@ -1,4 +1,5 @@
1
+ import type { AutoPlay as NitroAutoPlay } from './specs/AutoPlay.nitro';
1
2
  declare const _default: {
2
- registerHeadlessTask: () => void;
3
+ registerHeadlessTask: (hybridAutoPlay: NitroAutoPlay) => void;
3
4
  };
4
5
  export default _default;
@@ -1,15 +1,14 @@
1
1
  import { AppRegistry, Platform } from 'react-native';
2
- import { HybridAutoPlay } from '.';
3
- const taskProvider = () => () => new Promise((resolve) => {
4
- const remove = HybridAutoPlay.addListener('didDisconnect', () => {
2
+ const createTaskProvider = (hybridAutoPlay) => () => () => new Promise((resolve) => {
3
+ const remove = hybridAutoPlay.addListener('didDisconnect', () => {
5
4
  resolve();
6
5
  remove();
7
6
  });
8
7
  });
9
- const registerHeadlessTask = () => {
8
+ const registerHeadlessTask = (hybridAutoPlay) => {
10
9
  if (Platform.OS !== 'android') {
11
10
  return;
12
11
  }
13
- AppRegistry.registerHeadlessTask('AndroidAutoHeadlessJsTask', taskProvider);
12
+ AppRegistry.registerHeadlessTask('AndroidAutoHeadlessJsTask', createTaskProvider(hybridAutoPlay));
14
13
  };
15
14
  export default { registerHeadlessTask };
@@ -1,5 +1,5 @@
1
1
  import type React from 'react';
2
- import { type SafeAreaInsets } from '..';
2
+ import type { SafeAreaInsets } from '..';
3
3
  export declare const SafeAreaInsetsContext: React.Context<SafeAreaInsets>;
4
4
  export declare function SafeAreaInsetsProvider({ children, moduleName, }: {
5
5
  children: React.ReactNode;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useEffect, useState } from 'react';
3
- import { HybridAutoPlay } from '..';
3
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
4
4
  const DEFAULT = { top: 0, bottom: 0, left: 0, right: 0 };
5
5
  export const SafeAreaInsetsContext = createContext(DEFAULT);
6
6
  export function SafeAreaInsetsProvider({ children, moduleName, }) {
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import { PermissionsAndroid } from 'react-native';
3
- import { HybridAndroidAutoTelemetry, HybridAutoPlay } from '..';
3
+ import { HybridAndroidAutoTelemetry } from '../hybrid/HybridAndroidAutoTelemetry';
4
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
4
5
  /**
5
6
  * Hook to check if the telemetry permissions are granted. If the permissions are not granted, it will request them from the user.
6
7
  *
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
- import { HybridAutoPlay } from '..';
2
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
3
3
  /**
4
4
  * An effect hook that only runs when the CarPlay/Android Auto screen is visible to the user and dependencies have changed.
5
5
  * It behaves like `useEffect`, but the effect function is only executed if the screen is visible.
@@ -1,4 +1,4 @@
1
- import { type Location } from '..';
1
+ import type { Location } from '..';
2
2
  export declare const useVoiceInput: () => {
3
3
  voiceInputResult: {
4
4
  coordinates: Location | undefined;
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useEffect, useState } from 'react';
2
- import { HybridAutoPlay } from '..';
2
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
3
3
  export const useVoiceInput = () => {
4
4
  const [isConnected, setIsConnected] = useState(false);
5
5
  const [voiceInputResult, setVoiceInputResult] = useState();
@@ -0,0 +1,2 @@
1
+ import type { AndroidAutoTelemetry } from '../specs/AndroidAutoTelemetry.nitro';
2
+ export declare const HybridAndroidAutoTelemetry: AndroidAutoTelemetry;
@@ -0,0 +1,2 @@
1
+ import { NitroModules } from 'react-native-nitro-modules';
2
+ export const HybridAndroidAutoTelemetry = NitroModules.createHybridObject('AndroidAutoTelemetry');
@@ -0,0 +1,2 @@
1
+ import type { AndroidAutoTelemetry } from '../specs/AndroidAutoTelemetry.nitro';
2
+ export declare const HybridAndroidAutoTelemetry: AndroidAutoTelemetry | null;
@@ -0,0 +1 @@
1
+ export const HybridAndroidAutoTelemetry = null;
@@ -0,0 +1,2 @@
1
+ import type { AutoPlay } from '../specs/AutoPlay.nitro';
2
+ export declare const HybridAutoPlay: AutoPlay;
@@ -0,0 +1,2 @@
1
+ import { NitroModules } from 'react-native-nitro-modules';
2
+ export const HybridAutoPlay = NitroModules.createHybridObject('AutoPlay');
package/lib/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
+ import { HybridAndroidAutoTelemetry } from './hybrid/HybridAndroidAutoTelemetry';
2
+ import { HybridAutoPlay } from './hybrid/HybridAutoPlay';
1
3
  import type { AndroidAutomotive } from './specs/AndroidAutomotive.nitro';
2
- import type { AndroidAutoTelemetry as NitroAndroidAutoTelemetry } from './specs/AndroidAutoTelemetry.nitro';
3
- import type { AutoPlay as NitroAutoPlay } from './specs/AutoPlay.nitro';
4
- export declare const HybridAutoPlay: NitroAutoPlay;
5
- export declare const HybridAndroidAutoTelemetry: NitroAndroidAutoTelemetry | null;
4
+ export { HybridAutoPlay };
5
+ export { HybridAndroidAutoTelemetry };
6
6
  export declare const HybridAndroidAutomotive: AndroidAutomotive | null;
7
7
  /**
8
8
  * These are the static module names for the app running on the mobile device, head unit screen and the CarPlay dashboard.
package/lib/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { Platform } from 'react-native';
2
2
  import { NitroModules } from 'react-native-nitro-modules';
3
3
  import AutoPlayHeadlessJsTask from './AutoPlayHeadlessJsTask';
4
- AutoPlayHeadlessJsTask.registerHeadlessTask();
5
- export const HybridAutoPlay = NitroModules.createHybridObject('AutoPlay');
6
- export const HybridAndroidAutoTelemetry = Platform.OS === 'android'
7
- ? NitroModules.createHybridObject('AndroidAutoTelemetry')
8
- : null;
4
+ import { HybridAndroidAutoTelemetry } from './hybrid/HybridAndroidAutoTelemetry';
5
+ import { HybridAutoPlay } from './hybrid/HybridAutoPlay';
6
+ AutoPlayHeadlessJsTask.registerHeadlessTask(HybridAutoPlay);
7
+ export { HybridAutoPlay };
8
+ export { HybridAndroidAutoTelemetry };
9
9
  export const HybridAndroidAutomotive = Platform.OS === 'android'
10
10
  ? NitroModules.createHybridObject('AndroidAutomotive')
11
11
  : null;
@@ -27,9 +27,9 @@ declare class Dashboard {
27
27
  * @param eventType generic events
28
28
  * @returns callback to remove the listener
29
29
  */
30
- addListener(event: EventName, callback: () => void): import("..").CleanupCallback;
31
- addListenerRenderState(callback: (payload: VisibilityState) => void): import("..").CleanupCallback;
32
- addListenerColorScheme(callback: (payload: ColorScheme) => void): import("..").CleanupCallback;
30
+ addListener(event: EventName, callback: () => void): import("../types/Event").CleanupCallback;
31
+ addListenerRenderState(callback: (payload: VisibilityState) => void): import("../types/Event").CleanupCallback;
32
+ addListenerColorScheme(callback: (payload: ColorScheme) => void): import("../types/Event").CleanupCallback;
33
33
  }
34
34
  export declare const CarPlayDashboard: Dashboard;
35
35
  export {};
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
2
  import { AppRegistry, Platform } from 'react-native';
3
3
  import { NitroModules } from 'react-native-nitro-modules';
4
- import { HybridAutoPlay } from '..';
5
4
  import { SafeAreaInsetsProvider } from '../components/SafeAreaInsetsContext';
5
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
6
6
  import { NitroImageUtil } from '../utils/NitroImage';
7
7
  const HybridCarPlayDashboard = Platform.OS === 'ios'
8
8
  ? NitroModules.createHybridObject('CarPlayDashboard')
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { type AutoText } from '..';
2
+ import type { AutoText } from '..';
3
3
  import type { ActionButtonAndroid, MapButton, MapPanButton } from '../types/Button';
4
4
  import type { AutoManeuver } from '../types/Maneuver';
5
5
  import type { ColorScheme, RootComponentInitialProps } from '../types/RootComponent';
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
2
  import { AppRegistry, Platform } from 'react-native';
3
3
  import { NitroModules } from 'react-native-nitro-modules';
4
- import { HybridAutoPlay } from '..';
5
4
  import { MapTemplateProvider } from '../components/MapTemplateContext';
6
5
  import { SafeAreaInsetsProvider } from '../components/SafeAreaInsetsContext';
6
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
7
7
  import { NitroActionUtil } from '../utils/NitroAction';
8
8
  import { NitroAlertUtil } from '../utils/NitroAlert';
9
9
  import { NitroManeuverUtil } from '../utils/NitroManeuver';
@@ -1,4 +1,4 @@
1
- import { type AutoImage, type BaseMapTemplateConfig, type CustomActionButtonAndroid, type HeaderActionsAndroid, type TextButton } from '..';
1
+ import type { AutoImage, BaseMapTemplateConfig, CustomActionButtonAndroid, HeaderActionsAndroid, TextButton } from '..';
2
2
  import type { AutoText } from '../types/Text';
3
3
  import { type NitroAction } from '../utils/NitroAction';
4
4
  import { type NitroImage } from '../utils/NitroImage';
@@ -1,7 +1,7 @@
1
1
  import { Platform } from 'react-native';
2
2
  import { NitroModules } from 'react-native-nitro-modules';
3
3
  import uuid from 'react-native-uuid';
4
- import { HybridAutoPlay, } from '..';
4
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
5
5
  import { NitroActionUtil } from '../utils/NitroAction';
6
6
  import { NitroImageUtil } from '../utils/NitroImage';
7
7
  import { NitroMapButton } from '../utils/NitroMapButton';
@@ -1,5 +1,5 @@
1
1
  import uuid from 'react-native-uuid';
2
- import { HybridAutoPlay } from '..';
2
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
3
3
  import { NitroActionUtil } from '../utils/NitroAction';
4
4
  export class Template {
5
5
  id;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iternio/react-native-auto-play",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "Android Auto and Apple CarPlay for react-native",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",
@@ -36,8 +36,9 @@
36
36
  "typescript": "tsc",
37
37
  "generate:fonts": "node ./assets/symbolFont/bundleAsset.js",
38
38
  "specs": "nitrogen --logLevel=\"debug\"",
39
- "prepare": "tsc && nitrogen && npm run generate:fonts",
40
- "swift:format": "swift-format format ./ios --in-place --recursive"
39
+ "prepare": "yarn circular && tsc && nitrogen && npm run generate:fonts",
40
+ "swift:format": "swift-format format ./ios --in-place --recursive",
41
+ "circular": "dpdm ./src/* --circular --exit-code circular:1 --no-tree --warning=false --transform"
41
42
  },
42
43
  "keywords": [
43
44
  "react-native",
@@ -59,7 +60,8 @@
59
60
  },
60
61
  "devDependencies": {
61
62
  "@types/jest": "^29.5.12",
62
- "@types/react": "^19.1.03",
63
+ "@types/react": "~19.2.0",
64
+ "dpdm": "^4.0.1",
63
65
  "nitrogen": "^0.35.3",
64
66
  "typescript": "^5.8.3"
65
67
  },
@@ -1,19 +1,22 @@
1
1
  import { AppRegistry, Platform, type Task, type TaskProvider } from 'react-native';
2
- import { HybridAutoPlay } from '.';
2
+ import type { AutoPlay as NitroAutoPlay } from './specs/AutoPlay.nitro';
3
3
 
4
- const taskProvider: TaskProvider = (): Task => () =>
5
- new Promise((resolve) => {
6
- const remove = HybridAutoPlay.addListener('didDisconnect', () => {
7
- resolve();
8
- remove();
4
+ const createTaskProvider =
5
+ (hybridAutoPlay: NitroAutoPlay): TaskProvider =>
6
+ (): Task =>
7
+ () =>
8
+ new Promise((resolve) => {
9
+ const remove = hybridAutoPlay.addListener('didDisconnect', () => {
10
+ resolve();
11
+ remove();
12
+ });
9
13
  });
10
- });
11
14
 
12
- const registerHeadlessTask = () => {
15
+ const registerHeadlessTask = (hybridAutoPlay: NitroAutoPlay) => {
13
16
  if (Platform.OS !== 'android') {
14
17
  return;
15
18
  }
16
- AppRegistry.registerHeadlessTask('AndroidAutoHeadlessJsTask', taskProvider);
19
+ AppRegistry.registerHeadlessTask('AndroidAutoHeadlessJsTask', createTaskProvider(hybridAutoPlay));
17
20
  };
18
21
 
19
22
  export default { registerHeadlessTask };
@@ -1,6 +1,7 @@
1
1
  import type React from 'react';
2
2
  import { createContext, useEffect, useState } from 'react';
3
- import { HybridAutoPlay, type SafeAreaInsets } from '..';
3
+ import type { SafeAreaInsets } from '..';
4
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
4
5
 
5
6
  const DEFAULT: SafeAreaInsets = { top: 0, bottom: 0, left: 0, right: 0 };
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import { type Permission, PermissionsAndroid } from 'react-native';
3
- import { HybridAndroidAutoTelemetry, HybridAutoPlay } from '..';
3
+ import { HybridAndroidAutoTelemetry } from '../hybrid/HybridAndroidAutoTelemetry';
4
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
4
5
 
5
6
  import type { AndroidAutoPermissions, Telemetry } from '../types/Telemetry';
6
7
 
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
- import { HybridAutoPlay } from '..';
2
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
3
3
 
4
4
  /**
5
5
  * An effect hook that only runs when the CarPlay/Android Auto screen is visible to the user and dependencies have changed.
@@ -1,5 +1,6 @@
1
1
  import { useCallback, useEffect, useState } from 'react';
2
- import { HybridAutoPlay, type Location } from '..';
2
+ import type { Location } from '..';
3
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
3
4
 
4
5
  export const useVoiceInput = () => {
5
6
  const [isConnected, setIsConnected] = useState(false);
@@ -0,0 +1,5 @@
1
+ import { NitroModules } from 'react-native-nitro-modules';
2
+ import type { AndroidAutoTelemetry } from '../specs/AndroidAutoTelemetry.nitro';
3
+
4
+ export const HybridAndroidAutoTelemetry =
5
+ NitroModules.createHybridObject<AndroidAutoTelemetry>('AndroidAutoTelemetry');
@@ -0,0 +1,3 @@
1
+ import type { AndroidAutoTelemetry } from '../specs/AndroidAutoTelemetry.nitro';
2
+
3
+ export const HybridAndroidAutoTelemetry: AndroidAutoTelemetry | null = null;
@@ -0,0 +1,4 @@
1
+ import { NitroModules } from 'react-native-nitro-modules';
2
+ import type { AutoPlay } from '../specs/AutoPlay.nitro';
3
+
4
+ export const HybridAutoPlay = NitroModules.createHybridObject<AutoPlay>('AutoPlay');
package/src/index.ts CHANGED
@@ -1,18 +1,14 @@
1
1
  import { Platform } from 'react-native';
2
2
  import { NitroModules } from 'react-native-nitro-modules';
3
3
  import AutoPlayHeadlessJsTask from './AutoPlayHeadlessJsTask';
4
+ import { HybridAndroidAutoTelemetry } from './hybrid/HybridAndroidAutoTelemetry';
5
+ import { HybridAutoPlay } from './hybrid/HybridAutoPlay';
4
6
  import type { AndroidAutomotive } from './specs/AndroidAutomotive.nitro';
5
- import type { AndroidAutoTelemetry as NitroAndroidAutoTelemetry } from './specs/AndroidAutoTelemetry.nitro';
6
- import type { AutoPlay as NitroAutoPlay } from './specs/AutoPlay.nitro';
7
7
 
8
- AutoPlayHeadlessJsTask.registerHeadlessTask();
8
+ AutoPlayHeadlessJsTask.registerHeadlessTask(HybridAutoPlay);
9
9
 
10
- export const HybridAutoPlay = NitroModules.createHybridObject<NitroAutoPlay>('AutoPlay');
11
-
12
- export const HybridAndroidAutoTelemetry =
13
- Platform.OS === 'android'
14
- ? NitroModules.createHybridObject<NitroAndroidAutoTelemetry>('AndroidAutoTelemetry')
15
- : null;
10
+ export { HybridAutoPlay };
11
+ export { HybridAndroidAutoTelemetry };
16
12
 
17
13
  export const HybridAndroidAutomotive =
18
14
  Platform.OS === 'android'
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
2
  import { AppRegistry, Platform } from 'react-native';
3
3
  import { NitroModules } from 'react-native-nitro-modules';
4
- import { HybridAutoPlay } from '..';
5
4
  import { SafeAreaInsetsProvider } from '../components/SafeAreaInsetsContext';
5
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
6
6
  import type {
7
7
  BaseCarPlayDashboardButton,
8
8
  CarPlayDashboard as NitroCarPlayDashboard,
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
2
  import { AppRegistry, Platform } from 'react-native';
3
3
  import { NitroModules } from 'react-native-nitro-modules';
4
- import { type AutoText, HybridAutoPlay } from '..';
4
+ import type { AutoText } from '..';
5
5
  import { MapTemplateProvider } from '../components/MapTemplateContext';
6
6
  import { SafeAreaInsetsProvider } from '../components/SafeAreaInsetsContext';
7
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
7
8
  import type { MapTemplate as NitroMapTemplate } from '../specs/MapTemplate.nitro';
8
9
  import type { ActionButtonAndroid, MapButton, MapPanButton } from '../types/Button';
9
10
  import type { AutoManeuver } from '../types/Maneuver';
@@ -1,14 +1,14 @@
1
1
  import { Platform } from 'react-native';
2
2
  import { NitroModules } from 'react-native-nitro-modules';
3
3
  import uuid from 'react-native-uuid';
4
- import {
5
- type AutoImage,
6
- type BaseMapTemplateConfig,
7
- type CustomActionButtonAndroid,
8
- type HeaderActionsAndroid,
9
- HybridAutoPlay,
10
- type TextButton,
4
+ import type {
5
+ AutoImage,
6
+ BaseMapTemplateConfig,
7
+ CustomActionButtonAndroid,
8
+ HeaderActionsAndroid,
9
+ TextButton,
11
10
  } from '..';
11
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
12
12
  import type { MessageTemplate as NitroMessageTemplate } from '../specs/MessageTemplate.nitro';
13
13
  import type { AutoText } from '../types/Text';
14
14
  import { type NitroAction, NitroActionUtil } from '../utils/NitroAction';
@@ -1,5 +1,5 @@
1
1
  import uuid from 'react-native-uuid';
2
- import { HybridAutoPlay } from '..';
2
+ import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
3
3
  import type { ActionButtonAndroid, ActionButtonIos, AppButton, BackButton } from '../types/Button';
4
4
  import { type NitroAction, NitroActionUtil } from '../utils/NitroAction';
5
5
  import type { NitroMapButton } from '../utils/NitroMapButton';
@@ -1,36 +0,0 @@
1
- package com.margelo.nitro.swe.iternio.reactnativeautoplay.utils
2
-
3
- import com.facebook.react.ReactApplication
4
- import com.facebook.react.ReactInstanceEventListener
5
- import com.facebook.react.bridge.ReactContext
6
- import kotlinx.coroutines.suspendCancellableCoroutine
7
- import kotlin.coroutines.resume
8
-
9
- object ReactContextResolver {
10
- suspend fun getReactContext(application: ReactApplication): ReactContext {
11
- val host =
12
- application.reactHost ?: throw IllegalArgumentException("application host null")
13
-
14
- return suspendCancellableCoroutine { continuation ->
15
- host.currentReactContext?.let {
16
- continuation.resume(it)
17
- return@suspendCancellableCoroutine
18
- }
19
-
20
- val listener = object : ReactInstanceEventListener {
21
- override fun onReactContextInitialized(context: ReactContext) {
22
- host.removeReactInstanceEventListener(this)
23
- continuation.resume(context)
24
- }
25
- }
26
- host.addReactInstanceEventListener(listener)
27
-
28
- continuation.invokeOnCancellation {
29
- host.removeReactInstanceEventListener(listener)
30
- }
31
-
32
- host.start()
33
- }
34
-
35
- }
36
- }