@iternio/react-native-auto-play 0.5.6 → 0.5.7
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/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAndroidWindowInformation.kt +49 -0
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAutoPlay.kt +1 -0
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/VirtualRenderer.kt +116 -29
- package/lib/HybridAutoPlay.d.ts +2 -0
- package/lib/HybridAutoPlay.js +2 -0
- package/lib/components/OnAppearedChildRenderer.d.ts +10 -0
- package/lib/components/OnAppearedChildRenderer.js +26 -0
- package/lib/components/WindowInformationWrapper.d.ts +15 -0
- package/lib/components/WindowInformationWrapper.js +17 -0
- package/lib/hooks/useIsAutoPlayFocused.d.ts +7 -0
- package/lib/hooks/useIsAutoPlayFocused.js +20 -0
- package/lib/hybrid/HybridAndroidWindowInformation.android.d.ts +2 -0
- package/lib/hybrid/HybridAndroidWindowInformation.android.js +2 -0
- package/lib/hybrid/HybridAndroidWindowInformation.d.ts +2 -0
- package/lib/hybrid/HybridAndroidWindowInformation.js +1 -0
- package/lib/hybrid.d.ts +2 -0
- package/lib/hybrid.js +2 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.js +2 -1
- package/lib/scenes/AutoPlayCluster.js +6 -1
- package/lib/specs/AndroidWindowInformation.nitro.d.ts +22 -0
- package/lib/specs/AndroidWindowInformation.nitro.js +1 -0
- package/lib/specs/AutomotivePermissionRequestTemplate.d.ts +11 -0
- package/lib/specs/AutomotivePermissionRequestTemplate.js +1 -0
- package/lib/specs/AutomotivePermissionRequestTemplate.nitro.d.ts +11 -0
- package/lib/specs/AutomotivePermissionRequestTemplate.nitro.js +1 -0
- package/lib/templates/AutomotivePermissionRequestTemplate.d.ts +23 -0
- package/lib/templates/AutomotivePermissionRequestTemplate.js +18 -0
- package/lib/templates/MapTemplate.js +6 -1
- package/lib/types/Glyphmap.d.ts +4105 -0
- package/lib/types/Glyphmap.js +4105 -0
- package/lib/types/RootComponent.d.ts +4 -0
- package/lib/utils/NitroOptionsPanel.d.ts +67 -0
- package/lib/utils/NitroOptionsPanel.js +48 -0
- package/nitro.json +6 -0
- package/nitrogen/generated/android/ReactNativeAutoPlay+autolinking.cmake +2 -0
- package/nitrogen/generated/android/ReactNativeAutoPlayOnLoad.cpp +18 -0
- package/nitrogen/generated/android/c++/JFunc_void_WindowInformation.hpp +77 -0
- package/nitrogen/generated/android/c++/JHybridAndroidWindowInformationSpec.cpp +68 -0
- package/nitrogen/generated/android/c++/JHybridAndroidWindowInformationSpec.hpp +63 -0
- package/nitrogen/generated/android/c++/JWindowInformation.hpp +65 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_WindowInformation.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAndroidWindowInformationSpec.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/WindowInformation.kt +61 -0
- package/nitrogen/generated/shared/c++/HybridAndroidWindowInformationSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridAndroidWindowInformationSpec.hpp +65 -0
- package/nitrogen/generated/shared/c++/WindowInformation.hpp +91 -0
- package/package.json +1 -1
- package/src/components/WindowInformationWrapper.tsx +30 -0
- package/src/hybrid/HybridAndroidWindowInformation.android.ts +5 -0
- package/src/hybrid/HybridAndroidWindowInformation.ts +3 -0
- package/src/index.ts +2 -1
- package/src/scenes/AutoPlayCluster.ts +6 -1
- package/src/specs/AndroidWindowInformation.nitro.ts +24 -0
- package/src/templates/MapTemplate.ts +6 -1
- package/src/types/RootComponent.ts +4 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
package com.margelo.nitro.swe.iternio.reactnativeautoplay
|
|
2
|
+
|
|
3
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
4
|
+
import java.util.concurrent.CopyOnWriteArrayList
|
|
5
|
+
|
|
6
|
+
class HybridAndroidWindowInformation : HybridAndroidWindowInformationSpec() {
|
|
7
|
+
init {
|
|
8
|
+
windowInformationListeners.clear()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
override fun addWindowInformationListener(
|
|
12
|
+
moduleName: String, callback: (WindowInformation) -> Unit
|
|
13
|
+
): () -> Unit {
|
|
14
|
+
val callbacks = windowInformationListeners.getOrPut(moduleName) {
|
|
15
|
+
CopyOnWriteArrayList()
|
|
16
|
+
}
|
|
17
|
+
callbacks.add(callback)
|
|
18
|
+
|
|
19
|
+
VirtualRenderer.getWindowInformation(moduleName)?.let {
|
|
20
|
+
callback(it)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
windowInformationListeners[moduleName]?.let {
|
|
25
|
+
it.remove(callback)
|
|
26
|
+
if (it.isEmpty()) {
|
|
27
|
+
windowInformationListeners.remove(moduleName)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
companion object {
|
|
34
|
+
const val TAG = "HybridAndroidWindowInformation"
|
|
35
|
+
|
|
36
|
+
private val windowInformationListeners =
|
|
37
|
+
ConcurrentHashMap<String, CopyOnWriteArrayList<(WindowInformation) -> Unit>>()
|
|
38
|
+
|
|
39
|
+
fun removeListeners(templateId: String) {
|
|
40
|
+
windowInformationListeners.remove(templateId)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fun emitWindowInformation(moduleName: String, window: WindowInformation) {
|
|
44
|
+
windowInformationListeners[moduleName]?.forEach {
|
|
45
|
+
it(window)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAutoPlay.kt
CHANGED
|
@@ -264,6 +264,7 @@ class HybridAutoPlay : HybridAutoPlaySpec() {
|
|
|
264
264
|
fun removeListeners(templateId: String) {
|
|
265
265
|
renderStateListeners.remove(templateId)
|
|
266
266
|
safeAreaInsetsListeners.remove(templateId)
|
|
267
|
+
HybridAndroidWindowInformation.removeListeners(templateId)
|
|
267
268
|
}
|
|
268
269
|
|
|
269
270
|
fun emit(event: EventName) {
|
package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/VirtualRenderer.kt
CHANGED
|
@@ -42,20 +42,24 @@ class VirtualRenderer(
|
|
|
42
42
|
) {
|
|
43
43
|
private var virtualDisplay: VirtualDisplay? = null
|
|
44
44
|
|
|
45
|
-
private
|
|
45
|
+
private var reactSurfaceImpl: ReactSurfaceImpl? = null
|
|
46
46
|
private var reactSurfaceView: ReactSurfaceView? = null
|
|
47
47
|
private var reactSurfaceId: Int? = null
|
|
48
48
|
|
|
49
49
|
private var height: Int = 0
|
|
50
50
|
private var width: Int = 0
|
|
51
|
+
private var dpi: Int = 0
|
|
51
52
|
|
|
52
53
|
private var splashWillDisappear = false
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
|
-
* scale is the actual scale factor required to calculate proper insets and is passed in initialProperties to js side
|
|
56
|
+
* scale is the actual scale factor required to calculate proper insets and is passed in initialProperties to js side.
|
|
57
|
+
* The density is derived from the surface container's dpi so it always matches the VirtualDisplay,
|
|
58
|
+
* and is recomputed on every onSurfaceAvailable since the host may recreate the surface with new metrics.
|
|
56
59
|
*/
|
|
57
|
-
private
|
|
58
|
-
|
|
60
|
+
private var virtualScreenDensity = context.resources.displayMetrics.density
|
|
61
|
+
var scale = BuildConfig.SCALE_FACTOR * virtualScreenDensity
|
|
62
|
+
private set
|
|
59
63
|
|
|
60
64
|
private fun isSurfaceReady(surfaceContainer: SurfaceContainer): Boolean {
|
|
61
65
|
return surfaceContainer.surface != null && surfaceContainer.dpi != 0 && surfaceContainer.height != 0 && surfaceContainer.width != 0
|
|
@@ -68,7 +72,7 @@ class VirtualRenderer(
|
|
|
68
72
|
val areaDebouncer = Debouncer(200)
|
|
69
73
|
|
|
70
74
|
// 12dp seems to be the default margin on AA for the ETA widget and the maneuver so use it as fallback
|
|
71
|
-
val defaultMargin = (12.0 *
|
|
75
|
+
val defaultMargin get() = (12.0 * virtualScreenDensity).toInt()
|
|
72
76
|
var minMargin = Int.MAX_VALUE
|
|
73
77
|
var stableArea = Rect(0, 0, 0, 0)
|
|
74
78
|
var visibleArea = Rect(0, 0, 0, 0)
|
|
@@ -78,25 +82,16 @@ class VirtualRenderer(
|
|
|
78
82
|
return
|
|
79
83
|
}
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
virtualDisplay?.release()
|
|
83
|
-
virtualDisplay = manager.createVirtualDisplay(
|
|
84
|
-
moduleName,
|
|
85
|
-
surfaceContainer.width,
|
|
86
|
-
surfaceContainer.height,
|
|
87
|
-
surfaceContainer.dpi,
|
|
88
|
-
surfaceContainer.surface,
|
|
89
|
-
DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION,
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
val display = virtualDisplay?.display ?: return
|
|
93
|
-
height = surfaceContainer.height
|
|
94
|
-
width = surfaceContainer.width
|
|
95
|
-
|
|
96
|
-
initRenderer(display)
|
|
85
|
+
applySurface(surfaceContainer)
|
|
97
86
|
}
|
|
98
87
|
|
|
99
88
|
override fun onSurfaceDestroyed(surfaceContainer: SurfaceContainer) {
|
|
89
|
+
// the host re-emits visible/stable areas for the next surface, so drop state derived
|
|
90
|
+
// from the old surface instead of combining it with the new dimensions
|
|
91
|
+
minMargin = Int.MAX_VALUE
|
|
92
|
+
stableArea = Rect(0, 0, 0, 0)
|
|
93
|
+
visibleArea = Rect(0, 0, 0, 0)
|
|
94
|
+
|
|
100
95
|
virtualDisplay?.release()
|
|
101
96
|
virtualDisplay = null
|
|
102
97
|
}
|
|
@@ -230,6 +225,46 @@ class VirtualRenderer(
|
|
|
230
225
|
})
|
|
231
226
|
}
|
|
232
227
|
|
|
228
|
+
@MainThread
|
|
229
|
+
private fun applySurface(surfaceContainer: SurfaceContainer) {
|
|
230
|
+
val surfaceWidth = surfaceContainer.width
|
|
231
|
+
val surfaceHeight = surfaceContainer.height
|
|
232
|
+
|
|
233
|
+
// The host signals surface size changes only via onSurfaceDestroyed -> onSurfaceAvailable
|
|
234
|
+
// with new dimensions, so all size-derived state (layout params, scale, Fabric measure
|
|
235
|
+
// specs) must be recomputed when the metrics differ from the current ones. The react
|
|
236
|
+
// surface itself is kept alive so JS state survives the resize; the JS side is informed
|
|
237
|
+
// via the windowInformation listener since the window initial prop stays stale.
|
|
238
|
+
val isResize = reactSurfaceView != null && (width != surfaceWidth
|
|
239
|
+
|| height != surfaceHeight || dpi != surfaceContainer.dpi)
|
|
240
|
+
|
|
241
|
+
val manager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
|
242
|
+
virtualDisplay?.release()
|
|
243
|
+
virtualDisplay = manager.createVirtualDisplay(
|
|
244
|
+
moduleName,
|
|
245
|
+
surfaceWidth,
|
|
246
|
+
surfaceHeight,
|
|
247
|
+
surfaceContainer.dpi,
|
|
248
|
+
surfaceContainer.surface,
|
|
249
|
+
DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
val display = virtualDisplay?.display ?: return
|
|
253
|
+
height = surfaceHeight
|
|
254
|
+
width = surfaceWidth
|
|
255
|
+
dpi = surfaceContainer.dpi
|
|
256
|
+
virtualScreenDensity = surfaceContainer.dpi / 160f
|
|
257
|
+
scale = BuildConfig.SCALE_FACTOR * virtualScreenDensity
|
|
258
|
+
|
|
259
|
+
initRenderer(display)
|
|
260
|
+
|
|
261
|
+
if (isResize) {
|
|
262
|
+
getWindowInformation(moduleName)?.let {
|
|
263
|
+
HybridAndroidWindowInformation.emitWindowInformation(moduleName, it)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
233
268
|
private fun getMapTemplateConfig(): MapTemplateConfig? {
|
|
234
269
|
val screenManager = AndroidAutoScreen.getScreen(moduleName)?.screenManager ?: return null
|
|
235
270
|
val marker = screenManager.top.marker ?: return null
|
|
@@ -290,19 +325,48 @@ class VirtualRenderer(
|
|
|
290
325
|
val appTheme = context.applicationContext.applicationInfo.theme
|
|
291
326
|
val themedContext = ContextThemeWrapper(context.applicationContext, appTheme)
|
|
292
327
|
|
|
293
|
-
|
|
294
|
-
|
|
328
|
+
val surfaceImpl = reactSurfaceImpl ?: ReactSurfaceImpl(
|
|
329
|
+
themedContext, moduleName, initialProperties
|
|
330
|
+
).also {
|
|
331
|
+
reactSurfaceImpl = it
|
|
295
332
|
}
|
|
296
333
|
|
|
297
334
|
var splashScreenView: View? = null
|
|
298
335
|
|
|
299
336
|
reactSurfaceView?.let {
|
|
300
337
|
(it.parent as ViewGroup).removeView(it)
|
|
338
|
+
|
|
339
|
+
// the surface may have been recreated with new dimensions, so reapply all
|
|
340
|
+
// size-derived values and push the new layout specs into the running Fabric surface
|
|
341
|
+
it.layoutParams = FrameLayout.LayoutParams(
|
|
342
|
+
(width / reactNativeScale).toInt(), (height / reactNativeScale).toInt()
|
|
343
|
+
)
|
|
344
|
+
it.scaleX = reactNativeScale
|
|
345
|
+
it.scaleY = reactNativeScale
|
|
346
|
+
|
|
347
|
+
reactSurfaceId?.let { surfaceId ->
|
|
348
|
+
fabricUiManager.updateRootLayoutSpecs(
|
|
349
|
+
surfaceId,
|
|
350
|
+
View.MeasureSpec.makeMeasureSpec(
|
|
351
|
+
(width / reactNativeScale).toInt(), View.MeasureSpec.EXACTLY
|
|
352
|
+
),
|
|
353
|
+
View.MeasureSpec.makeMeasureSpec(
|
|
354
|
+
(height / reactNativeScale).toInt(), View.MeasureSpec.EXACTLY
|
|
355
|
+
),
|
|
356
|
+
0,
|
|
357
|
+
0
|
|
358
|
+
)
|
|
359
|
+
}
|
|
301
360
|
} ?: run {
|
|
361
|
+
// skip the splash when the surface is rebuilt after a resize, content is already loaded
|
|
302
362
|
splashScreenView =
|
|
303
|
-
if (isCluster
|
|
363
|
+
if (isCluster && !splashWillDisappear) {
|
|
364
|
+
getClusterSplashScreen(themedContext, height, width)
|
|
365
|
+
} else {
|
|
366
|
+
null
|
|
367
|
+
}
|
|
304
368
|
|
|
305
|
-
val surfaceView = ReactSurfaceView(themedContext,
|
|
369
|
+
val surfaceView = ReactSurfaceView(themedContext, surfaceImpl).apply {
|
|
306
370
|
layoutParams = FrameLayout.LayoutParams(
|
|
307
371
|
(width / reactNativeScale).toInt(), (height / reactNativeScale).toInt()
|
|
308
372
|
)
|
|
@@ -402,10 +466,7 @@ class VirtualRenderer(
|
|
|
402
466
|
}
|
|
403
467
|
|
|
404
468
|
@MainThread
|
|
405
|
-
private fun
|
|
406
|
-
virtualDisplay?.release()
|
|
407
|
-
virtualDisplay = null
|
|
408
|
-
|
|
469
|
+
private fun stopReactSurface() {
|
|
409
470
|
val uiManager = NitroModules.applicationContext?.let {
|
|
410
471
|
UIManagerHelper.getUIManager(
|
|
411
472
|
it, UIManagerType.FABRIC
|
|
@@ -420,6 +481,19 @@ class VirtualRenderer(
|
|
|
420
481
|
// Fabric already invalidated
|
|
421
482
|
} finally {
|
|
422
483
|
reactSurfaceId = null
|
|
484
|
+
reactSurfaceView = null
|
|
485
|
+
reactSurfaceImpl = null
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
@MainThread
|
|
490
|
+
private fun stop() {
|
|
491
|
+
virtualDisplay?.release()
|
|
492
|
+
virtualDisplay = null
|
|
493
|
+
|
|
494
|
+
try {
|
|
495
|
+
stopReactSurface()
|
|
496
|
+
} finally {
|
|
423
497
|
virtualRenderer.remove(moduleName)
|
|
424
498
|
}
|
|
425
499
|
}
|
|
@@ -433,6 +507,19 @@ class VirtualRenderer(
|
|
|
433
507
|
return virtualRenderer.contains(moduleId)
|
|
434
508
|
}
|
|
435
509
|
|
|
510
|
+
fun getWindowInformation(moduleId: String): WindowInformation? {
|
|
511
|
+
val renderer = virtualRenderer[moduleId] ?: return null
|
|
512
|
+
if (renderer.width == 0 || renderer.height == 0) {
|
|
513
|
+
return null
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return WindowInformation(
|
|
517
|
+
width = (renderer.width / renderer.scale).toInt().toDouble(),
|
|
518
|
+
height = (renderer.height / renderer.scale).toInt().toDouble(),
|
|
519
|
+
scale = renderer.scale.toDouble()
|
|
520
|
+
)
|
|
521
|
+
}
|
|
522
|
+
|
|
436
523
|
fun removeRenderer(moduleId: String) {
|
|
437
524
|
virtualRenderer[moduleId]?.stop()
|
|
438
525
|
virtualRenderer.remove(moduleId)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type Props = {
|
|
2
|
+
children: React.ReactNode;
|
|
3
|
+
moduleName: string;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* renders the passed children when the specified scene/screen appeared
|
|
7
|
+
* this makes sure child hooks are executed only when the map template is ready
|
|
8
|
+
*/
|
|
9
|
+
export default function OnAppearedChildRenderer({ children, moduleName }: Props): import("react").ReactNode;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
|
|
3
|
+
/**
|
|
4
|
+
* renders the passed children when the specified scene/screen appeared
|
|
5
|
+
* this makes sure child hooks are executed only when the map template is ready
|
|
6
|
+
*/
|
|
7
|
+
export default function OnAppearedChildRenderer({ children, moduleName }) {
|
|
8
|
+
const [didAppear, setDidAppear] = useState(false);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
let remove = HybridAutoPlay.addListenerRenderState(moduleName, (renderState) => {
|
|
11
|
+
if (renderState === 'didAppear') {
|
|
12
|
+
remove?.();
|
|
13
|
+
remove = null;
|
|
14
|
+
setDidAppear(true);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
return () => {
|
|
18
|
+
remove?.();
|
|
19
|
+
remove = null;
|
|
20
|
+
};
|
|
21
|
+
}, [moduleName]);
|
|
22
|
+
if (didAppear) {
|
|
23
|
+
return children;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { RootComponentInitialProps } from '../types/RootComponent';
|
|
3
|
+
/**
|
|
4
|
+
* Renders a user-provided root component and keeps its `window` prop up to date when the host
|
|
5
|
+
* recreates the render surface with new dimensions (some head units run Android Auto windowed
|
|
6
|
+
* and resize it at runtime), so consumers can simply read `props.window` without registering a
|
|
7
|
+
* listener themselves.
|
|
8
|
+
* On iOS this only passes the initial props through — CarPlay windows keep their size for the
|
|
9
|
+
* lifetime of a scene, so there is nothing to subscribe to.
|
|
10
|
+
*/
|
|
11
|
+
export declare function WindowInformationWrapper({ moduleName, component, componentProps, }: {
|
|
12
|
+
moduleName: string;
|
|
13
|
+
component: React.ComponentType<RootComponentInitialProps>;
|
|
14
|
+
componentProps: RootComponentInitialProps;
|
|
15
|
+
}): React.ReactElement<RootComponentInitialProps, string | React.JSXElementConstructor<any>>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createElement, useEffect, useState } from 'react';
|
|
2
|
+
import { HybridAndroidWindowInformation } from '../hybrid/HybridAndroidWindowInformation';
|
|
3
|
+
/**
|
|
4
|
+
* Renders a user-provided root component and keeps its `window` prop up to date when the host
|
|
5
|
+
* recreates the render surface with new dimensions (some head units run Android Auto windowed
|
|
6
|
+
* and resize it at runtime), so consumers can simply read `props.window` without registering a
|
|
7
|
+
* listener themselves.
|
|
8
|
+
* On iOS this only passes the initial props through — CarPlay windows keep their size for the
|
|
9
|
+
* lifetime of a scene, so there is nothing to subscribe to.
|
|
10
|
+
*/
|
|
11
|
+
export function WindowInformationWrapper({ moduleName, component, componentProps, }) {
|
|
12
|
+
const [window, setWindow] = useState(componentProps.window);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
return HybridAndroidWindowInformation?.addWindowInformationListener(moduleName, setWindow);
|
|
15
|
+
}, [moduleName]);
|
|
16
|
+
return createElement(component, { ...componentProps, window });
|
|
17
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A hook to determine if the CarPlay/Android Auto screen is currently focused (visible).
|
|
3
|
+
*
|
|
4
|
+
* @param moduleName The name of the module to listen to.
|
|
5
|
+
* @returns `true` if the screen is focused, `false` otherwise.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useIsAutoPlayFocused(moduleName: string): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { HybridAutoPlay } from '..';
|
|
3
|
+
/**
|
|
4
|
+
* A hook to determine if the CarPlay/Android Auto screen is currently focused (visible).
|
|
5
|
+
*
|
|
6
|
+
* @param moduleName The name of the module to listen to.
|
|
7
|
+
* @returns `true` if the screen is focused, `false` otherwise.
|
|
8
|
+
*/
|
|
9
|
+
export function useIsAutoPlayFocused(moduleName) {
|
|
10
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const remove = HybridAutoPlay.addListenerRenderState(moduleName, (state) => {
|
|
13
|
+
setIsFocused(state === 'didAppear');
|
|
14
|
+
});
|
|
15
|
+
return () => {
|
|
16
|
+
remove();
|
|
17
|
+
};
|
|
18
|
+
}, [moduleName]);
|
|
19
|
+
return isFocused;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const HybridAndroidWindowInformation = null;
|
package/lib/hybrid.d.ts
ADDED
package/lib/hybrid.js
ADDED
package/lib/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { HybridAndroidAutoTelemetry } from './hybrid/HybridAndroidAutoTelemetry';
|
|
2
|
+
import { HybridAndroidWindowInformation } from './hybrid/HybridAndroidWindowInformation';
|
|
2
3
|
import { HybridAutoPlay } from './hybrid/HybridAutoPlay';
|
|
3
4
|
import { HybridVoice } from './hybrid/HybridVoice';
|
|
4
5
|
import type { AndroidAutomotive } from './specs/AndroidAutomotive.nitro';
|
|
5
|
-
export { HybridAndroidAutoTelemetry, HybridAutoPlay, HybridVoice };
|
|
6
|
+
export { HybridAndroidAutoTelemetry, HybridAndroidWindowInformation, HybridAutoPlay, HybridVoice };
|
|
6
7
|
export declare const HybridAndroidAutomotive: AndroidAutomotive | null;
|
|
7
8
|
/**
|
|
8
9
|
* 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
|
@@ -2,10 +2,11 @@ import { Platform } from 'react-native';
|
|
|
2
2
|
import { NitroModules } from 'react-native-nitro-modules';
|
|
3
3
|
import AutoPlayHeadlessJsTask from './AutoPlayHeadlessJsTask';
|
|
4
4
|
import { HybridAndroidAutoTelemetry } from './hybrid/HybridAndroidAutoTelemetry';
|
|
5
|
+
import { HybridAndroidWindowInformation } from './hybrid/HybridAndroidWindowInformation';
|
|
5
6
|
import { HybridAutoPlay } from './hybrid/HybridAutoPlay';
|
|
6
7
|
import { HybridVoice } from './hybrid/HybridVoice';
|
|
7
8
|
AutoPlayHeadlessJsTask.registerHeadlessTask(HybridAutoPlay);
|
|
8
|
-
export { HybridAndroidAutoTelemetry, HybridAutoPlay, HybridVoice };
|
|
9
|
+
export { HybridAndroidAutoTelemetry, HybridAndroidWindowInformation, HybridAutoPlay, HybridVoice };
|
|
9
10
|
export const HybridAndroidAutomotive = Platform.OS === 'android'
|
|
10
11
|
? NitroModules.createHybridObject('AndroidAutomotive')
|
|
11
12
|
: null;
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { AppRegistry, Platform } from 'react-native';
|
|
3
3
|
import { NitroModules } from 'react-native-nitro-modules';
|
|
4
4
|
import { SafeAreaInsetsProvider } from '../components/SafeAreaInsetsContext';
|
|
5
|
+
import { WindowInformationWrapper } from '../components/WindowInformationWrapper';
|
|
5
6
|
import { NitroImageUtil } from '../utils/NitroImage';
|
|
6
7
|
const HybridCluster = NitroModules.createHybridObject('Cluster');
|
|
7
8
|
class Cluster {
|
|
@@ -49,7 +50,11 @@ class Cluster {
|
|
|
49
50
|
AppRegistry.registerComponent(clusterId, () => (props) => React.createElement(SafeAreaInsetsProvider, {
|
|
50
51
|
moduleName: clusterId,
|
|
51
52
|
// biome-ignore lint/correctness/noChildrenProp: there is no other way in a ts file
|
|
52
|
-
children: React.createElement(
|
|
53
|
+
children: React.createElement(WindowInformationWrapper, {
|
|
54
|
+
moduleName: clusterId,
|
|
55
|
+
component,
|
|
56
|
+
componentProps: props,
|
|
57
|
+
}),
|
|
53
58
|
}));
|
|
54
59
|
this.clusters[clusterId] = true;
|
|
55
60
|
await HybridCluster.initRootView(clusterId);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { HybridObject } from 'react-native-nitro-modules';
|
|
2
|
+
import type { CleanupCallback } from '../types/Event';
|
|
3
|
+
import type { WindowInformation } from '../types/RootComponent';
|
|
4
|
+
/**
|
|
5
|
+
* Android-only access to render-surface window dimensions. On iOS this module does not exist —
|
|
6
|
+
* use the platform-split `HybridAndroidWindowInformation` wrapper which resolves to `null` there.
|
|
7
|
+
*/
|
|
8
|
+
export interface AndroidWindowInformation extends HybridObject<{
|
|
9
|
+
android: 'kotlin';
|
|
10
|
+
}> {
|
|
11
|
+
/**
|
|
12
|
+
* callback for window dimension changes, fired when the host recreates the render surface
|
|
13
|
+
* with a different size (some head units run Android Auto windowed and resize it at runtime).
|
|
14
|
+
* Root components rendered via a template's `component` prop do NOT need this — their
|
|
15
|
+
* `window` prop is kept up to date automatically. Use this listener only for
|
|
16
|
+
* size-dependent logic living outside a root component.
|
|
17
|
+
* Fires with the last known window information immediately if the surface is already initialized.
|
|
18
|
+
* @param moduleName one of @AutoPlayModules or a cluster scene uuid
|
|
19
|
+
* @namespace Android
|
|
20
|
+
*/
|
|
21
|
+
addWindowInformationListener(moduleName: string, callback: (window: WindowInformation) => void): CleanupCallback;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { HybridObject } from 'react-native-nitro-modules';
|
|
2
|
+
import type { NitroAutomotivePermissionRequestTemplateConfig } from '../templates/AutomotivePermissionRequestTemplate';
|
|
3
|
+
import type { NitroTemplateConfig } from './AutoPlay.nitro';
|
|
4
|
+
interface AutomotivePermissionRequestTemplateConfig extends NitroTemplateConfig, NitroAutomotivePermissionRequestTemplateConfig {
|
|
5
|
+
}
|
|
6
|
+
export interface AutomotivePermissionRequestTemplate extends HybridObject<{
|
|
7
|
+
android: 'kotlin';
|
|
8
|
+
}> {
|
|
9
|
+
createAutomotivePermissionRequestTemplate(config: AutomotivePermissionRequestTemplateConfig): void;
|
|
10
|
+
}
|
|
11
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { HybridObject } from 'react-native-nitro-modules';
|
|
2
|
+
import type { NitroAutomotivePermissionRequestTemplateConfig } from '../templates/AutomotivePermissionRequestTemplate';
|
|
3
|
+
import type { NitroTemplateConfig } from './AutoPlay.nitro';
|
|
4
|
+
interface AutomotivePermissionRequestTemplateConfig extends NitroTemplateConfig, NitroAutomotivePermissionRequestTemplateConfig {
|
|
5
|
+
}
|
|
6
|
+
export interface AutomotivePermissionRequestTemplate extends HybridObject<{
|
|
7
|
+
android: 'kotlin';
|
|
8
|
+
}> {
|
|
9
|
+
createAutomotivePermissionRequestTemplate(config: AutomotivePermissionRequestTemplateConfig): void;
|
|
10
|
+
}
|
|
11
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CustomActionButtonAndroid } from '../types/Button';
|
|
2
|
+
import type { AutoText } from '../types/Text';
|
|
3
|
+
import { type NitroAction } from '../utils/NitroAction';
|
|
4
|
+
import { type HeaderActions, Template, type TemplateConfig } from './Template';
|
|
5
|
+
export interface NitroAutomotivePermissionRequestTemplateConfig extends TemplateConfig {
|
|
6
|
+
headerActions?: Array<NitroAction>;
|
|
7
|
+
title: AutoText;
|
|
8
|
+
actions: Array<NitroAction>;
|
|
9
|
+
}
|
|
10
|
+
export type AutomotivePermissionRequestTemplateConfig = Omit<NitroAutomotivePermissionRequestTemplateConfig, 'headerActions' | 'buttons'> & {
|
|
11
|
+
/**
|
|
12
|
+
* action buttons, usually at the the top right on Android and a top bar on iOS
|
|
13
|
+
*/
|
|
14
|
+
headerActions?: HeaderActions<AutomotivePermissionRequestTemplate>;
|
|
15
|
+
actions: [CustomActionButtonAndroid<AutomotivePermissionRequestTemplate>] | [
|
|
16
|
+
CustomActionButtonAndroid<AutomotivePermissionRequestTemplate>,
|
|
17
|
+
CustomActionButtonAndroid<AutomotivePermissionRequestTemplate>
|
|
18
|
+
];
|
|
19
|
+
};
|
|
20
|
+
export declare class AutomotivePermissionRequestTemplate extends Template<AutomotivePermissionRequestTemplateConfig, HeaderActions<AutomotivePermissionRequestTemplate>> {
|
|
21
|
+
private template;
|
|
22
|
+
constructor(config: AutomotivePermissionRequestTemplateConfig);
|
|
23
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { NitroModules } from 'react-native-nitro-modules';
|
|
2
|
+
import { NitroActionUtil } from '../utils/NitroAction';
|
|
3
|
+
import { Template, } from './Template';
|
|
4
|
+
const HybridAutomotivePermissionRequestTemplate = NitroModules.createHybridObject('AutomotivePermissionRequestTemplate');
|
|
5
|
+
export class AutomotivePermissionRequestTemplate extends Template {
|
|
6
|
+
template = this;
|
|
7
|
+
constructor(config) {
|
|
8
|
+
super(config);
|
|
9
|
+
const { headerActions, actions, ...rest } = config;
|
|
10
|
+
const nitroConfig = {
|
|
11
|
+
...rest,
|
|
12
|
+
id: this.id,
|
|
13
|
+
headerActions: NitroActionUtil.convert(this.template, headerActions),
|
|
14
|
+
actions: NitroActionUtil.convert(this.template, actions),
|
|
15
|
+
};
|
|
16
|
+
HybridAutomotivePermissionRequestTemplate.createAutomotivePermissionRequestTemplate(nitroConfig);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -3,6 +3,7 @@ import { AppRegistry, Platform } from 'react-native';
|
|
|
3
3
|
import { NitroModules } from 'react-native-nitro-modules';
|
|
4
4
|
import { MapTemplateProvider } from '../components/MapTemplateContext';
|
|
5
5
|
import { SafeAreaInsetsProvider } from '../components/SafeAreaInsetsContext';
|
|
6
|
+
import { WindowInformationWrapper } from '../components/WindowInformationWrapper';
|
|
6
7
|
import { HybridAutoPlay } from '../hybrid/HybridAutoPlay';
|
|
7
8
|
import { NitroActionUtil } from '../utils/NitroAction';
|
|
8
9
|
import { NitroAlertUtil } from '../utils/NitroAlert';
|
|
@@ -23,7 +24,11 @@ export class MapTemplate extends Template {
|
|
|
23
24
|
children: React.createElement(SafeAreaInsetsProvider, {
|
|
24
25
|
moduleName: this.id,
|
|
25
26
|
// biome-ignore lint/correctness/noChildrenProp: there is no other way in a ts file
|
|
26
|
-
children: React.createElement(
|
|
27
|
+
children: React.createElement(WindowInformationWrapper, {
|
|
28
|
+
moduleName: this.id,
|
|
29
|
+
component,
|
|
30
|
+
componentProps: props,
|
|
31
|
+
}),
|
|
27
32
|
}),
|
|
28
33
|
}));
|
|
29
34
|
const nitroConfig = {
|