@iternio/react-native-auto-play 0.5.14-beta.1 → 0.6.0

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 (122) hide show
  1. package/README.md +214 -29
  2. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AndroidAutoSession.kt +5 -0
  3. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridMapTemplate.kt +9 -2
  4. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/NativeBackdrop.kt +40 -0
  5. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/VirtualRenderer.kt +52 -0
  6. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/GridTemplate.kt +47 -13
  7. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/Parser.kt +21 -2
  8. package/ios/Types.swift +3 -0
  9. package/ios/extensions/UIImage+Toggle.swift +5 -2
  10. package/ios/hybrid/HybridAutoPlay.swift +28 -16
  11. package/ios/hybrid/HybridMapTemplate.swift +22 -5
  12. package/ios/scenes/AutoPlayInterfaceController.swift +261 -72
  13. package/ios/templates/AutoPlayMapPanelDelegate.swift +190 -0
  14. package/ios/templates/AutoPlayTemplate.swift +24 -2
  15. package/ios/templates/GridTemplate.swift +57 -57
  16. package/ios/templates/InformationTemplate.swift +84 -9
  17. package/ios/templates/ListTemplate.swift +66 -11
  18. package/ios/templates/MapTemplate.swift +83 -68
  19. package/ios/templates/MessageTemplate.swift +72 -6
  20. package/ios/templates/Parser.swift +564 -69
  21. package/ios/templates/SearchTemplate.swift +1 -1
  22. package/ios/templates/TemplateStore.swift +5 -15
  23. package/ios/utils/SymbolFont.swift +13 -28
  24. package/lib/Constants.d.ts +4 -0
  25. package/lib/Constants.js +6 -0
  26. package/lib/index.d.ts +1 -0
  27. package/lib/index.js +1 -0
  28. package/lib/specs/MapTemplate.nitro.d.ts +3 -1
  29. package/lib/templates/GridTemplate.d.ts +23 -3
  30. package/lib/templates/GridTemplate.js +8 -0
  31. package/lib/templates/InformationTemplate.d.ts +38 -12
  32. package/lib/templates/ListTemplate.d.ts +16 -47
  33. package/lib/templates/MapTemplate.d.ts +26 -2
  34. package/lib/templates/MapTemplate.js +16 -2
  35. package/lib/templates/MessageTemplate.d.ts +52 -11
  36. package/lib/templates/MessageTemplate.js +1 -1
  37. package/lib/utils/NitroOptionsPanel.d.ts +103 -0
  38. package/lib/utils/NitroOptionsPanel.js +63 -0
  39. package/lib/utils/NitroSection.d.ts +89 -2
  40. package/lib/utils/NitroSection.js +49 -2
  41. package/nitrogen/generated/android/ReactNativeAutoPlay+autolinking.cmake +1 -0
  42. package/nitrogen/generated/android/c++/JChargingConnector.hpp +79 -0
  43. package/nitrogen/generated/android/c++/JGridImageSize.hpp +64 -0
  44. package/nitrogen/generated/android/c++/JGridTemplateConfig.hpp +7 -1
  45. package/nitrogen/generated/android/c++/JHybridGridTemplateSpec.cpp +4 -0
  46. package/nitrogen/generated/android/c++/JHybridInformationTemplateSpec.cpp +12 -0
  47. package/nitrogen/generated/android/c++/JHybridListTemplateSpec.cpp +12 -0
  48. package/nitrogen/generated/android/c++/JHybridMapTemplateSpec.cpp +78 -3
  49. package/nitrogen/generated/android/c++/JHybridMapTemplateSpec.hpp +2 -1
  50. package/nitrogen/generated/android/c++/JHybridSearchTemplateSpec.cpp +12 -0
  51. package/nitrogen/generated/android/c++/JInformationTemplateConfig.hpp +6 -0
  52. package/nitrogen/generated/android/c++/JListImageType.hpp +67 -0
  53. package/nitrogen/generated/android/c++/JListTemplateConfig.hpp +6 -0
  54. package/nitrogen/generated/android/c++/JNitroChargerLocation.hpp +115 -0
  55. package/nitrogen/generated/android/c++/JNitroChargerOutlet.hpp +82 -0
  56. package/nitrogen/generated/android/c++/JNitroOptionsPanelChargerSection.hpp +112 -0
  57. package/nitrogen/generated/android/c++/JNitroOptionsPanelConfig.hpp +157 -0
  58. package/nitrogen/generated/android/c++/JNitroOptionsPanelGridSection.hpp +102 -0
  59. package/nitrogen/generated/android/c++/JNitroOptionsPanelSection.cpp +30 -0
  60. package/nitrogen/generated/android/c++/JNitroOptionsPanelSection.hpp +129 -0
  61. package/nitrogen/generated/android/c++/JNitroRow.hpp +33 -3
  62. package/nitrogen/generated/android/c++/JNitroSection.hpp +6 -0
  63. package/nitrogen/generated/android/c++/JSearchTemplateConfig.hpp +6 -0
  64. package/nitrogen/generated/android/c++/JWaypointCoordinate.hpp +65 -0
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/ChargingConnector.kt +30 -0
  66. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/GridImageSize.kt +25 -0
  67. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/GridTemplateConfig.kt +9 -4
  68. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridMapTemplateSpec.kt +5 -1
  69. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/ListImageType.kt +26 -0
  70. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/NitroChargerLocation.kt +90 -0
  71. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/NitroChargerOutlet.kt +70 -0
  72. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/NitroOptionsPanelChargerSection.kt +61 -0
  73. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/NitroOptionsPanelConfig.kt +61 -0
  74. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/NitroOptionsPanelGridSection.kt +56 -0
  75. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/NitroOptionsPanelSection.kt +75 -0
  76. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/NitroRow.kt +36 -6
  77. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/WaypointCoordinate.kt +61 -0
  78. package/nitrogen/generated/ios/ReactNativeAutoPlay-Swift-Cxx-Bridge.hpp +174 -0
  79. package/nitrogen/generated/ios/ReactNativeAutoPlay-Swift-Cxx-Umbrella.hpp +27 -0
  80. package/nitrogen/generated/ios/c++/HybridGridTemplateSpecSwift.hpp +3 -0
  81. package/nitrogen/generated/ios/c++/HybridInformationTemplateSpecSwift.hpp +9 -0
  82. package/nitrogen/generated/ios/c++/HybridListTemplateSpecSwift.hpp +9 -0
  83. package/nitrogen/generated/ios/c++/HybridMapTemplateSpecSwift.hpp +47 -1
  84. package/nitrogen/generated/ios/c++/HybridSearchTemplateSpecSwift.hpp +9 -0
  85. package/nitrogen/generated/ios/swift/ChargingConnector.swift +68 -0
  86. package/nitrogen/generated/ios/swift/GridImageSize.swift +48 -0
  87. package/nitrogen/generated/ios/swift/GridTemplateConfig.swift +12 -1
  88. package/nitrogen/generated/ios/swift/HybridMapTemplateSpec.swift +2 -1
  89. package/nitrogen/generated/ios/swift/HybridMapTemplateSpec_cxx.swift +31 -4
  90. package/nitrogen/generated/ios/swift/ListImageType.swift +52 -0
  91. package/nitrogen/generated/ios/swift/NitroChargerLocation.swift +161 -0
  92. package/nitrogen/generated/ios/swift/NitroChargerOutlet.swift +65 -0
  93. package/nitrogen/generated/ios/swift/NitroOptionsPanelChargerSection.swift +64 -0
  94. package/nitrogen/generated/ios/swift/NitroOptionsPanelConfig.swift +94 -0
  95. package/nitrogen/generated/ios/swift/NitroOptionsPanelGridSection.swift +53 -0
  96. package/nitrogen/generated/ios/swift/NitroOptionsPanelSection.swift +32 -0
  97. package/nitrogen/generated/ios/swift/NitroRow.swift +81 -1
  98. package/nitrogen/generated/ios/swift/WaypointCoordinate.swift +52 -0
  99. package/nitrogen/generated/shared/c++/ChargingConnector.hpp +104 -0
  100. package/nitrogen/generated/shared/c++/GridImageSize.hpp +84 -0
  101. package/nitrogen/generated/shared/c++/GridTemplateConfig.hpp +8 -1
  102. package/nitrogen/generated/shared/c++/HybridMapTemplateSpec.cpp +1 -0
  103. package/nitrogen/generated/shared/c++/HybridMapTemplateSpec.hpp +5 -1
  104. package/nitrogen/generated/shared/c++/ListImageType.hpp +88 -0
  105. package/nitrogen/generated/shared/c++/NitroChargerLocation.hpp +131 -0
  106. package/nitrogen/generated/shared/c++/NitroChargerOutlet.hpp +98 -0
  107. package/nitrogen/generated/shared/c++/NitroOptionsPanelChargerSection.hpp +98 -0
  108. package/nitrogen/generated/shared/c++/NitroOptionsPanelConfig.hpp +107 -0
  109. package/nitrogen/generated/shared/c++/NitroOptionsPanelGridSection.hpp +91 -0
  110. package/nitrogen/generated/shared/c++/NitroRow.hpp +39 -2
  111. package/nitrogen/generated/shared/c++/WaypointCoordinate.hpp +91 -0
  112. package/package.json +1 -1
  113. package/src/Constants.ts +9 -0
  114. package/src/index.ts +1 -0
  115. package/src/specs/MapTemplate.nitro.ts +3 -1
  116. package/src/templates/GridTemplate.ts +38 -3
  117. package/src/templates/InformationTemplate.ts +60 -29
  118. package/src/templates/ListTemplate.ts +27 -55
  119. package/src/templates/MapTemplate.ts +60 -2
  120. package/src/templates/MessageTemplate.ts +71 -24
  121. package/src/utils/NitroOptionsPanel.ts +218 -0
  122. package/src/utils/NitroSection.ts +160 -5
@@ -22,6 +22,8 @@ class MapTemplate: AutoPlayHeaderProviding,
22
22
  var mapButtons: [NitroMapButton]?
23
23
  var visibleTravelEstimate: VisibleTravelEstimate?
24
24
 
25
+ private var optionsPanel: NitroOptionsPanelConfig?
26
+
25
27
  override var autoDismissMs: Double? {
26
28
  return config.autoDismissMs
27
29
  }
@@ -92,67 +94,6 @@ class MapTemplate: AutoPlayHeaderProviding,
92
94
  }
93
95
  }
94
96
 
95
- func parseMapButtons(mapButtons: [NitroMapButton]) -> [CPMapButton] {
96
- guard let traitCollection = SceneStore.getRootTraitCollection() else {
97
- return []
98
- }
99
-
100
- return mapButtons.map { button in
101
- if let glyphImage = button.image.glyphImage,
102
- let icon = SymbolFont.imageFromNitroImage(
103
- image: glyphImage,
104
- size: CPButtonMaximumImageSize.height,
105
- traitCollection: traitCollection
106
- )
107
- {
108
- return CPMapButton(image: icon) { _ in
109
- if button.type == .pan {
110
- self.onPanButtonPress()
111
- return
112
- }
113
- button.onPress?()
114
- }
115
- }
116
- if let assetImage = button.image.assetImage,
117
- let icon = Parser.parseAssetImage(
118
- assetImage: assetImage,
119
- traitCollection: traitCollection
120
- )
121
- {
122
- return CPMapButton(image: icon) { _ in
123
- if button.type == .pan {
124
- self.onPanButtonPress()
125
- return
126
- }
127
- button.onPress?()
128
- }
129
- }
130
- if let remoteImage = button.image.remoteImage,
131
- let icon = Parser.parseRemoteImage(
132
- remoteImage: remoteImage,
133
- traitCollection: traitCollection
134
- )
135
- {
136
- return CPMapButton(image: icon) { _ in
137
- if button.type == .pan {
138
- self.onPanButtonPress()
139
- return
140
- }
141
- button.onPress?()
142
- }
143
- }
144
-
145
- return CPMapButton { _ in
146
- if button.type == .pan {
147
- self.onPanButtonPress()
148
- return
149
- }
150
- button.onPress?()
151
- }
152
- }
153
-
154
- }
155
-
156
97
  @MainActor
157
98
  override func _invalidate() {
158
99
  if tripSelectorVisible {
@@ -176,15 +117,35 @@ class MapTemplate: AutoPlayHeaderProviding,
176
117
  button.type != .pan
177
118
  } ?? []
178
119
 
179
- template.mapButtons = parseMapButtons(mapButtons: mapButtons)
120
+ template.mapButtons = Parser.parseMapButtons(
121
+ mapButtons: mapButtons,
122
+ onPanButtonPress: onPanButtonPress
123
+ )
180
124
 
181
125
  return
182
126
  }
183
127
 
184
- setBarButtons(template: template, barButtons: barButtons)
128
+ // A panel owns the bar buttons while it exists; keep barButtons up to date but don't apply it.
129
+ let hasPanel: Bool = {
130
+ if #available(iOS 27.0, *) {
131
+ return SceneStore.getRootScene()?.interfaceController?.hasPanel ?? false
132
+ }
133
+ return false
134
+ }()
135
+
136
+ if !hasPanel {
137
+ // Restore CarPlay's default auto-hide (disabled while a panel is shown, see panelDidShow).
138
+ template.automaticallyHidesNavigationBar = true
139
+ template.hidesButtonsWithNavigationBar = true
140
+
141
+ setBarButtons(template: template, barButtons: barButtons)
185
142
 
186
- if let mapButtons = mapButtons {
187
- template.mapButtons = parseMapButtons(mapButtons: mapButtons)
143
+ if let mapButtons = mapButtons {
144
+ template.mapButtons = Parser.parseMapButtons(
145
+ mapButtons: mapButtons,
146
+ onPanButtonPress: onPanButtonPress
147
+ )
148
+ }
188
149
  }
189
150
  }
190
151
 
@@ -222,7 +183,7 @@ class MapTemplate: AutoPlayHeaderProviding,
222
183
 
223
184
  template.tripEstimateStyle = isDark ? .dark : .light
224
185
 
225
- invalidate()
186
+ super.traitCollectionDidChange()
226
187
  }
227
188
 
228
189
  // MARK: gestures
@@ -428,6 +389,10 @@ class MapTemplate: AutoPlayHeaderProviding,
428
389
  duration: alertConfig.durationMs / 1000
429
390
  )
430
391
 
392
+ if #available(iOS 27.0, *) {
393
+ alert.showsCloseButton = false
394
+ }
395
+
431
396
  func setNavigationAlert() {
432
397
  self.navigationAlert = .init(alert: alert, config: alertConfig)
433
398
  template.present(navigationAlert: alert, animated: true)
@@ -496,8 +461,9 @@ class MapTemplate: AutoPlayHeaderProviding,
496
461
 
497
462
  self.template.leadingNavigationBarButtons = []
498
463
  self.template.trailingNavigationBarButtons = []
499
- self.template.mapButtons = self.parseMapButtons(
500
- mapButtons: mapButtons
464
+ self.template.mapButtons = Parser.parseMapButtons(
465
+ mapButtons: mapButtons,
466
+ onPanButtonPress: self.onPanButtonPress
501
467
  )
502
468
  }
503
469
 
@@ -846,6 +812,7 @@ class MapTemplate: AutoPlayHeaderProviding,
846
812
  }
847
813
 
848
814
  self.navigationSession = template.startNavigationSession(for: trip)
815
+ applyOptionsPanel()
849
816
  }
850
817
 
851
818
  func stopNavigation() {
@@ -853,6 +820,54 @@ class MapTemplate: AutoPlayHeaderProviding,
853
820
  navigationSession = nil
854
821
  }
855
822
 
823
+ func updateOptionsPanel(config: NitroOptionsPanelConfig?) {
824
+ optionsPanel = config
825
+ applyOptionsPanel()
826
+ }
827
+
828
+ @available(iOS 27.0, *)
829
+ private func updateOptionsPanelSection(section: NitroSection, sectionIndex: Int) {
830
+ guard var sections = optionsPanel?.sections, sectionIndex < sections.count else { return }
831
+
832
+ // `.first` is the `NitroSection` (list) case of `NitroOptionsPanelSection` — matches
833
+ // whatever nitrogen assigned the last time `yarn specs` ran, see the ordering note on
834
+ // `Parser.parseOptionsPanelSections`.
835
+ sections[sectionIndex] = .first(section)
836
+ optionsPanel = NitroOptionsPanelConfig(
837
+ title: optionsPanel?.title,
838
+ sections: sections,
839
+ actions: optionsPanel?.actions
840
+ )
841
+ applyOptionsPanel()
842
+ }
843
+
844
+ private func applyOptionsPanel() {
845
+ guard #available(iOS 27.0, *) else { return }
846
+ guard let navigationSession else { return }
847
+ guard let optionsPanel else {
848
+ navigationSession.optionsPanel = nil
849
+ return
850
+ }
851
+ guard let traitCollection = SceneStore.getRootTraitCollection() else { return }
852
+
853
+ let sections = Parser.parseOptionsPanelSections(
854
+ sections: optionsPanel.sections,
855
+ updateSection: { [weak self] section, sectionIndex in
856
+ self?.updateOptionsPanelSection(section: section, sectionIndex: sectionIndex)
857
+ },
858
+ traitCollection: traitCollection
859
+ )
860
+
861
+ navigationSession.optionsPanel = CPMapPanel(
862
+ title: Parser.parseText(text: optionsPanel.title),
863
+ sections: sections,
864
+ buttonConfiguration: Parser.parsePanelButtonConfiguration(
865
+ actions: optionsPanel.actions,
866
+ traitCollection: traitCollection
867
+ )
868
+ )
869
+ }
870
+
856
871
  func setManeuverState(state: ManeuverState) {
857
872
  guard #available(iOS 17.4, *) else { return }
858
873
  guard let navigationSession = navigationSession else { return }
@@ -8,25 +8,91 @@
8
8
  import CarPlay
9
9
 
10
10
  class MessageTemplate: AutoPlayTemplate {
11
- let template: CPAlertTemplate
11
+ /// `nil` when this template renders as a map panel instead (see `mapPanel`).
12
+ private(set) var template: CPAlertTemplate?
12
13
  var config: MessageTemplateConfig
13
14
 
15
+ /// Whether this template renders as a `CPMapPanel` instead of a plain `CPAlertTemplate`.
16
+ private let usesMapPanel: Bool
17
+
18
+ /// Built lazily on the first `_invalidate()` call rather than in `init()`, same reasoning as `InformationTemplate`: `CPMapPanel.buttonConfiguration` is readonly and needs `traitCollection` for its optional symbol button image, which isn't safe to fetch off the main thread that `init()` isn't guaranteed to run on. Boxed as `Any` since `CPMapPanel` itself is only available on iOS 27+ while this class supports a much lower deployment target.
19
+ private var mapPanel: Any?
20
+
21
+ /// `CPMapPanel.delegate` is `weak`, so this keeps the shared delegate alive for as long as the panel exists. Boxed as `Any` for the same reason as `mapPanel`.
22
+ private var mapPanelDelegate: Any?
23
+
14
24
  override var autoDismissMs: Double? {
15
25
  return config.autoDismissMs
16
26
  }
17
27
 
18
- override func getTemplate() -> CPTemplate {
28
+ override func getTemplate() throws -> CPTemplate {
29
+ guard let template else {
30
+ throw AutoPlayError.templateIsMapPanel(config.id)
31
+ }
19
32
  return template
20
33
  }
21
34
 
35
+ override func getPanel() -> Any? {
36
+ return mapPanel
37
+ }
38
+
39
+ override func getPanelHeaderActions() -> [NitroAction]? {
40
+ return config.headerActions
41
+ }
42
+
43
+ override func getPanelMapButtons() -> [NitroMapButton]? {
44
+ return config.mapConfig?.mapButtons
45
+ }
46
+
22
47
  init(config: MessageTemplateConfig) {
23
48
  self.config = config
24
49
 
25
- template = CPAlertTemplate(
26
- titleVariants: [Parser.parseText(text: config.message)!],
27
- actions: Parser.parseAlertActions(alertActions: config.actions),
28
- id: config.id
50
+ if #available(iOS 27.0, *) {
51
+ usesMapPanel = config.mapConfig != nil
52
+ }
53
+ else {
54
+ usesMapPanel = false
55
+ }
56
+
57
+ if !usesMapPanel {
58
+ template = CPAlertTemplate(
59
+ titleVariants: [Parser.parseText(text: config.message)!],
60
+ actions: Parser.parseAlertActions(alertActions: config.actions),
61
+ id: config.id
62
+ )
63
+ }
64
+ }
65
+
66
+ @MainActor
67
+ override func _invalidate() {
68
+ guard usesMapPanel, #available(iOS 27.0, *), mapPanel == nil else { return }
69
+ guard let traitCollection = SceneStore.getRootTraitCollection() else { return }
70
+
71
+ // The message and its actions are only ever set once and never updated afterwards,
72
+ // so there's nothing to keep in sync with the panel's content later.
73
+ let panel = CPMapPanel(
74
+ title: Parser.parseText(text: config.title),
75
+ sections: [
76
+ CPMapPanelSection(
77
+ title: nil,
78
+ items: [
79
+ CPMapPanelItem(
80
+ listItem: CPListItem(text: Parser.parseText(text: config.message), detailText: nil)
81
+ )
82
+ ]
83
+ )
84
+ ],
85
+ buttonConfiguration: Parser.parsePanelButtonConfiguration(
86
+ actions: config.actions,
87
+ traitCollection: traitCollection
88
+ )
29
89
  )
90
+
91
+ mapPanel = panel
92
+
93
+ let delegate = AutoPlayMapPanelDelegate(template: self, templateId: config.id)
94
+ mapPanelDelegate = delegate
95
+ panel.delegate = delegate
30
96
  }
31
97
 
32
98
  override func onWillAppear(animated: Bool) {