@iternio/react-native-auto-play 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +56 -7
  2. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/Parser.kt +49 -14
  3. package/jest.js +2 -0
  4. package/lib/index.web.d.ts +73 -0
  5. package/lib/index.web.js +72 -1
  6. package/lib/types/Image.d.ts +6 -1
  7. package/lib/utils/NitroColor.d.ts +1 -0
  8. package/lib/utils/NitroColor.js +8 -0
  9. package/lib/utils/NitroImage.js +1 -1
  10. package/lib/utils/NitroManeuver.js +1 -3
  11. package/lib-cjs/AutoPlayHeadlessJsTask.js +16 -0
  12. package/lib-cjs/Constants.js +9 -0
  13. package/lib-cjs/components/MapTemplateContext.js +10 -0
  14. package/lib-cjs/components/SafeAreaInsetsContext.js +21 -0
  15. package/lib-cjs/components/SafeAreaView.js +22 -0
  16. package/lib-cjs/components/WindowInformationWrapper.js +20 -0
  17. package/lib-cjs/hooks/useAndroidAutoTelemetry.js +115 -0
  18. package/lib-cjs/hooks/useFocusedEffect.js +35 -0
  19. package/lib-cjs/hooks/useMapTemplate.js +13 -0
  20. package/lib-cjs/hooks/useSafeAreaInsets.js +9 -0
  21. package/lib-cjs/hooks/useVoiceInput.js +42 -0
  22. package/lib-cjs/hybrid/HybridAndroidAutoTelemetry.js +4 -0
  23. package/lib-cjs/hybrid/HybridAndroidWindowInformation.js +4 -0
  24. package/lib-cjs/hybrid/HybridAutoPlay.js +5 -0
  25. package/lib-cjs/hybrid/HybridVoice.js +61 -0
  26. package/lib-cjs/index.js +76 -0
  27. package/lib-cjs/index.web.js +98 -0
  28. package/lib-cjs/scenes/AutoPlayCluster.js +140 -0
  29. package/lib-cjs/scenes/CarPlayDashboardScene.js +100 -0
  30. package/lib-cjs/specs/AndroidAutoTelemetry.nitro.js +2 -0
  31. package/lib-cjs/specs/AndroidAutomotive.nitro.js +16 -0
  32. package/lib-cjs/specs/AndroidWindowInformation.nitro.js +2 -0
  33. package/lib-cjs/specs/AutoPlay.nitro.js +2 -0
  34. package/lib-cjs/specs/CarPlayDashboard.nitro.js +2 -0
  35. package/lib-cjs/specs/Cluster.nitro.js +2 -0
  36. package/lib-cjs/specs/GridTemplate.nitro.js +2 -0
  37. package/lib-cjs/specs/InformationTemplate.nitro.js +2 -0
  38. package/lib-cjs/specs/ListTemplate.nitro.js +2 -0
  39. package/lib-cjs/specs/MapTemplate.nitro.js +2 -0
  40. package/lib-cjs/specs/MessageTemplate.nitro.js +2 -0
  41. package/lib-cjs/specs/SearchTemplate.nitro.js +2 -0
  42. package/lib-cjs/specs/SignInTemplate.nitro.js +2 -0
  43. package/lib-cjs/specs/Voice.nitro.js +2 -0
  44. package/lib-cjs/templates/GridTemplate.js +41 -0
  45. package/lib-cjs/templates/InformationTemplate.js +49 -0
  46. package/lib-cjs/templates/ListTemplate.js +33 -0
  47. package/lib-cjs/templates/MapTemplate.js +168 -0
  48. package/lib-cjs/templates/MessageTemplate.js +50 -0
  49. package/lib-cjs/templates/SearchTemplate.js +32 -0
  50. package/lib-cjs/templates/SignInTemplate.js +55 -0
  51. package/lib-cjs/templates/Template.js +40 -0
  52. package/lib-cjs/types/Button.js +12 -0
  53. package/lib-cjs/types/Event.js +2 -0
  54. package/lib-cjs/types/Image.js +2 -0
  55. package/lib-cjs/types/Maneuver.js +77 -0
  56. package/lib-cjs/types/RootComponent.js +2 -0
  57. package/lib-cjs/types/SignInMethod.js +22 -0
  58. package/lib-cjs/types/Telemetry.js +24 -0
  59. package/lib-cjs/types/Text.js +8 -0
  60. package/lib-cjs/types/Trip.js +2 -0
  61. package/lib-cjs/types/Voice.js +2 -0
  62. package/lib-cjs/utils/ErrorUtil.js +34 -0
  63. package/lib-cjs/utils/NitroAction.js +129 -0
  64. package/lib-cjs/utils/NitroAlert.js +19 -0
  65. package/lib-cjs/utils/NitroAttributedString.js +14 -0
  66. package/lib-cjs/utils/NitroColor.js +32 -0
  67. package/lib-cjs/utils/NitroGrid.js +12 -0
  68. package/lib-cjs/utils/NitroImage.js +97 -0
  69. package/lib-cjs/utils/NitroManeuver.js +79 -0
  70. package/lib-cjs/utils/NitroMapButton.js +33 -0
  71. package/lib-cjs/utils/NitroOptionsPanel.js +66 -0
  72. package/lib-cjs/utils/NitroSection.js +112 -0
  73. package/nitrogen/generated/android/c++/JNitroColor.hpp +8 -4
  74. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/NitroColor.kt +9 -4
  75. package/nitrogen/generated/ios/swift/NitroColor.swift +20 -2
  76. package/nitrogen/generated/shared/c++/NitroColor.hpp +7 -3
  77. package/package.json +7 -3
  78. package/src/index.web.ts +166 -0
  79. package/src/types/Image.ts +6 -1
  80. package/src/utils/NitroColor.ts +10 -1
  81. package/src/utils/NitroImage.ts +1 -5
  82. package/src/utils/NitroManeuver.ts +1 -4
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NitroOptionsPanelUtil = void 0;
4
+ const NitroAction_1 = require("./NitroAction");
5
+ const NitroGrid_1 = require("./NitroGrid");
6
+ const NitroImage_1 = require("./NitroImage");
7
+ const NitroSection_1 = require("./NitroSection");
8
+ const isRadioSection = (items) => {
9
+ return items.length > 0 && items.every((item) => item.type === 'radio');
10
+ };
11
+ const convertSection = (template, section) => {
12
+ if (section.type === 'grid') {
13
+ return {
14
+ title: section.title,
15
+ buttons: NitroGrid_1.NitroGridUtil.convert(template, section.buttons),
16
+ };
17
+ }
18
+ if (section.type === 'charger') {
19
+ const { location } = section;
20
+ return {
21
+ title: section.title,
22
+ outlets: section.outlets.map((outlet) => ({
23
+ connector: outlet.connector,
24
+ voltage: outlet.voltage,
25
+ powerKw: outlet.powerKw,
26
+ onPress: outlet.onPress ? () => outlet.onPress?.(template) : undefined,
27
+ })),
28
+ location: location
29
+ ? {
30
+ name: location.name,
31
+ address: location.address,
32
+ coordinate: location.coordinate,
33
+ distance: location.travelEstimates.distance,
34
+ duration: location.travelEstimates.duration,
35
+ visible: location.travelEstimates.visible,
36
+ image: NitroImage_1.NitroImageUtil.convert(location.image),
37
+ onPress: location.onPress ? () => location.onPress?.(template) : undefined,
38
+ }
39
+ : undefined,
40
+ };
41
+ }
42
+ const { items, title = '' } = section;
43
+ const multiSection = isRadioSection(items)
44
+ ? { type: 'radio', title, items }
45
+ : {
46
+ type: 'default',
47
+ title,
48
+ items,
49
+ };
50
+ const [nitroSection] = NitroSection_1.NitroSectionUtil.convert(template, [multiSection]) ?? [];
51
+ if (nitroSection == null) {
52
+ throw new Error('converting sections failed');
53
+ }
54
+ return nitroSection;
55
+ };
56
+ const convert = (template, config) => {
57
+ if (config == null) {
58
+ return undefined;
59
+ }
60
+ return {
61
+ title: config.title,
62
+ sections: config.sections.map((section) => convertSection(template, section)),
63
+ actions: NitroAction_1.NitroActionUtil.convert(template, config.actions),
64
+ };
65
+ };
66
+ exports.NitroOptionsPanelUtil = { convert };
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NitroSectionUtil = void 0;
4
+ const Text_1 = require("../types/Text");
5
+ const NitroImage_1 = require("./NitroImage");
6
+ const validateRadioItems = (type, items) => {
7
+ if (__DEV__ &&
8
+ type === 'radio' &&
9
+ (items.filter((item) => item.selected).length > 1 || items.every((item) => !item.selected))) {
10
+ throw new Error('radio lists must have one selected item');
11
+ }
12
+ };
13
+ const convert = (template, sections) => {
14
+ if (sections == null) {
15
+ return undefined;
16
+ }
17
+ if (Array.isArray(sections)) {
18
+ return sections.map((section) => {
19
+ const { title, type } = section;
20
+ const items = section.items.map((item) => convertRow(template, item));
21
+ validateRadioItems(type, items);
22
+ return {
23
+ items,
24
+ type,
25
+ title,
26
+ };
27
+ });
28
+ }
29
+ const items = sections.items.map((item) => convertRow(template, item));
30
+ validateRadioItems(sections.type, items);
31
+ return [
32
+ {
33
+ items,
34
+ type: sections.type,
35
+ },
36
+ ];
37
+ };
38
+ /**
39
+ * `AutoText.distance`/`.duration` only take effect if `text` actually uses the matching
40
+ * `TextPlaceholders` marker — populate whichever of those the text references, leaving the rest
41
+ * of `text` untouched, so a title/address that doesn't opt in via a placeholder is unaffected.
42
+ */
43
+ const withTravelEstimates = (text, distance, durationSeconds) => {
44
+ const hasDistance = text.text.includes(Text_1.TextPlaceholders.Distance);
45
+ const hasDuration = text.text.includes(Text_1.TextPlaceholders.Duration);
46
+ if (!hasDistance && !hasDuration) {
47
+ return text;
48
+ }
49
+ return {
50
+ ...text,
51
+ distance: hasDistance ? distance : text.distance,
52
+ duration: hasDuration ? durationSeconds : text.duration,
53
+ };
54
+ };
55
+ const convertRow = (template, item) => {
56
+ const { type, enabled = true, image, imageType } = item;
57
+ // `WaypointRow` has no `detailedText` of its own — `address` doubles as the detail line
58
+ // whenever this falls back to a plain row (non-panel context, Android).
59
+ let title = item.title;
60
+ let detailedText = item.type === 'waypoint'
61
+ ? item.address != null
62
+ ? { text: item.address }
63
+ : undefined
64
+ : 'detailedText' in item
65
+ ? item.detailedText
66
+ : undefined;
67
+ // Opt-in only: a title/address using `{distance}`/`{duration}` gets those values filled in,
68
+ // via the same substitution `AutoText` already does everywhere else — no separate "estimate"
69
+ // UI element needed for a row embedded in a list alongside other rows. Deliberately NOT gated
70
+ // on `travelEstimates.visible` — that flag only controls the panel-only sibling
71
+ // `CPTravelEstimates` item (see `Parser.swift`'s `travelEstimatesVisible` check), which has no
72
+ // bearing on the non-panel/Android fallback text this feeds; a placeholder in the text is
73
+ // already its own explicit opt-in regardless of `visible`.
74
+ if (item.type === 'waypoint') {
75
+ const { distance, duration } = item.travelEstimates;
76
+ title = withTravelEstimates(title, distance, duration.seconds);
77
+ if (detailedText != null) {
78
+ detailedText = withTravelEstimates(detailedText, distance, duration.seconds);
79
+ }
80
+ }
81
+ const selected = type === 'radio' ? (item.selected ?? false) : undefined;
82
+ const onTogglePress = item.type === 'toggle' ? item.onPress : undefined;
83
+ const onRowPress = item.type !== 'text' && item.type !== 'toggle' ? item.onPress : undefined;
84
+ const onPress = item.type === 'text'
85
+ ? undefined
86
+ : (checked) => {
87
+ if (onTogglePress != null && checked != null) {
88
+ onTogglePress(template, checked);
89
+ return;
90
+ }
91
+ if (onRowPress != null) {
92
+ onRowPress(template);
93
+ }
94
+ };
95
+ return {
96
+ browsable: type === 'default' ? item.browsable : undefined,
97
+ detailedText,
98
+ enabled,
99
+ image: NitroImage_1.NitroImageUtil.convert(image),
100
+ imageType,
101
+ title,
102
+ checked: type === 'toggle' ? item.checked : undefined,
103
+ onPress,
104
+ selected,
105
+ coordinate: item.type === 'waypoint' ? item.coordinate : undefined,
106
+ distance: item.type === 'waypoint' ? item.travelEstimates.distance : undefined,
107
+ duration: item.type === 'waypoint' ? item.travelEstimates.duration : undefined,
108
+ travelEstimatesVisible: item.type === 'waypoint' ? item.travelEstimates.visible : undefined,
109
+ address: item.type === 'waypoint' ? item.address : undefined,
110
+ };
111
+ };
112
+ exports.NitroSectionUtil = { convert };
@@ -10,7 +10,7 @@
10
10
  #include <fbjni/fbjni.h>
11
11
  #include "NitroColor.hpp"
12
12
 
13
-
13
+ #include <optional>
14
14
 
15
15
  namespace margelo::nitro::swe::iternio::reactnativeautoplay {
16
16
 
@@ -35,9 +35,12 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
35
35
  double lightColor = this->getFieldValue(fieldLightColor);
36
36
  static const auto fieldDarkColor = clazz->getField<double>("darkColor");
37
37
  double darkColor = this->getFieldValue(fieldDarkColor);
38
+ static const auto fieldIsDefault = clazz->getField<jni::JBoolean>("isDefault");
39
+ jni::local_ref<jni::JBoolean> isDefault = this->getFieldValue(fieldIsDefault);
38
40
  return NitroColor(
39
41
  lightColor,
40
- darkColor
42
+ darkColor,
43
+ isDefault != nullptr ? std::make_optional(static_cast<bool>(isDefault->value())) : std::nullopt
41
44
  );
42
45
  }
43
46
 
@@ -47,13 +50,14 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
47
50
  */
48
51
  [[maybe_unused]]
49
52
  static jni::local_ref<JNitroColor::javaobject> fromCpp(const NitroColor& value) {
50
- using JSignature = JNitroColor(double, double);
53
+ using JSignature = JNitroColor(double, double, jni::alias_ref<jni::JBoolean>);
51
54
  static const auto clazz = javaClassStatic();
52
55
  static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
53
56
  return create(
54
57
  clazz,
55
58
  value.lightColor,
56
- value.darkColor
59
+ value.darkColor,
60
+ value.isDefault.has_value() ? jni::JBoolean::valueOf(value.isDefault.value()) : nullptr
57
61
  );
58
62
  }
59
63
  };
@@ -23,7 +23,10 @@ data class NitroColor(
23
23
  val lightColor: Double,
24
24
  @DoNotStrip
25
25
  @Keep
26
- val darkColor: Double
26
+ val darkColor: Double,
27
+ @DoNotStrip
28
+ @Keep
29
+ val isDefault: Boolean?
27
30
  ) {
28
31
  /* primary constructor */
29
32
 
@@ -32,12 +35,14 @@ data class NitroColor(
32
35
  if (other !is NitroColor) return false
33
36
  return Objects.deepEquals(this.lightColor, other.lightColor)
34
37
  && Objects.deepEquals(this.darkColor, other.darkColor)
38
+ && Objects.deepEquals(this.isDefault, other.isDefault)
35
39
  }
36
40
 
37
41
  override fun hashCode(): Int {
38
42
  return arrayOf<Any?>(
39
43
  lightColor,
40
- darkColor
44
+ darkColor,
45
+ isDefault
41
46
  ).contentDeepHashCode()
42
47
  }
43
48
 
@@ -49,8 +54,8 @@ data class NitroColor(
49
54
  @Keep
50
55
  @Suppress("unused")
51
56
  @JvmStatic
52
- private fun fromCpp(lightColor: Double, darkColor: Double): NitroColor {
53
- return NitroColor(lightColor, darkColor)
57
+ private fun fromCpp(lightColor: Double, darkColor: Double, isDefault: Boolean?): NitroColor {
58
+ return NitroColor(lightColor, darkColor, isDefault)
54
59
  }
55
60
  }
56
61
  }
@@ -18,8 +18,14 @@ public extension NitroColor {
18
18
  /**
19
19
  * Create a new instance of `NitroColor`.
20
20
  */
21
- init(lightColor: Double, darkColor: Double) {
22
- self.init(lightColor, darkColor)
21
+ init(lightColor: Double, darkColor: Double, isDefault: Bool?) {
22
+ self.init(lightColor, darkColor, { () -> bridge.std__optional_bool_ in
23
+ if let __unwrappedValue = isDefault {
24
+ return bridge.create_std__optional_bool_(__unwrappedValue)
25
+ } else {
26
+ return .init()
27
+ }
28
+ }())
23
29
  }
24
30
 
25
31
  @inline(__always)
@@ -31,4 +37,16 @@ public extension NitroColor {
31
37
  var darkColor: Double {
32
38
  return self.__darkColor
33
39
  }
40
+
41
+ @inline(__always)
42
+ var isDefault: Bool? {
43
+ return { () -> Bool? in
44
+ if bridge.has_value_std__optional_bool_(self.__isDefault) {
45
+ let __unwrapped = bridge.get_std__optional_bool_(self.__isDefault)
46
+ return __unwrapped
47
+ } else {
48
+ return nil
49
+ }
50
+ }()
51
+ }
34
52
  }
@@ -30,7 +30,7 @@
30
30
 
31
31
 
32
32
 
33
-
33
+ #include <optional>
34
34
 
35
35
  namespace margelo::nitro::swe::iternio::reactnativeautoplay {
36
36
 
@@ -41,10 +41,11 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
41
41
  public:
42
42
  double lightColor SWIFT_PRIVATE;
43
43
  double darkColor SWIFT_PRIVATE;
44
+ std::optional<bool> isDefault SWIFT_PRIVATE;
44
45
 
45
46
  public:
46
47
  NitroColor() = default;
47
- explicit NitroColor(double lightColor, double darkColor): lightColor(lightColor), darkColor(darkColor) {}
48
+ explicit NitroColor(double lightColor, double darkColor, std::optional<bool> isDefault): lightColor(lightColor), darkColor(darkColor), isDefault(isDefault) {}
48
49
 
49
50
  public:
50
51
  friend bool operator==(const NitroColor& lhs, const NitroColor& rhs) = default;
@@ -61,13 +62,15 @@ namespace margelo::nitro {
61
62
  jsi::Object obj = arg.asObject(runtime);
62
63
  return margelo::nitro::swe::iternio::reactnativeautoplay::NitroColor(
63
64
  JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lightColor"))),
64
- JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "darkColor")))
65
+ JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "darkColor"))),
66
+ JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "isDefault")))
65
67
  );
66
68
  }
67
69
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::swe::iternio::reactnativeautoplay::NitroColor& arg) {
68
70
  jsi::Object obj(runtime);
69
71
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "lightColor"), JSIConverter<double>::toJSI(runtime, arg.lightColor));
70
72
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "darkColor"), JSIConverter<double>::toJSI(runtime, arg.darkColor));
73
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "isDefault"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.isDefault));
71
74
  return obj;
72
75
  }
73
76
  static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
@@ -80,6 +83,7 @@ namespace margelo::nitro {
80
83
  }
81
84
  if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lightColor")))) return false;
82
85
  if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "darkColor")))) return false;
86
+ if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "isDefault")))) return false;
83
87
  return true;
84
88
  }
85
89
  };
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@iternio/react-native-auto-play",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Android Auto and Apple CarPlay for react-native",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",
7
+ "browser": "lib/index.web",
7
8
  "types": "lib/index.d.ts",
8
9
  "react-native": "src/index",
9
10
  "source": "src/index",
@@ -11,6 +12,8 @@
11
12
  "src",
12
13
  "react-native.config.js",
13
14
  "lib",
15
+ "lib-cjs",
16
+ "jest.js",
14
17
  "nitrogen",
15
18
  "android/build.gradle",
16
19
  "android/gradle.properties",
@@ -30,12 +33,13 @@
30
33
  ],
31
34
  "scripts": {
32
35
  "typecheck": "tsc --noEmit",
33
- "clean": "rm -rf android/build node_modules/**/android/build lib",
36
+ "clean": "rm -rf android/build node_modules/**/android/build lib lib-cjs",
34
37
  "lint": "biome check src",
35
38
  "lint-ci": "biome ci src --reporter=summary",
36
39
  "typescript": "tsc",
40
+ "build:jest": "tsc -p tsconfig.jest.json",
37
41
  "specs": "nitrogen --logLevel=\"debug\"",
38
- "prepare": "yarn circular && tsc && nitrogen",
42
+ "prepare": "yarn circular && tsc && yarn build:jest && nitrogen",
39
43
  "swift:format": "swift-format format ./ios --in-place --recursive",
40
44
  "circular": "dpdm ./src/* --circular --exit-code circular:1 --no-tree --warning=false --transform"
41
45
  },
package/src/index.web.ts CHANGED
@@ -0,0 +1,166 @@
1
+ // Web/Jest stub for the native CarPlay/Android Auto surface. Nothing here talks to a real native
2
+ // module — every function call resolves to `undefined` (or an already-resolved promise, which is
3
+ // indistinguishable from `undefined` to an `await`er) rather than throwing. Types are preserved by
4
+ // casting against the real exports, so callers still get full type-checking/autocomplete.
5
+ //
6
+ // Anything below that's already safe as-is (pure types, plain enums, or modules that neither
7
+ // create native objects nor import anything that does) is re-exported directly instead of stubbed.
8
+
9
+ import { createElement } from 'react';
10
+ import { View, type ViewProps } from 'react-native';
11
+ import type { AndroidAutomotive } from './specs/AndroidAutomotive.nitro';
12
+ import type { AutoPlay } from './specs/AutoPlay.nitro';
13
+ import type { Voice } from './specs/Voice.nitro';
14
+ import type { GridTemplate as RealGridTemplate } from './templates/GridTemplate';
15
+ import type { InformationTemplate as RealInformationTemplate } from './templates/InformationTemplate';
16
+ import type { ListTemplate as RealListTemplate } from './templates/ListTemplate';
17
+ import type { MapTemplate as RealMapTemplate } from './templates/MapTemplate';
18
+ import type { MessageTemplate as RealMessageTemplate } from './templates/MessageTemplate';
19
+ import type { SearchTemplate as RealSearchTemplate } from './templates/SearchTemplate';
20
+ import type { SignInTemplate as RealSignInTemplate } from './templates/SignInTemplate';
21
+ import type { Template as RealTemplate } from './templates/Template';
22
+ import type { SafeAreaInsets } from './types/Event';
23
+ import type { Telemetry } from './types/Telemetry';
24
+
25
+ function noop() {
26
+ return undefined;
27
+ }
28
+
29
+ /** Any property access returns a no-op function, so any method call is safe regardless of name. */
30
+ function createNoopObject<T>(): T {
31
+ return new Proxy(
32
+ {},
33
+ {
34
+ get: (_target, prop) => (prop === 'then' || prop === 'toJSON' ? undefined : noop),
35
+ }
36
+ ) as T;
37
+ }
38
+
39
+ function createNoopClass<T>(): new (...args: never[]) => T {
40
+ function NoopInstance() {
41
+ return createNoopObject<T>();
42
+ }
43
+ return NoopInstance as unknown as new (
44
+ ...args: never[]
45
+ ) => T;
46
+ }
47
+
48
+ export * from './Constants';
49
+ export * from './hooks/useMapTemplate';
50
+ export type {
51
+ ActiveCarUxRestrictions,
52
+ AppFocusState,
53
+ } from './specs/AndroidAutomotive.nitro';
54
+ export { CarUxRestrictions } from './specs/AndroidAutomotive.nitro';
55
+ export type { GridImageSize, GridTemplateConfig } from './templates/GridTemplate';
56
+ export type { InformationItems, InformationTemplateConfig } from './templates/InformationTemplate';
57
+ export type {
58
+ DefaultRow,
59
+ ListImageType,
60
+ ListTemplateConfig,
61
+ MultiSection,
62
+ RadioRow,
63
+ Section,
64
+ SingleSection,
65
+ TextRow,
66
+ ToggleRow,
67
+ WaypointCoordinate,
68
+ WaypointRow,
69
+ } from './templates/ListTemplate';
70
+ export type {
71
+ BaseMapTemplateConfig,
72
+ ChargerLocation,
73
+ ChargerOutlet,
74
+ ChargingConnector,
75
+ HeaderActionsAndroidMap,
76
+ MapButtons,
77
+ MapHeaderActions,
78
+ MapTemplateConfig,
79
+ OptionsPanelChargerSection,
80
+ OptionsPanelConfig,
81
+ OptionsPanelGridSection,
82
+ OptionsPanelListSection,
83
+ OptionsPanelSection,
84
+ PanelActionsIos,
85
+ PanelHeaderActions,
86
+ Point,
87
+ TripSelectorCallback,
88
+ VisibleTravelEstimate,
89
+ } from './templates/MapTemplate';
90
+ export type { MessageTemplateConfig } from './templates/MessageTemplate';
91
+ export type { SearchSection, SearchTemplateConfig } from './templates/SearchTemplate';
92
+ export type { SignInTemplateConfig } from './templates/SignInTemplate';
93
+ export type {
94
+ ActionButton,
95
+ HeaderActions,
96
+ HeaderActionsAndroid,
97
+ HeaderActionsIos,
98
+ NitroBaseMapTemplateConfig,
99
+ NitroTemplateConfig,
100
+ TemplateConfig,
101
+ } from './templates/Template';
102
+ export * from './types/Button';
103
+ export * from './types/Event';
104
+ export * from './types/Image';
105
+ export * from './types/Maneuver';
106
+ export * from './types/RootComponent';
107
+ export * from './types/SignInMethod';
108
+ export * from './types/Telemetry';
109
+ export * from './types/Text';
110
+ export * from './types/Trip';
111
+ export type { VoiceInputChunk, VoiceInputOptions, VoiceInputResult } from './types/Voice';
112
+ export * from './utils/ErrorUtil';
113
+ export type {
114
+ AlertPriority,
115
+ NavigationAlert as Alert,
116
+ NavigationAlertAction as AlertAction,
117
+ } from './utils/NitroAlert';
118
+ export type { ThemedColor } from './utils/NitroColor';
119
+ export type { GridButton } from './utils/NitroGrid';
120
+
121
+ export enum AutoPlayModules {
122
+ App = 'main',
123
+ AutoPlayRoot = 'AutoPlayRoot',
124
+ CarPlayDashboard = 'CarPlayDashboard',
125
+ }
126
+
127
+ const NO_INSETS: SafeAreaInsets = { top: 0, bottom: 0, left: 0, right: 0 };
128
+ export const useSafeAreaInsets = () => NO_INSETS;
129
+ export const SafeAreaView = (props: ViewProps) => createElement(View, props);
130
+
131
+ export function setIconFont(_name: string, _glyphMap?: Record<string, number>): void {}
132
+
133
+ export const useFocusedEffect = () => {};
134
+
135
+ export const useVoiceInput = () => ({
136
+ voiceInputResult: undefined,
137
+ resetVoiceInputResult: () => {},
138
+ });
139
+
140
+ export const useAndroidAutoTelemetry = (): {
141
+ permissionsGranted: boolean | null;
142
+ telemetry: Telemetry | undefined;
143
+ error: string | undefined;
144
+ } => ({
145
+ permissionsGranted: null,
146
+ telemetry: undefined,
147
+ error: undefined,
148
+ });
149
+
150
+ export const AutoPlayCluster = createNoopObject<Record<string, unknown>>();
151
+ export const CarPlayDashboard = createNoopObject<Record<string, unknown>>();
152
+
153
+ export const HybridAndroidAutomotive: AndroidAutomotive | null = null;
154
+ export { HybridAndroidAutoTelemetry } from './hybrid/HybridAndroidAutoTelemetry';
155
+ export { HybridAndroidWindowInformation } from './hybrid/HybridAndroidWindowInformation';
156
+ export const HybridAutoPlay = createNoopObject<AutoPlay>();
157
+ export const HybridVoice = createNoopObject<Voice>();
158
+
159
+ export const Template = createNoopClass<InstanceType<typeof RealTemplate>>();
160
+ export const ListTemplate = createNoopClass<RealListTemplate>();
161
+ export const GridTemplate = createNoopClass<RealGridTemplate>();
162
+ export const InformationTemplate = createNoopClass<RealInformationTemplate>();
163
+ export const MessageTemplate = createNoopClass<RealMessageTemplate>();
164
+ export const MapTemplate = createNoopClass<RealMapTemplate>();
165
+ export const SearchTemplate = createNoopClass<RealSearchTemplate>();
166
+ export const SignInTemplate = createNoopClass<RealSignInTemplate>();
@@ -24,7 +24,8 @@ export type GlyphMapKey = keyof AutoPlayGlyphMap extends never
24
24
  type GlyphStyleFields = {
25
25
  /**
26
26
  * Sets the icon dark and light mode color or a single color for both.
27
- * Defaults to white for dark mode and black for light mode if not specified.
27
+ * Defaults to `'default'` if not specified, which is white for dark mode and black for light mode.
28
+ * On Android Auto the host tint that follows the template is not applied when `backgroundColor` is not transparent.
28
29
  * Might not get applied everywhere like MapTemplate buttons on Android.
29
30
  */
30
31
  color?: ThemedColor | string;
@@ -61,6 +62,8 @@ export type AutoImage =
61
62
  /**
62
63
  * if specified the image gets tinted, if not it will just use the original image
63
64
  * Might not get applied everywhere like MapTemplate buttons on Android.
65
+ * Use `'default'` for monochrome icons that must stay readable on both dark and light
66
+ * Android Auto templates (17.8+), see README.
64
67
  */
65
68
  color?: ThemedColor | string;
66
69
  type: 'asset';
@@ -70,6 +73,8 @@ export type AutoImage =
70
73
  uri: string;
71
74
  /**
72
75
  * if specified the image gets tinted, if not it will just use the original image
76
+ * Use `'default'` for monochrome icons that must stay readable on both dark and light
77
+ * Android Auto templates (17.8+), see README.
73
78
  */
74
79
  color?: ThemedColor | string;
75
80
  /**
@@ -1,8 +1,14 @@
1
1
  import { processColor } from 'react-native';
2
2
 
3
- export type NitroColor = { lightColor: number; darkColor: number };
3
+ export type NitroColor = { lightColor: number; darkColor: number; isDefault?: boolean };
4
4
  export type ThemedColor = { lightColor: string; darkColor: string };
5
5
 
6
+ const DefaultNitroColor = {
7
+ darkColor: convertColor('white'),
8
+ lightColor: convertColor('black'),
9
+ isDefault: true,
10
+ };
11
+
6
12
  function convertColor(color: string): number;
7
13
  function convertColor(color?: string): number | undefined;
8
14
  function convertColor(color?: string): number | undefined {
@@ -18,6 +24,9 @@ function convert(color?: ThemedColor | string): NitroColor | undefined {
18
24
  }
19
25
 
20
26
  if (typeof color === 'string') {
27
+ if (color === 'default') {
28
+ return DefaultNitroColor;
29
+ }
21
30
  const convertedColor = convertColor(color);
22
31
  return { darkColor: convertedColor, lightColor: convertedColor };
23
32
  }
@@ -96,11 +96,7 @@ function convert(image?: AutoImage): NitroImage | undefined {
96
96
  }
97
97
 
98
98
  if (image.type === 'glyph') {
99
- const {
100
- color = { darkColor: 'white', lightColor: 'black' },
101
- fontScale,
102
- backgroundColor = 'transparent',
103
- } = image;
99
+ const { color = 'default', fontScale, backgroundColor = 'transparent' } = image;
104
100
 
105
101
  return {
106
102
  glyph: resolveGlyph(image),
@@ -71,10 +71,7 @@ function convertManeuverImage(image?: ManeuverImage): NitroImage | undefined {
71
71
  }
72
72
 
73
73
  if (image.type === 'glyph') {
74
- const color =
75
- typeof image.color === 'string'
76
- ? image.color
77
- : (image.color ?? { darkColor: 'white', lightColor: 'black' });
74
+ const color = typeof image.color === 'string' ? image.color : (image.color ?? 'default');
78
75
  return NitroImageUtil.convert({
79
76
  ...image,
80
77
  color: color,