@santoshpk/react-native-carplay 1.0.3

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 (269) hide show
  1. package/README.md +119 -0
  2. package/android/.idea/compiler.xml +6 -0
  3. package/android/.idea/gradle.xml +17 -0
  4. package/android/.idea/jarRepositories.xml +25 -0
  5. package/android/.idea/misc.xml +10 -0
  6. package/android/.idea/vcs.xml +6 -0
  7. package/android/build.gradle +63 -0
  8. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  9. package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  10. package/android/gradle.properties +20 -0
  11. package/android/gradlew +234 -0
  12. package/android/gradlew.bat +89 -0
  13. package/android/local.properties +8 -0
  14. package/android/src/main/AndroidManifest.xml +31 -0
  15. package/android/src/main/AndroidManifestNew.xml +31 -0
  16. package/android/src/main/java/org/birkir/carplay/CarPlayModule.kt +348 -0
  17. package/android/src/main/java/org/birkir/carplay/CarPlayPackage.kt +18 -0
  18. package/android/src/main/java/org/birkir/carplay/CarPlayService.kt +34 -0
  19. package/android/src/main/java/org/birkir/carplay/CarPlaySession.kt +114 -0
  20. package/android/src/main/java/org/birkir/carplay/parser/Ext.kt +11 -0
  21. package/android/src/main/java/org/birkir/carplay/parser/Parser.kt +18 -0
  22. package/android/src/main/java/org/birkir/carplay/parser/RCTGridTemplate.kt +28 -0
  23. package/android/src/main/java/org/birkir/carplay/parser/RCTListTemplate.kt +64 -0
  24. package/android/src/main/java/org/birkir/carplay/parser/RCTMapTemplate.kt +185 -0
  25. package/android/src/main/java/org/birkir/carplay/parser/RCTMessageTemplate.kt +36 -0
  26. package/android/src/main/java/org/birkir/carplay/parser/RCTPaneTemplate.kt +24 -0
  27. package/android/src/main/java/org/birkir/carplay/parser/RCTSearchTemplate.kt +41 -0
  28. package/android/src/main/java/org/birkir/carplay/parser/RCTTabTemplate.kt +52 -0
  29. package/android/src/main/java/org/birkir/carplay/parser/RCTTemplate.kt +434 -0
  30. package/android/src/main/java/org/birkir/carplay/parser/TemplateParser.kt +35 -0
  31. package/android/src/main/java/org/birkir/carplay/screens/CarScreen.kt +69 -0
  32. package/android/src/main/java/org/birkir/carplay/screens/CarScreenContext.kt +10 -0
  33. package/android/src/main/java/org/birkir/carplay/utils/EventEmitter.kt +174 -0
  34. package/android/src/main/java/org/birkir/carplay/utils/VirtualRenderer.kt +75 -0
  35. package/ios/RCTConvert+RNCarPlay.h +18 -0
  36. package/ios/RCTConvert+RNCarPlay.m +96 -0
  37. package/ios/RNCPStore.h +24 -0
  38. package/ios/RNCPStore.m +68 -0
  39. package/ios/RNCarPlay.h +29 -0
  40. package/ios/RNCarPlay.m +1839 -0
  41. package/ios/RNCarPlay.xcodeproj/project.pbxproj +300 -0
  42. package/lib/CarPlay.d.ts +196 -0
  43. package/lib/CarPlay.d.ts.map +1 -0
  44. package/lib/CarPlay.js +176 -0
  45. package/lib/CarPlay.js.map +1 -0
  46. package/lib/index.d.ts +44 -0
  47. package/lib/index.d.ts.map +1 -0
  48. package/lib/index.js +64 -0
  49. package/lib/index.js.map +1 -0
  50. package/lib/interfaces/Action.d.ts +13 -0
  51. package/lib/interfaces/Action.d.ts.map +1 -0
  52. package/lib/interfaces/Action.js +3 -0
  53. package/lib/interfaces/Action.js.map +1 -0
  54. package/lib/interfaces/AlertAction.d.ts +6 -0
  55. package/lib/interfaces/AlertAction.d.ts.map +1 -0
  56. package/lib/interfaces/AlertAction.js +3 -0
  57. package/lib/interfaces/AlertAction.js.map +1 -0
  58. package/lib/interfaces/BarButton.d.ts +39 -0
  59. package/lib/interfaces/BarButton.d.ts.map +1 -0
  60. package/lib/interfaces/BarButton.js +3 -0
  61. package/lib/interfaces/BarButton.js.map +1 -0
  62. package/lib/interfaces/CarColor.d.ts +2 -0
  63. package/lib/interfaces/CarColor.d.ts.map +1 -0
  64. package/lib/interfaces/CarColor.js +3 -0
  65. package/lib/interfaces/CarColor.js.map +1 -0
  66. package/lib/interfaces/GridButton.d.ts +24 -0
  67. package/lib/interfaces/GridButton.d.ts.map +1 -0
  68. package/lib/interfaces/GridButton.js +3 -0
  69. package/lib/interfaces/GridButton.js.map +1 -0
  70. package/lib/interfaces/Header.d.ts +15 -0
  71. package/lib/interfaces/Header.d.ts.map +1 -0
  72. package/lib/interfaces/Header.js +3 -0
  73. package/lib/interfaces/Header.js.map +1 -0
  74. package/lib/interfaces/ListItem.d.ts +61 -0
  75. package/lib/interfaces/ListItem.d.ts.map +1 -0
  76. package/lib/interfaces/ListItem.js +3 -0
  77. package/lib/interfaces/ListItem.js.map +1 -0
  78. package/lib/interfaces/ListItemUpdate.d.ts +15 -0
  79. package/lib/interfaces/ListItemUpdate.d.ts.map +1 -0
  80. package/lib/interfaces/ListItemUpdate.js +3 -0
  81. package/lib/interfaces/ListItemUpdate.js.map +1 -0
  82. package/lib/interfaces/ListSection.d.ts +21 -0
  83. package/lib/interfaces/ListSection.d.ts.map +1 -0
  84. package/lib/interfaces/ListSection.js +3 -0
  85. package/lib/interfaces/ListSection.js.map +1 -0
  86. package/lib/interfaces/Maneuver.d.ts +31 -0
  87. package/lib/interfaces/Maneuver.d.ts.map +1 -0
  88. package/lib/interfaces/Maneuver.js +3 -0
  89. package/lib/interfaces/Maneuver.js.map +1 -0
  90. package/lib/interfaces/MapButton.d.ts +27 -0
  91. package/lib/interfaces/MapButton.d.ts.map +1 -0
  92. package/lib/interfaces/MapButton.js +3 -0
  93. package/lib/interfaces/MapButton.js.map +1 -0
  94. package/lib/interfaces/NavigationAlert.d.ts +44 -0
  95. package/lib/interfaces/NavigationAlert.d.ts.map +1 -0
  96. package/lib/interfaces/NavigationAlert.js +10 -0
  97. package/lib/interfaces/NavigationAlert.js.map +1 -0
  98. package/lib/interfaces/NavigationInfo.d.ts +17 -0
  99. package/lib/interfaces/NavigationInfo.d.ts.map +1 -0
  100. package/lib/interfaces/NavigationInfo.js +3 -0
  101. package/lib/interfaces/NavigationInfo.js.map +1 -0
  102. package/lib/interfaces/NavigationStep.d.ts +17 -0
  103. package/lib/interfaces/NavigationStep.d.ts.map +1 -0
  104. package/lib/interfaces/NavigationStep.js +3 -0
  105. package/lib/interfaces/NavigationStep.js.map +1 -0
  106. package/lib/interfaces/Pane.d.ts +29 -0
  107. package/lib/interfaces/Pane.d.ts.map +1 -0
  108. package/lib/interfaces/Pane.js +3 -0
  109. package/lib/interfaces/Pane.js.map +1 -0
  110. package/lib/interfaces/PauseReason.d.ts +8 -0
  111. package/lib/interfaces/PauseReason.d.ts.map +1 -0
  112. package/lib/interfaces/PauseReason.js +12 -0
  113. package/lib/interfaces/PauseReason.js.map +1 -0
  114. package/lib/interfaces/Place.d.ts +11 -0
  115. package/lib/interfaces/Place.d.ts.map +1 -0
  116. package/lib/interfaces/Place.js +3 -0
  117. package/lib/interfaces/Place.js.map +1 -0
  118. package/lib/interfaces/TextConfiguration.d.ts +6 -0
  119. package/lib/interfaces/TextConfiguration.d.ts.map +1 -0
  120. package/lib/interfaces/TextConfiguration.js +3 -0
  121. package/lib/interfaces/TextConfiguration.js.map +1 -0
  122. package/lib/interfaces/TimeRemainingColor.d.ts +2 -0
  123. package/lib/interfaces/TimeRemainingColor.d.ts.map +1 -0
  124. package/lib/interfaces/TimeRemainingColor.js +3 -0
  125. package/lib/interfaces/TimeRemainingColor.js.map +1 -0
  126. package/lib/interfaces/TravelEstimates.d.ts +37 -0
  127. package/lib/interfaces/TravelEstimates.d.ts.map +1 -0
  128. package/lib/interfaces/TravelEstimates.js +3 -0
  129. package/lib/interfaces/TravelEstimates.js.map +1 -0
  130. package/lib/interfaces/VoiceControlState.d.ts +8 -0
  131. package/lib/interfaces/VoiceControlState.d.ts.map +1 -0
  132. package/lib/interfaces/VoiceControlState.js +3 -0
  133. package/lib/interfaces/VoiceControlState.js.map +1 -0
  134. package/lib/navigation/NavigationSession.d.ts +18 -0
  135. package/lib/navigation/NavigationSession.d.ts.map +1 -0
  136. package/lib/navigation/NavigationSession.js +51 -0
  137. package/lib/navigation/NavigationSession.js.map +1 -0
  138. package/lib/navigation/Trip.d.ts +22 -0
  139. package/lib/navigation/Trip.d.ts.map +1 -0
  140. package/lib/navigation/Trip.js +18 -0
  141. package/lib/navigation/Trip.js.map +1 -0
  142. package/lib/templates/ActionSheetTemplate.d.ts +18 -0
  143. package/lib/templates/ActionSheetTemplate.d.ts.map +1 -0
  144. package/lib/templates/ActionSheetTemplate.js +16 -0
  145. package/lib/templates/ActionSheetTemplate.js.map +1 -0
  146. package/lib/templates/AlertTemplate.d.ts +17 -0
  147. package/lib/templates/AlertTemplate.d.ts.map +1 -0
  148. package/lib/templates/AlertTemplate.js +16 -0
  149. package/lib/templates/AlertTemplate.js.map +1 -0
  150. package/lib/templates/ContactTemplate.d.ts +36 -0
  151. package/lib/templates/ContactTemplate.d.ts.map +1 -0
  152. package/lib/templates/ContactTemplate.js +16 -0
  153. package/lib/templates/ContactTemplate.js.map +1 -0
  154. package/lib/templates/GridTemplate.d.ts +38 -0
  155. package/lib/templates/GridTemplate.d.ts.map +1 -0
  156. package/lib/templates/GridTemplate.js +17 -0
  157. package/lib/templates/GridTemplate.js.map +1 -0
  158. package/lib/templates/InformationTemplate.d.ts +28 -0
  159. package/lib/templates/InformationTemplate.d.ts.map +1 -0
  160. package/lib/templates/InformationTemplate.js +28 -0
  161. package/lib/templates/InformationTemplate.js.map +1 -0
  162. package/lib/templates/ListTemplate.d.ts +112 -0
  163. package/lib/templates/ListTemplate.d.ts.map +1 -0
  164. package/lib/templates/ListTemplate.js +57 -0
  165. package/lib/templates/ListTemplate.js.map +1 -0
  166. package/lib/templates/MapTemplate.d.ts +171 -0
  167. package/lib/templates/MapTemplate.d.ts.map +1 -0
  168. package/lib/templates/MapTemplate.js +115 -0
  169. package/lib/templates/MapTemplate.js.map +1 -0
  170. package/lib/templates/NowPlayingTemplate.d.ts +31 -0
  171. package/lib/templates/NowPlayingTemplate.d.ts.map +1 -0
  172. package/lib/templates/NowPlayingTemplate.js +18 -0
  173. package/lib/templates/NowPlayingTemplate.js.map +1 -0
  174. package/lib/templates/PointOfInterestTemplate.d.ts +38 -0
  175. package/lib/templates/PointOfInterestTemplate.d.ts.map +1 -0
  176. package/lib/templates/PointOfInterestTemplate.js +18 -0
  177. package/lib/templates/PointOfInterestTemplate.js.map +1 -0
  178. package/lib/templates/SearchTemplate.d.ts +32 -0
  179. package/lib/templates/SearchTemplate.d.ts.map +1 -0
  180. package/lib/templates/SearchTemplate.js +41 -0
  181. package/lib/templates/SearchTemplate.js.map +1 -0
  182. package/lib/templates/TabBarTemplate.d.ts +27 -0
  183. package/lib/templates/TabBarTemplate.d.ts.map +1 -0
  184. package/lib/templates/TabBarTemplate.js +26 -0
  185. package/lib/templates/TabBarTemplate.js.map +1 -0
  186. package/lib/templates/Template.d.ts +83 -0
  187. package/lib/templates/Template.d.ts.map +1 -0
  188. package/lib/templates/Template.js +71 -0
  189. package/lib/templates/Template.js.map +1 -0
  190. package/lib/templates/VoiceControlTemplate.d.ts +18 -0
  191. package/lib/templates/VoiceControlTemplate.d.ts.map +1 -0
  192. package/lib/templates/VoiceControlTemplate.js +20 -0
  193. package/lib/templates/VoiceControlTemplate.js.map +1 -0
  194. package/lib/templates/android/AndroidNavigationBaseTemplate.d.ts +19 -0
  195. package/lib/templates/android/AndroidNavigationBaseTemplate.d.ts.map +1 -0
  196. package/lib/templates/android/AndroidNavigationBaseTemplate.js +29 -0
  197. package/lib/templates/android/AndroidNavigationBaseTemplate.js.map +1 -0
  198. package/lib/templates/android/MessageTemplate.d.ts +16 -0
  199. package/lib/templates/android/MessageTemplate.d.ts.map +1 -0
  200. package/lib/templates/android/MessageTemplate.js +11 -0
  201. package/lib/templates/android/MessageTemplate.js.map +1 -0
  202. package/lib/templates/android/NavigationTemplate.d.ts +44 -0
  203. package/lib/templates/android/NavigationTemplate.d.ts.map +1 -0
  204. package/lib/templates/android/NavigationTemplate.js +17 -0
  205. package/lib/templates/android/NavigationTemplate.js.map +1 -0
  206. package/lib/templates/android/PaneTemplate.d.ts +20 -0
  207. package/lib/templates/android/PaneTemplate.d.ts.map +1 -0
  208. package/lib/templates/android/PaneTemplate.js +17 -0
  209. package/lib/templates/android/PaneTemplate.js.map +1 -0
  210. package/lib/templates/android/PlaceListMapTemplate.d.ts +78 -0
  211. package/lib/templates/android/PlaceListMapTemplate.d.ts.map +1 -0
  212. package/lib/templates/android/PlaceListMapTemplate.js +25 -0
  213. package/lib/templates/android/PlaceListMapTemplate.js.map +1 -0
  214. package/lib/templates/android/PlaceListNavigationTemplate.d.ts +51 -0
  215. package/lib/templates/android/PlaceListNavigationTemplate.d.ts.map +1 -0
  216. package/lib/templates/android/PlaceListNavigationTemplate.js +20 -0
  217. package/lib/templates/android/PlaceListNavigationTemplate.js.map +1 -0
  218. package/lib/templates/android/RoutePreviewNavigationTemplate.d.ts +60 -0
  219. package/lib/templates/android/RoutePreviewNavigationTemplate.d.ts.map +1 -0
  220. package/lib/templates/android/RoutePreviewNavigationTemplate.js +23 -0
  221. package/lib/templates/android/RoutePreviewNavigationTemplate.js.map +1 -0
  222. package/lib/tsconfig.tsbuildinfo +1 -0
  223. package/package.json +49 -0
  224. package/react-native-carplay.podspec +21 -0
  225. package/src/CarPlay.ts +320 -0
  226. package/src/index.ts +50 -0
  227. package/src/interfaces/Action.ts +15 -0
  228. package/src/interfaces/AlertAction.ts +5 -0
  229. package/src/interfaces/BarButton.ts +41 -0
  230. package/src/interfaces/CarColor.ts +1 -0
  231. package/src/interfaces/GridButton.ts +25 -0
  232. package/src/interfaces/Header.ts +16 -0
  233. package/src/interfaces/ListItem.ts +61 -0
  234. package/src/interfaces/ListItemUpdate.ts +14 -0
  235. package/src/interfaces/ListSection.ts +21 -0
  236. package/src/interfaces/Maneuver.ts +30 -0
  237. package/src/interfaces/MapButton.ts +27 -0
  238. package/src/interfaces/NavigationAlert.ts +46 -0
  239. package/src/interfaces/NavigationInfo.ts +19 -0
  240. package/src/interfaces/NavigationStep.ts +17 -0
  241. package/src/interfaces/Pane.ts +29 -0
  242. package/src/interfaces/PauseReason.ts +7 -0
  243. package/src/interfaces/Place.ts +12 -0
  244. package/src/interfaces/TextConfiguration.ts +5 -0
  245. package/src/interfaces/TimeRemainingColor.ts +1 -0
  246. package/src/interfaces/TravelEstimates.ts +39 -0
  247. package/src/interfaces/VoiceControlState.ts +8 -0
  248. package/src/navigation/NavigationSession.ts +57 -0
  249. package/src/navigation/Trip.ts +36 -0
  250. package/src/templates/ActionSheetTemplate.ts +21 -0
  251. package/src/templates/AlertTemplate.ts +20 -0
  252. package/src/templates/ContactTemplate.ts +45 -0
  253. package/src/templates/GridTemplate.ts +45 -0
  254. package/src/templates/InformationTemplate.ts +42 -0
  255. package/src/templates/ListTemplate.ts +150 -0
  256. package/src/templates/MapTemplate.ts +231 -0
  257. package/src/templates/NowPlayingTemplate.ts +38 -0
  258. package/src/templates/PointOfInterestTemplate.ts +42 -0
  259. package/src/templates/SearchTemplate.ts +70 -0
  260. package/src/templates/TabBarTemplate.ts +56 -0
  261. package/src/templates/Template.ts +165 -0
  262. package/src/templates/VoiceControlTemplate.ts +25 -0
  263. package/src/templates/android/AndroidNavigationBaseTemplate.ts +46 -0
  264. package/src/templates/android/MessageTemplate.ts +19 -0
  265. package/src/templates/android/NavigationTemplate.ts +50 -0
  266. package/src/templates/android/PaneTemplate.ts +26 -0
  267. package/src/templates/android/PlaceListMapTemplate.ts +94 -0
  268. package/src/templates/android/PlaceListNavigationTemplate.ts +57 -0
  269. package/src/templates/android/RoutePreviewNavigationTemplate.ts +66 -0
@@ -0,0 +1,1839 @@
1
+ #import "RNCarPlay.h"
2
+ #import <React/RCTConvert.h>
3
+ #import <React/RCTRootView.h>
4
+
5
+ @implementation RNCarPlay
6
+ {
7
+ bool hasListeners;
8
+ }
9
+
10
+ @synthesize interfaceController;
11
+ @synthesize window;
12
+ @synthesize searchResultBlock;
13
+ @synthesize selectedResultBlock;
14
+ @synthesize isNowPlayingActive;
15
+
16
+ -(void)startObserving {
17
+ hasListeners = YES;
18
+ }
19
+
20
+ -(void)stopObserving {
21
+ hasListeners = NO;
22
+ }
23
+
24
+ + (NSDictionary *) getConnectedWindowInformation: (CPWindow *) window {
25
+ return @{
26
+ @"width": @(window.bounds.size.width),
27
+ @"height": @(window.bounds.size.height),
28
+ @"scale": @(window.screen.scale)
29
+ };
30
+ }
31
+
32
+ + (void) connectWithInterfaceController:(CPInterfaceController*)interfaceController window:(CPWindow*)window scene:(CPTemplateApplicationScene*)scene {
33
+ RNCPStore * store = [RNCPStore sharedManager];
34
+ store.interfaceController = interfaceController;
35
+ store.window = window;
36
+ store.scene = scene;
37
+ [store setConnected:true];
38
+ RNCarPlay *cp = [RNCarPlay allocWithZone:nil];
39
+ if (cp.bridge) {
40
+ [cp sendEventWithName:@"didConnect" body:[self getConnectedWindowInformation:window]];
41
+ }
42
+ }
43
+
44
+ + (void) disconnect {
45
+ RNCarPlay *cp = [RNCarPlay allocWithZone:nil];
46
+ RNCPStore *store = [RNCPStore sharedManager];
47
+ [store setConnected:false];
48
+ [[store.window subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
49
+
50
+ if (cp.bridge) {
51
+ [cp sendEventWithName:@"didDisconnect" body:@{}];
52
+ }
53
+ }
54
+
55
+ RCT_EXPORT_MODULE();
56
+
57
+ + (id)allocWithZone:(NSZone *)zone {
58
+ static RNCarPlay *sharedInstance = nil;
59
+ static dispatch_once_t onceToken;
60
+ dispatch_once(&onceToken, ^{
61
+ sharedInstance = [super allocWithZone:zone];
62
+ });
63
+ return sharedInstance;
64
+ }
65
+
66
+ - (NSArray<NSString *> *)supportedEvents
67
+ {
68
+ return @[
69
+ @"didConnect",
70
+ @"didDisconnect",
71
+ // interface
72
+ @"barButtonPressed",
73
+ @"backButtonPressed",
74
+ @"didAppear",
75
+ @"didDisappear",
76
+ @"willAppear",
77
+ @"willDisappear",
78
+ @"buttonPressed",
79
+ // grid
80
+ @"gridButtonPressed",
81
+ // information
82
+ @"actionButtonPressed",
83
+ // list
84
+ @"didSelectListItem",
85
+ // search
86
+ @"updatedSearchText",
87
+ @"searchButtonPressed",
88
+ @"selectedResult",
89
+ // tabbar
90
+ @"didSelectTemplate",
91
+ // nowplaying
92
+ @"upNextButtonPressed",
93
+ @"albumArtistButtonPressed",
94
+ // poi
95
+ @"didSelectPointOfInterest",
96
+ // map
97
+ @"mapButtonPressed",
98
+ @"didUpdatePanGestureWithTranslation",
99
+ @"didEndPanGestureWithVelocity",
100
+ @"panBeganWithDirection",
101
+ @"panEndedWithDirection",
102
+ @"panWithDirection",
103
+ @"didBeginPanGesture",
104
+ @"didDismissPanningInterface",
105
+ @"willDismissPanningInterface",
106
+ @"didShowPanningInterface",
107
+ @"didDismissNavigationAlert",
108
+ @"willDismissNavigationAlert",
109
+ @"didShowNavigationAlert",
110
+ @"willShowNavigationAlert",
111
+ @"didCancelNavigation",
112
+ @"alertActionPressed",
113
+ @"selectedPreviewForTrip",
114
+ @"startedTrip",
115
+ ];
116
+ }
117
+
118
+ - (dispatch_queue_t)methodQueue
119
+ {
120
+ return dispatch_get_main_queue();
121
+ }
122
+
123
+
124
+ -(UIImage *)imageWithTint:(UIImage *)image andTintColor:(UIColor *)tintColor {
125
+ UIImage *imageNew = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
126
+ UIImageView *imageView = [[UIImageView alloc] initWithImage:imageNew];
127
+ imageView.tintColor = tintColor;
128
+ UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 0.0);
129
+ [imageView.layer renderInContext:UIGraphicsGetCurrentContext()];
130
+ UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
131
+ UIGraphicsEndImageContext();
132
+ return tintedImage;
133
+ }
134
+
135
+ -(UIImage*)dynamicImageWithNormalImage:(UIImage*)normalImage darkImage:(UIImage*)darkImage {
136
+ RNCPStore *store = [RNCPStore sharedManager];
137
+ if (normalImage == nil || darkImage == nil) {
138
+ return normalImage ? : darkImage;
139
+ }
140
+ if (@available(iOS 14.0, *)) {
141
+ UIImageAsset* imageAsset = darkImage.imageAsset;
142
+
143
+ // darkImage
144
+ UITraitCollection* darkImageTraitCollection = [UITraitCollection traitCollectionWithTraitsFromCollections:
145
+ @[[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark],
146
+ [UITraitCollection traitCollectionWithDisplayScale:normalImage.scale]]];
147
+ [imageAsset registerImage:normalImage withTraitCollection:darkImageTraitCollection];
148
+
149
+ return [imageAsset imageWithTraitCollection: store.interfaceController.carTraitCollection];
150
+ }
151
+ else {
152
+ return normalImage;
153
+ }
154
+ }
155
+
156
+ - (UIImage *)imageWithSize:(UIImage *)image convertToSize:(CGSize)size {
157
+ UIGraphicsImageRendererFormat *renderFormat = [UIGraphicsImageRendererFormat defaultFormat];
158
+ renderFormat.opaque = NO;
159
+ UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size format:renderFormat];
160
+
161
+ UIImage *resizedImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
162
+ [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
163
+ }];
164
+ return resizedImage;
165
+ }
166
+
167
+ - (void)updateItemImageWithURL:(CPListItem *)item imgUrl:(NSString *)imgUrlString {
168
+ NSURL *imgUrl = [NSURL URLWithString:imgUrlString];
169
+
170
+ NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:imgUrl completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
171
+ if (data) {
172
+ UIImage *image = [UIImage imageWithData:data];
173
+ dispatch_async(dispatch_get_main_queue(), ^{
174
+ [item setImage:image];
175
+ });
176
+ } else {
177
+ NSLog(@"Failed to load image from URL: %@", imgUrl);
178
+ }
179
+ }];
180
+ [task resume];
181
+ }
182
+
183
+ RCT_EXPORT_METHOD(checkForConnection) {
184
+ RNCPStore *store = [RNCPStore sharedManager];
185
+ if ([store isConnected] && hasListeners) {
186
+ [self sendEventWithName:@"didConnect" body:[RNCarPlay getConnectedWindowInformation: store.window]];
187
+ }
188
+ }
189
+
190
+ RCT_EXPORT_METHOD(openUrl:(NSString *)url) {
191
+ RNCPStore *store = [RNCPStore sharedManager];
192
+ CPTemplateApplicationScene *templateApplicationScene = store.scene;
193
+ NSURL *URL = [NSURL URLWithString:url];
194
+ [templateApplicationScene openURL:URL options:NULL completionHandler:^(BOOL success) {
195
+ if (success) {
196
+ NSLog(@"Opened url %@", url);
197
+ }
198
+ }];
199
+ }
200
+
201
+
202
+ RCT_EXPORT_METHOD(createTemplate:(NSString *)templateId config:(NSDictionary*)config callback:(id)callback)
203
+ {
204
+ // Get the shared instance of the RNCPStore class
205
+ RNCPStore *store = [RNCPStore sharedManager];
206
+
207
+ // Extract values from the 'config' dictionary
208
+ NSString *type = [RCTConvert NSString:config[@"type"]];
209
+ NSString *title = [RCTConvert NSString:config[@"title"]];
210
+ NSArray *leadingNavigationBarButtons = [self parseBarButtons:[RCTConvert NSArray:config[@"leadingNavigationBarButtons"]] templateId:templateId];
211
+ NSArray *trailingNavigationBarButtons = [self parseBarButtons:[RCTConvert NSArray:config[@"trailingNavigationBarButtons"]] templateId:templateId];
212
+
213
+ // Create a new CPTemplate object
214
+ CPTemplate *carPlayTemplate = [[CPTemplate alloc] init];
215
+
216
+ if ([type isEqualToString:@"search"]) {
217
+ CPSearchTemplate *searchTemplate = [[CPSearchTemplate alloc] init];
218
+ searchTemplate.delegate = self;
219
+ carPlayTemplate = searchTemplate;
220
+ }
221
+ else if ([type isEqualToString:@"grid"]) {
222
+ NSArray *buttons = [self parseGridButtons:[RCTConvert NSArray:config[@"buttons"]] templateId:templateId];
223
+ CPGridTemplate *gridTemplate = [[CPGridTemplate alloc] initWithTitle:title gridButtons:buttons];
224
+ [gridTemplate setLeadingNavigationBarButtons:leadingNavigationBarButtons];
225
+ [gridTemplate setTrailingNavigationBarButtons:trailingNavigationBarButtons];
226
+ carPlayTemplate = gridTemplate;
227
+ }
228
+ else if ([type isEqualToString:@"list"]) {
229
+ NSArray *sections = [self parseSections:[RCTConvert NSArray:config[@"sections"]]];
230
+ CPListTemplate *listTemplate;
231
+ if (@available(iOS 15.0, *)) {
232
+ if ([config objectForKey:@"assistant"]) {
233
+ NSDictionary *assistant = [config objectForKey:@"assistant"];
234
+ BOOL _enabled = [assistant valueForKey:@"enabled"];
235
+ if (_enabled) {
236
+ CPAssistantCellConfiguration *conf = [[CPAssistantCellConfiguration alloc] initWithPosition:[RCTConvert CPAssistantCellPosition:[config valueForKey:@"position"]] visibility:[RCTConvert CPAssistantCellVisibility:[config valueForKey:@"visibility"]] assistantAction:[RCTConvert CPAssistantCellActionType:[config valueForKey:@"visibility"]]];
237
+ listTemplate = [[CPListTemplate alloc] initWithTitle:title sections:sections assistantCellConfiguration:conf];
238
+ }
239
+ }
240
+ }
241
+ if (listTemplate == nil) {
242
+ // Fallback on earlier versions
243
+ listTemplate = [[CPListTemplate alloc] initWithTitle:title sections:sections];
244
+ }
245
+ [listTemplate setLeadingNavigationBarButtons:leadingNavigationBarButtons];
246
+ [listTemplate setTrailingNavigationBarButtons:trailingNavigationBarButtons];
247
+ if (![RCTConvert BOOL:config[@"backButtonHidden"]]) {
248
+ if (@available(iOS 14.0, *)) {
249
+ CPBarButton *backButton = [[CPBarButton alloc] initWithTitle:@" Back" handler:^(CPBarButton * _Nonnull barButton) {
250
+ if (hasListeners) {
251
+ [self sendEventWithName:@"backButtonPressed" body:@{@"templateId":templateId}];
252
+ }
253
+ [self popTemplate:false];
254
+ }];
255
+ [listTemplate setBackButton:backButton];
256
+ }
257
+ }
258
+ if (config[@"emptyViewTitleVariants"]) {
259
+ if (@available(iOS 14.0, *)) {
260
+ listTemplate.emptyViewTitleVariants = [RCTConvert NSArray:config[@"emptyViewTitleVariants"]];
261
+ }
262
+ }
263
+ if (config[@"emptyViewSubtitleVariants"]) {
264
+ if (@available(iOS 14.0, *)) {
265
+ listTemplate.emptyViewSubtitleVariants = [RCTConvert NSArray:config[@"emptyViewSubtitleVariants"]];
266
+ }
267
+ }
268
+ listTemplate.delegate = self;
269
+ carPlayTemplate = listTemplate;
270
+ }
271
+ else if ([type isEqualToString:@"map"]) {
272
+ CPMapTemplate *mapTemplate = [[CPMapTemplate alloc] init];
273
+
274
+ [self applyConfigForMapTemplate:mapTemplate templateId:templateId config:config];
275
+ [mapTemplate setLeadingNavigationBarButtons:leadingNavigationBarButtons];
276
+ [mapTemplate setTrailingNavigationBarButtons:trailingNavigationBarButtons];
277
+ [mapTemplate setUserInfo:@{ @"templateId": templateId }];
278
+ mapTemplate.mapDelegate = self;
279
+
280
+ carPlayTemplate = mapTemplate;
281
+ } else if ([type isEqualToString:@"voicecontrol"]) {
282
+ CPVoiceControlTemplate *voiceTemplate = [[CPVoiceControlTemplate alloc] initWithVoiceControlStates: [self parseVoiceControlStates:config[@"voiceControlStates"]]];
283
+ carPlayTemplate = voiceTemplate;
284
+ } else if ([type isEqualToString:@"nowplaying"]) {
285
+ CPNowPlayingTemplate *nowPlayingTemplate = [CPNowPlayingTemplate sharedTemplate];
286
+ [nowPlayingTemplate setAlbumArtistButtonEnabled:[RCTConvert BOOL:config[@"albumArtistButtonEnabled"]]];
287
+ [nowPlayingTemplate setUpNextTitle:[RCTConvert NSString:config[@"upNextButtonTitle"]]];
288
+ [nowPlayingTemplate setUpNextButtonEnabled:[RCTConvert BOOL:config[@"upNextButtonEnabled"]]];
289
+ NSMutableArray<CPNowPlayingButton *> *buttons = [NSMutableArray new];
290
+ NSArray<NSDictionary*> *_buttons = [RCTConvert NSDictionaryArray:config[@"buttons"]];
291
+
292
+ NSDictionary *buttonTypeMapping = @{
293
+ @"shuffle": CPNowPlayingShuffleButton.class,
294
+ @"add-to-library": CPNowPlayingAddToLibraryButton.class,
295
+ @"more": CPNowPlayingMoreButton.class,
296
+ @"playback": CPNowPlayingPlaybackRateButton.class,
297
+ @"repeat": CPNowPlayingRepeatButton.class,
298
+ @"image": CPNowPlayingImageButton.class
299
+ };
300
+
301
+ for (NSDictionary *_button in _buttons) {
302
+ NSString *buttonType = [RCTConvert NSString:_button[@"type"]];
303
+ NSDictionary *body = @{@"templateId":templateId, @"id": _button[@"id"] };
304
+ Class buttonClass = buttonTypeMapping[buttonType];
305
+ if (buttonClass) {
306
+ CPNowPlayingButton *button = [[buttonClass alloc] initWithHandler:^(__kindof CPNowPlayingButton * _Nonnull) {
307
+ if (self->hasListeners) {
308
+ [self sendEventWithName:@"buttonPressed" body:body];
309
+ }
310
+ }];
311
+ [buttons addObject:button];
312
+ }
313
+ }
314
+ [nowPlayingTemplate updateNowPlayingButtons:buttons];
315
+ carPlayTemplate = nowPlayingTemplate;
316
+ } else if ([type isEqualToString:@"tabbar"]) {
317
+ CPTabBarTemplate *tabBarTemplate = [[CPTabBarTemplate alloc] initWithTemplates:[self parseTemplatesFrom:config]];
318
+ tabBarTemplate.delegate = self;
319
+ carPlayTemplate = tabBarTemplate;
320
+ } else if ([type isEqualToString:@"contact"]) {
321
+ NSString *nm = [RCTConvert NSString:config[@"name"]];
322
+ UIImage *img = [RCTConvert UIImage:config[@"image"]];
323
+ CPContact *contact = [[CPContact alloc] initWithName:nm image:img];
324
+ [contact setSubtitle:config[@"subtitle"]];
325
+ [contact setActions:[self parseButtons:config[@"actions"] templateId:templateId]];
326
+ CPContactTemplate *contactTemplate = [[CPContactTemplate alloc] initWithContact:contact];
327
+ carPlayTemplate = contactTemplate;
328
+ } else if ([type isEqualToString:@"actionsheet"]) {
329
+ NSString *title = [RCTConvert NSString:config[@"title"]];
330
+ NSString *message = [RCTConvert NSString:config[@"message"]];
331
+ NSMutableArray<CPAlertAction *> *actions = [NSMutableArray new];
332
+ NSArray<NSDictionary*> *_actions = [RCTConvert NSDictionaryArray:config[@"actions"]];
333
+ for (NSDictionary *_action in _actions) {
334
+ CPAlertAction *action = [[CPAlertAction alloc] initWithTitle:[RCTConvert NSString:_action[@"title"]] style:[RCTConvert CPAlertActionStyle:_action[@"style"]] handler:^(CPAlertAction *a) {
335
+ if (self->hasListeners) {
336
+ [self sendEventWithName:@"actionButtonPressed" body:@{@"templateId":templateId, @"id": _action[@"id"] }];
337
+ }
338
+ }];
339
+ [actions addObject:action];
340
+ }
341
+ CPActionSheetTemplate *actionSheetTemplate = [[CPActionSheetTemplate alloc] initWithTitle:title message:message actions:actions];
342
+ carPlayTemplate = actionSheetTemplate;
343
+ } else if ([type isEqualToString:@"alert"]) {
344
+ NSMutableArray<CPAlertAction *> *actions = [NSMutableArray new];
345
+ NSArray<NSDictionary*> *_actions = [RCTConvert NSDictionaryArray:config[@"actions"]];
346
+ for (NSDictionary *_action in _actions) {
347
+ CPAlertAction *action = [[CPAlertAction alloc] initWithTitle:[RCTConvert NSString:_action[@"title"]] style:[RCTConvert CPAlertActionStyle:_action[@"style"]] handler:^(CPAlertAction *a) {
348
+ if (self->hasListeners) {
349
+ [self sendEventWithName:@"actionButtonPressed" body:@{@"templateId":templateId, @"id": _action[@"id"] }];
350
+ }
351
+ }];
352
+ [actions addObject:action];
353
+ }
354
+ NSArray<NSString*>* titleVariants = [RCTConvert NSArray:config[@"titleVariants"]];
355
+ CPAlertTemplate *alertTemplate = [[CPAlertTemplate alloc] initWithTitleVariants:titleVariants actions:actions];
356
+ carPlayTemplate = alertTemplate;
357
+ } else if ([type isEqualToString:@"poi"]) {
358
+ // NSString *title = [RCTConvert NSString:config[@"title"]];
359
+ // NSMutableArray<__kindof CPPointOfInterest *> * items = [NSMutableArray new];
360
+ // NSUInteger selectedIndex = 0;
361
+
362
+ // NSArray<NSDictionary*> *_items = [RCTConvert NSDictionaryArray:config[@"items"]];
363
+ // for (NSDictionary *_item in _items) {
364
+ // CPPointOfInterest *poi = [RCTConvert CPPointOfInterest:_item];
365
+ // [poi setUserInfo:_item];
366
+ // [items addObject:poi];
367
+ // }
368
+
369
+ // CPPointOfInterestTemplate *poiTemplate = [[CPPointOfInterestTemplate alloc] initWithTitle:title pointsOfInterest:items selectedIndex:selectedIndex];
370
+ RNCPStore *store = [RNCPStore sharedManager];
371
+
372
+ CPPointOfInterestTemplate *poiTemplate = [self createCarPlayPOISCreen:store.interfaceController responseList:config[@"items"] templateId:templateId];
373
+
374
+ poiTemplate.pointOfInterestDelegate = self;
375
+ carPlayTemplate = poiTemplate;
376
+ } else if ([type isEqualToString:@"information"]) {
377
+ NSString *title = [RCTConvert NSString:config[@"title"]];
378
+ CPInformationTemplateLayout layout = [RCTConvert BOOL:config[@"leading"]] ? CPInformationTemplateLayoutLeading : CPInformationTemplateLayoutTwoColumn;
379
+ NSMutableArray<__kindof CPInformationItem *> * items = [NSMutableArray new];
380
+ NSMutableArray<__kindof CPTextButton *> * actions = [NSMutableArray new];
381
+
382
+ NSArray<NSDictionary*> *_items = [RCTConvert NSDictionaryArray:config[@"items"]];
383
+ for (NSDictionary *_item in _items) {
384
+ [items addObject:[[CPInformationItem alloc] initWithTitle:_item[@"title"] detail:_item[@"detail"]]];
385
+ }
386
+
387
+ NSArray<NSDictionary*> *_actions = [RCTConvert NSDictionaryArray:config[@"actions"]];
388
+ for (NSDictionary *_action in _actions) {
389
+ CPTextButton *action = [[CPTextButton alloc] initWithTitle:_action[@"title"] textStyle:CPTextButtonStyleNormal handler:^(__kindof CPTextButton * _Nonnull contactButton) {
390
+ if (self->hasListeners) {
391
+ [self sendEventWithName:@"actionButtonPressed" body:@{@"templateId":templateId, @"id": _action[@"id"] }];
392
+ }
393
+ }];
394
+ [actions addObject:action];
395
+ }
396
+
397
+ CPInformationTemplate *informationTemplate = [[CPInformationTemplate alloc] initWithTitle:title layout:layout items:items actions:actions];
398
+ carPlayTemplate = informationTemplate;
399
+ }
400
+
401
+ if (config[@"tabSystemItem"]) {
402
+ carPlayTemplate.tabSystemItem = [RCTConvert NSInteger:config[@"tabSystemItem"]];
403
+ }
404
+ if (config[@"tabSystemImageName"]) {
405
+ carPlayTemplate.tabImage = [UIImage systemImageNamed:[RCTConvert NSString:config[@"tabSystemImageName"]]];
406
+ }
407
+ if (config[@"tabImage"]) {
408
+ carPlayTemplate.tabImage = [RCTConvert UIImage:config[@"tabImage"]];
409
+ }
410
+ if (config[@"tabTitle"]) {
411
+ carPlayTemplate.tabTitle = [RCTConvert NSString:config[@"tabTitle"]];
412
+ }
413
+
414
+ [carPlayTemplate setUserInfo:@{ @"templateId": templateId }];
415
+ [store setTemplate:templateId template:carPlayTemplate];
416
+ }
417
+
418
+ RCT_EXPORT_METHOD(createTrip:(NSString*)tripId config:(NSDictionary*)config) {
419
+ RNCPStore *store = [RNCPStore sharedManager];
420
+ CPTrip *trip = [self parseTrip:config];
421
+ NSMutableDictionary *userInfo = trip.userInfo;
422
+ if (!userInfo) {
423
+ userInfo = [[NSMutableDictionary alloc] init];
424
+ trip.userInfo = userInfo;
425
+ }
426
+
427
+ [userInfo setValue:tripId forKey:@"id"];
428
+ [store setTrip:tripId trip:trip];
429
+ }
430
+
431
+ RCT_EXPORT_METHOD(updateTravelEstimatesForTrip:(NSString*)templateId tripId:(NSString*)tripId travelEstimates:(NSDictionary*)travelEstimates timeRemainingColor:(NSUInteger*)timeRemainingColor) {
432
+ RNCPStore *store = [RNCPStore sharedManager];
433
+ CPTemplate *template = [store findTemplateById:templateId];
434
+ if (template) {
435
+ CPMapTemplate *mapTemplate = (CPMapTemplate*) template;
436
+ CPTrip *trip = [[RNCPStore sharedManager] findTripById:tripId];
437
+ if (trip) {
438
+ CPTravelEstimates *estimates = [self parseTravelEstimates:travelEstimates];
439
+ [mapTemplate updateTravelEstimates:estimates forTrip:trip withTimeRemainingColor:(CPTimeRemainingColor) timeRemainingColor];
440
+ }
441
+ }
442
+ }
443
+
444
+ RCT_REMAP_METHOD(startNavigationSession,
445
+ templateId:(NSString *)templateId
446
+ tripId:(NSString *)tripId
447
+ startNavigationSessionWithResolver:(RCTPromiseResolveBlock)resolve
448
+ rejecter:(RCTPromiseRejectBlock)reject) {
449
+ RNCPStore *store = [RNCPStore sharedManager];
450
+ CPTemplate *template = [store findTemplateById:templateId];
451
+ if (template) {
452
+ CPMapTemplate *mapTemplate = (CPMapTemplate*) template;
453
+ CPTrip *trip = [[RNCPStore sharedManager] findTripById:tripId];
454
+ if (trip) {
455
+ CPNavigationSession *navigationSession = [mapTemplate startNavigationSessionForTrip:trip];
456
+ [store setNavigationSession:tripId navigationSession:navigationSession];
457
+ resolve(@{ @"tripId": tripId, @"navigationSessionId": tripId });
458
+ }
459
+ } else {
460
+ reject(@"template_not_found", @"Template not found in store", nil);
461
+ }
462
+ }
463
+
464
+ RCT_EXPORT_METHOD(updateManeuversNavigationSession:(NSString*)navigationSessionId maneuvers:(NSArray*)maneuvers) {
465
+ CPNavigationSession* navigationSession = [[RNCPStore sharedManager] findNavigationSessionById:navigationSessionId];
466
+ if (navigationSession) {
467
+ NSMutableArray<CPManeuver*>* upcomingManeuvers = [NSMutableArray array];
468
+ for (NSDictionary *maneuver in maneuvers) {
469
+ [upcomingManeuvers addObject:[self parseManeuver:maneuver]];
470
+ }
471
+ [navigationSession setUpcomingManeuvers:upcomingManeuvers];
472
+ }
473
+ }
474
+
475
+ RCT_EXPORT_METHOD(updateTravelEstimatesNavigationSession:(NSString*)navigationSessionId maneuverIndex:(NSUInteger)maneuverIndex travelEstimates:(NSDictionary*)travelEstimates) {
476
+ CPNavigationSession* navigationSession = [[RNCPStore sharedManager] findNavigationSessionById:navigationSessionId];
477
+ if (navigationSession) {
478
+ CPManeuver *maneuver = [[navigationSession upcomingManeuvers] objectAtIndex:maneuverIndex];
479
+ if (maneuver) {
480
+ [navigationSession updateTravelEstimates:[self parseTravelEstimates:travelEstimates] forManeuver:maneuver];
481
+ }
482
+ }
483
+ }
484
+
485
+ RCT_EXPORT_METHOD(pauseNavigationSession:(NSString*)navigationSessionId reason:(NSUInteger*)reason description:(NSString*)description) {
486
+ CPNavigationSession* navigationSession = [[RNCPStore sharedManager] findNavigationSessionById:navigationSessionId];
487
+ if (navigationSession) {
488
+ [navigationSession pauseTripForReason:(CPTripPauseReason) reason description:description];
489
+ } else {
490
+ NSLog(@"Could not find session");
491
+ }
492
+ }
493
+
494
+ RCT_EXPORT_METHOD(cancelNavigationSession:(NSString*)navigationSessionId) {
495
+ CPNavigationSession* navigationSession = [[RNCPStore sharedManager] findNavigationSessionById:navigationSessionId];
496
+ if (navigationSession) {
497
+ [navigationSession cancelTrip];
498
+ } else {
499
+ NSLog(@"Could not cancel. No session found.");
500
+ }
501
+ }
502
+
503
+ RCT_EXPORT_METHOD(finishNavigationSession:(NSString*)navigationSessionId) {
504
+ CPNavigationSession* navigationSession = [[RNCPStore sharedManager] findNavigationSessionById:navigationSessionId];
505
+ if (navigationSession) {
506
+ [navigationSession finishTrip];
507
+ }
508
+ }
509
+
510
+ RCT_EXPORT_METHOD(setRootTemplate:(NSString *)templateId animated:(BOOL)animated) {
511
+ RNCPStore *store = [RNCPStore sharedManager];
512
+ CPTemplate *template = [store findTemplateById:templateId];
513
+
514
+ store.interfaceController.delegate = self;
515
+
516
+ if (template) {
517
+ [store.interfaceController setRootTemplate:template animated:animated completion:^(BOOL done, NSError * _Nullable err) {
518
+ NSLog(@"error at setRootTemplate%@", err);
519
+ // noop
520
+ }];
521
+ } else {
522
+ NSLog(@"Failed to find template %@", template);
523
+ }
524
+ }
525
+
526
+ RCT_EXPORT_METHOD(pushTemplate:(NSString *)templateId animated:(BOOL)animated) {
527
+ RNCPStore *store = [RNCPStore sharedManager];
528
+ CPTemplate *template = [store findTemplateById:templateId];
529
+ if (template) {
530
+ [store.interfaceController pushTemplate:template animated:animated completion:^(BOOL done, NSError * _Nullable err) {
531
+ NSLog(@"error at pushTemplate %@", err);
532
+ // noop
533
+ }];
534
+ } else {
535
+ NSLog(@"Failed to find template %@", template);
536
+ }
537
+ }
538
+
539
+ RCT_EXPORT_METHOD(popToTemplate:(NSString *)templateId animated:(BOOL)animated) {
540
+ RNCPStore *store = [RNCPStore sharedManager];
541
+ CPTemplate *template = [store findTemplateById:templateId];
542
+ if (template) {
543
+ [store.interfaceController popToTemplate:template animated:animated completion:^(BOOL done, NSError * _Nullable err) {
544
+ NSLog(@"error at popToTemplate %@", err);
545
+ // noop
546
+ }];
547
+ } else {
548
+ NSLog(@"Failed to find template %@", template);
549
+ }
550
+ }
551
+
552
+ RCT_EXPORT_METHOD(popToRootTemplate:(BOOL)animated) {
553
+ RNCPStore *store = [RNCPStore sharedManager];
554
+ [store.interfaceController popToRootTemplateAnimated:animated completion:^(BOOL done, NSError * _Nullable err) {
555
+ NSLog(@"error at popToRootTemplate %@", err);
556
+ // noop
557
+ }];
558
+ }
559
+
560
+ RCT_EXPORT_METHOD(popTemplate:(BOOL)animated) {
561
+ RNCPStore *store = [RNCPStore sharedManager];
562
+ [store.interfaceController popTemplateAnimated:animated completion:^(BOOL done, NSError * _Nullable err) {
563
+ NSLog(@"error at popTemplate %@", err);
564
+ // noop
565
+ }];
566
+ }
567
+
568
+ RCT_EXPORT_METHOD(presentTemplate:(NSString *)templateId animated:(BOOL)animated) {
569
+ RNCPStore *store = [RNCPStore sharedManager];
570
+ CPTemplate *template = [store findTemplateById:templateId];
571
+ if (template) {
572
+ [store.interfaceController presentTemplate:template animated:animated completion:^(BOOL done, NSError * _Nullable err) {
573
+ NSLog(@"error at presentTemplate %@", err);
574
+ // noop
575
+ }];
576
+ } else {
577
+ NSLog(@"Failed to find template %@", template);
578
+ }
579
+ }
580
+
581
+ RCT_EXPORT_METHOD(dismissTemplate:(BOOL)animated) {
582
+ RNCPStore *store = [RNCPStore sharedManager];
583
+ [store.interfaceController dismissTemplateAnimated:animated];
584
+ }
585
+
586
+ RCT_EXPORT_METHOD(updateListTemplate:(NSString*)templateId config:(NSDictionary*)config) {
587
+ RNCPStore *store = [RNCPStore sharedManager];
588
+ CPTemplate *template = [store findTemplateById:templateId];
589
+ if (template && [template isKindOfClass:[CPListTemplate class]]) {
590
+ CPListTemplate *listTemplate = (CPListTemplate *)template;
591
+ if (config[@"leadingNavigationBarButtons"]) {
592
+ NSArray *leadingNavigationBarButtons = [self parseBarButtons:[RCTConvert NSArray:config[@"leadingNavigationBarButtons"]] templateId:templateId];
593
+ [listTemplate setLeadingNavigationBarButtons:leadingNavigationBarButtons];
594
+ }
595
+ if (config[@"trailingNavigationBarButtons"]) {
596
+ NSArray *trailingNavigationBarButtons = [self parseBarButtons:[RCTConvert NSArray:config[@"trailingNavigationBarButtons"]] templateId:templateId];
597
+ [listTemplate setTrailingNavigationBarButtons:trailingNavigationBarButtons];
598
+ }
599
+ if (config[@"emptyViewTitleVariants"]) {
600
+ listTemplate.emptyViewTitleVariants = [RCTConvert NSArray:config[@"emptyViewTitleVariants"]];
601
+ }
602
+ if (config[@"emptyViewSubtitleVariants"]) {
603
+ NSLog(@"%@", [RCTConvert NSArray:config[@"emptyViewSubtitleVariants"]]);
604
+ listTemplate.emptyViewSubtitleVariants = [RCTConvert NSArray:config[@"emptyViewSubtitleVariants"]];
605
+ }
606
+ }
607
+ }
608
+
609
+ RCT_EXPORT_METHOD(updateTabBarTemplates:(NSString *)templateId templates:(NSDictionary*)config) {
610
+ RNCPStore *store = [RNCPStore sharedManager];
611
+ CPTemplate *template = [store findTemplateById:templateId];
612
+ if (template) {
613
+ CPTabBarTemplate *tabBarTemplate = (CPTabBarTemplate*) template;
614
+ [tabBarTemplate updateTemplates:[self parseTemplatesFrom:config]];
615
+ } else {
616
+ NSLog(@"Failed to find template %@", template);
617
+ }
618
+ }
619
+
620
+
621
+ RCT_EXPORT_METHOD(updateListTemplateSections:(NSString *)templateId sections:(NSArray*)sections) {
622
+ RNCPStore *store = [RNCPStore sharedManager];
623
+ CPTemplate *template = [store findTemplateById:templateId];
624
+ if (template) {
625
+ CPListTemplate *listTemplate = (CPListTemplate*) template;
626
+ [listTemplate updateSections:[self parseSections:sections]];
627
+ } else {
628
+ NSLog(@"Failed to find template %@", template);
629
+ }
630
+ }
631
+
632
+ RCT_EXPORT_METHOD(updateListTemplateItem:(NSString *)templateId config:(NSDictionary*)config) {
633
+ RNCPStore *store = [RNCPStore sharedManager];
634
+ CPTemplate *template = [store findTemplateById:templateId];
635
+ if (template) {
636
+ CPListTemplate *listTemplate = (CPListTemplate*) template;
637
+ NSInteger sectionIndex = [RCTConvert NSInteger:config[@"sectionIndex"]];
638
+ if (sectionIndex >= listTemplate.sections.count) {
639
+ NSLog(@"Failed to update item at section %d, sections size is %d", index, listTemplate.sections.count);
640
+ return;
641
+ }
642
+ CPListSection *section = listTemplate.sections[sectionIndex];
643
+ NSInteger index = [RCTConvert NSInteger:config[@"itemIndex"]];
644
+ if (index >= section.items.count) {
645
+ NSLog(@"Failed to update item at index %d, section size is %d", index, section.items.count);
646
+ return;
647
+ }
648
+ CPListItem *item = (CPListItem *)section.items[index];
649
+ if (config[@"imgUrl"]) {
650
+ NSString *imgUrlString = [RCTConvert NSString:config[@"imgUrl"]];
651
+ [self updateItemImageWithURL:item imgUrl:imgUrlString];
652
+ }
653
+ if (config[@"image"]) {
654
+ [item setImage:[RCTConvert UIImage:config[@"image"]]];
655
+ }
656
+ if (config[@"text"]) {
657
+ [item setText:[RCTConvert NSString:config[@"text"]]];
658
+ }
659
+ if (config[@"detailText"]) {
660
+ [item setDetailText:[RCTConvert NSString:config[@"detailText"]]];
661
+ }
662
+ if (config[@"isPlaying"]) {
663
+ [item setPlaying:[RCTConvert BOOL:config[@"isPlaying"]]];
664
+ }
665
+ } else {
666
+ NSLog(@"Failed to find template %@", template);
667
+ }
668
+ }
669
+
670
+ RCT_EXPORT_METHOD(updateInformationTemplateItems:(NSString *)templateId items:(NSArray*)items) {
671
+ RNCPStore *store = [RNCPStore sharedManager];
672
+ CPTemplate *template = [store findTemplateById:templateId];
673
+ if (template) {
674
+ CPInformationTemplate *informationTemplate = (CPInformationTemplate*) template;
675
+ informationTemplate.items = [self parseInformationItems:items];
676
+ } else {
677
+ NSLog(@"Failed to find template %@", template);
678
+ }
679
+ }
680
+
681
+ RCT_EXPORT_METHOD(updateInformationTemplateActions:(NSString *)templateId items:(NSArray*)actions) {
682
+ RNCPStore *store = [RNCPStore sharedManager];
683
+ CPTemplate *template = [store findTemplateById:templateId];
684
+ if (template) {
685
+ CPInformationTemplate *informationTemplate = (CPInformationTemplate*) template;
686
+ informationTemplate.actions = [self parseInformationActions:actions templateId:templateId];
687
+ } else {
688
+ NSLog(@"Failed to find template %@", template);
689
+ }
690
+ }
691
+
692
+ RCT_EXPORT_METHOD(getMaximumListItemCount:(NSString *)templateId
693
+ resolver:(RCTPromiseResolveBlock)resolve
694
+ rejecter:(RCTPromiseRejectBlock)reject) {
695
+ RNCPStore *store = [RNCPStore sharedManager];
696
+ CPTemplate *template = [store findTemplateById:templateId];
697
+ if (template) {
698
+ CPListTemplate *listTemplate = (CPListTemplate*) template;
699
+ resolve(@(CPListTemplate.maximumItemCount));
700
+ } else {
701
+ NSLog(@"Failed to find template %@", template);
702
+ reject(@"template_not_found", @"Template not found in store", nil);
703
+ }
704
+ }
705
+
706
+ RCT_EXPORT_METHOD(getMaximumListSectionCount:(NSString *)templateId
707
+ resolver:(RCTPromiseResolveBlock)resolve
708
+ rejecter:(RCTPromiseRejectBlock)reject) {
709
+ RNCPStore *store = [RNCPStore sharedManager];
710
+ CPTemplate *template = [store findTemplateById:templateId];
711
+ if (template) {
712
+ CPListTemplate *listTemplate = (CPListTemplate*) template;
713
+ resolve(@(CPListTemplate.maximumSectionCount));
714
+ } else {
715
+ NSLog(@"Failed to find template %@", template);
716
+ reject(@"template_not_found", @"Template not found in store", nil);
717
+ }
718
+ }
719
+
720
+ RCT_EXPORT_METHOD(updateMapTemplateConfig:(NSString *)templateId config:(NSDictionary*)config) {
721
+ CPTemplate *template = [[RNCPStore sharedManager] findTemplateById:templateId];
722
+ if (template) {
723
+ CPMapTemplate *mapTemplate = (CPMapTemplate*) template;
724
+ [self applyConfigForMapTemplate:mapTemplate templateId:templateId config:config];
725
+ } else {
726
+ NSLog(@"Failed to find template %@", template);
727
+ }
728
+ }
729
+
730
+ RCT_EXPORT_METHOD(showPanningInterface:(NSString *)templateId animated:(BOOL)animated) {
731
+ CPTemplate *template = [[RNCPStore sharedManager] findTemplateById:templateId];
732
+ if (template) {
733
+ CPMapTemplate *mapTemplate = (CPMapTemplate*) template;
734
+ [mapTemplate showPanningInterfaceAnimated:animated];
735
+ } else {
736
+ NSLog(@"Failed to find template %@", template);
737
+ }
738
+ }
739
+
740
+ RCT_EXPORT_METHOD(dismissPanningInterface:(NSString *)templateId animated:(BOOL)animated) {
741
+ CPTemplate *template = [[RNCPStore sharedManager] findTemplateById:templateId];
742
+ if (template) {
743
+ CPMapTemplate *mapTemplate = (CPMapTemplate*) template;
744
+ [mapTemplate dismissPanningInterfaceAnimated:animated];
745
+ } else {
746
+ NSLog(@"Failed to find template %@", template);
747
+ }
748
+ }
749
+
750
+ RCT_EXPORT_METHOD(enableNowPlaying:(BOOL)enable) {
751
+ if (enable && !isNowPlayingActive) {
752
+ [CPNowPlayingTemplate.sharedTemplate addObserver:self];
753
+ } else if (!enable && isNowPlayingActive) {
754
+ [CPNowPlayingTemplate.sharedTemplate removeObserver:self];
755
+ }
756
+ }
757
+
758
+ RCT_EXPORT_METHOD(hideTripPreviews:(NSString*)templateId) {
759
+ CPTemplate *template = [[RNCPStore sharedManager] findTemplateById:templateId];
760
+ if (template) {
761
+ CPMapTemplate *mapTemplate = (CPMapTemplate*) template;
762
+ [mapTemplate hideTripPreviews];
763
+ }
764
+ }
765
+
766
+ RCT_EXPORT_METHOD(showTripPreviews:(NSString*)templateId tripIds:(NSArray*)tripIds tripConfiguration:(NSDictionary*)tripConfiguration) {
767
+ CPTemplate *template = [[RNCPStore sharedManager] findTemplateById:templateId];
768
+ NSMutableArray *trips = [[NSMutableArray alloc] init];
769
+
770
+ for (NSString *tripId in tripIds) {
771
+ CPTrip *trip = [[RNCPStore sharedManager] findTripById:tripId];
772
+ if (trip) {
773
+ [trips addObject:trip];
774
+ }
775
+ }
776
+
777
+ if (template) {
778
+ CPMapTemplate *mapTemplate = (CPMapTemplate*) template;
779
+ [mapTemplate showTripPreviews:trips textConfiguration:[self parseTripPreviewTextConfiguration:tripConfiguration]];
780
+ }
781
+ }
782
+
783
+ RCT_EXPORT_METHOD(showRouteChoicesPreviewForTrip:(NSString*)templateId tripId:(NSString*)tripId tripConfiguration:(NSDictionary*)tripConfiguration) {
784
+ CPTemplate *template = [[RNCPStore sharedManager] findTemplateById:templateId];
785
+ CPTrip *trip = [[RNCPStore sharedManager] findTripById:tripId];
786
+
787
+ if (template) {
788
+ CPMapTemplate *mapTemplate = (CPMapTemplate*) template;
789
+ [mapTemplate showRouteChoicesPreviewForTrip:trip textConfiguration:[self parseTripPreviewTextConfiguration:tripConfiguration]];
790
+ }
791
+ }
792
+
793
+ RCT_EXPORT_METHOD(presentNavigationAlert:(NSString*)templateId json:(NSDictionary*)json animated:(BOOL)animated) {
794
+ CPTemplate *template = [[RNCPStore sharedManager] findTemplateById:templateId];
795
+ if (template) {
796
+ CPMapTemplate *mapTemplate = (CPMapTemplate*) template;
797
+ [mapTemplate presentNavigationAlert:[self parseNavigationAlert:json templateId:templateId] animated:animated];
798
+ }
799
+ }
800
+
801
+ RCT_EXPORT_METHOD(dismissNavigationAlert:(NSString*)templateId animated:(BOOL)animated) {
802
+ CPTemplate *template = [[RNCPStore sharedManager] findTemplateById:templateId];
803
+ if (template) {
804
+ CPMapTemplate *mapTemplate = (CPMapTemplate*) template;
805
+ [mapTemplate dismissNavigationAlertAnimated:YES completion:^(BOOL completion) {
806
+ [self sendTemplateEventWithName:template name:@"didDismissNavigationAlert"];
807
+ }];
808
+ }
809
+ }
810
+
811
+ RCT_EXPORT_METHOD(activateVoiceControlState:(NSString*)templateId identifier:(NSString*)identifier) {
812
+ CPTemplate *template = [[RNCPStore sharedManager] findTemplateById:templateId];
813
+ if (template) {
814
+ CPVoiceControlTemplate *voiceTemplate = (CPVoiceControlTemplate*) template;
815
+ [voiceTemplate activateVoiceControlStateWithIdentifier:identifier];
816
+ }
817
+ }
818
+
819
+ RCT_EXPORT_METHOD(reactToUpdatedSearchText:(NSArray *)items) {
820
+ NSArray *sectionsItems = [self parseListItems:items startIndex:0];
821
+
822
+ if (self.searchResultBlock) {
823
+ self.searchResultBlock(sectionsItems);
824
+ self.searchResultBlock = nil;
825
+ }
826
+ }
827
+
828
+ RCT_EXPORT_METHOD(reactToSelectedResult:(BOOL)status) {
829
+ if (self.selectedResultBlock) {
830
+ self.selectedResultBlock();
831
+ self.selectedResultBlock = nil;
832
+ }
833
+ }
834
+
835
+ RCT_EXPORT_METHOD(updateMapTemplateMapButtons:(NSString*) templateId mapButtons:(NSArray*) mapButtonConfig) {
836
+ CPTemplate *template = [[RNCPStore sharedManager] findTemplateById:templateId];
837
+ if (template) {
838
+ CPMapTemplate *mapTemplate = (CPMapTemplate*) template;
839
+ NSArray *mapButtons = [RCTConvert NSArray:mapButtonConfig];
840
+ NSMutableArray *result = [NSMutableArray array];
841
+ for (NSDictionary *mapButton in mapButtons) {
842
+ NSString *_id = [mapButton objectForKey:@"id"];
843
+ [result addObject:[RCTConvert CPMapButton:mapButton withHandler:^(CPMapButton * _Nonnull mapButton) {
844
+ [self sendTemplateEventWithName:mapTemplate name:@"mapButtonPressed" json:@{ @"id": _id }];
845
+ }]];
846
+ }
847
+ [mapTemplate setMapButtons:result];
848
+ }
849
+ }
850
+
851
+ # pragma parsers
852
+
853
+ - (void) applyConfigForMapTemplate:(CPMapTemplate*)mapTemplate templateId:(NSString*)templateId config:(NSDictionary*)config {
854
+ RNCPStore *store = [RNCPStore sharedManager];
855
+
856
+ if ([config objectForKey:@"guidanceBackgroundColor"]) {
857
+ [mapTemplate setGuidanceBackgroundColor:[RCTConvert UIColor:config[@"guidanceBackgroundColor"]]];
858
+ }
859
+ else {
860
+ [mapTemplate setGuidanceBackgroundColor:UIColor.systemGray5Color];
861
+ }
862
+
863
+ if ([config objectForKey:@"tripEstimateStyle"]) {
864
+ [mapTemplate setTripEstimateStyle:[RCTConvert CPTripEstimateStyle:config[@"tripEstimateStyle"]]];
865
+ }
866
+ else {
867
+ [mapTemplate setTripEstimateStyle:CPTripEstimateStyleDark];
868
+ }
869
+
870
+ if ([config objectForKey:@"leadingNavigationBarButtons"]){
871
+ NSArray *leadingNavigationBarButtons = [self parseBarButtons:[RCTConvert NSArray:config[@"leadingNavigationBarButtons"]] templateId:templateId];
872
+ [mapTemplate setLeadingNavigationBarButtons:leadingNavigationBarButtons];
873
+ }
874
+
875
+ if ([config objectForKey:@"trailingNavigationBarButtons"]){
876
+ NSArray *trailingNavigationBarButtons = [self parseBarButtons:[RCTConvert NSArray:config[@"trailingNavigationBarButtons"]] templateId:templateId];
877
+ [mapTemplate setTrailingNavigationBarButtons:trailingNavigationBarButtons];
878
+ }
879
+
880
+ if ([config objectForKey:@"mapButtons"]) {
881
+ NSArray *mapButtons = [RCTConvert NSArray:config[@"mapButtons"]];
882
+ NSMutableArray *result = [NSMutableArray array];
883
+ for (NSDictionary *mapButton in mapButtons) {
884
+ NSString *_id = [mapButton objectForKey:@"id"];
885
+ [result addObject:[RCTConvert CPMapButton:mapButton withHandler:^(CPMapButton * _Nonnull mapButton) {
886
+ [self sendTemplateEventWithName:mapTemplate name:@"mapButtonPressed" json:@{ @"id": _id }];
887
+ }]];
888
+ }
889
+ [mapTemplate setMapButtons:result];
890
+ }
891
+
892
+ if ([config objectForKey:@"automaticallyHidesNavigationBar"]) {
893
+ [mapTemplate setAutomaticallyHidesNavigationBar:[RCTConvert BOOL:config[@"automaticallyHidesNavigationBar"]]];
894
+ }
895
+
896
+ if ([config objectForKey:@"hidesButtonsWithNavigationBar"]) {
897
+ [mapTemplate setHidesButtonsWithNavigationBar:[RCTConvert BOOL:config[@"hidesButtonsWithNavigationBar"]]];
898
+ }
899
+
900
+ if ([config objectForKey:@"render"]) {
901
+ RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:self.bridge moduleName:templateId initialProperties:@{}];
902
+ [rootView setFrame:store.window.frame];
903
+ [[store.window subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
904
+ [store.window addSubview:rootView];
905
+ }
906
+ }
907
+
908
+ - (NSArray<__kindof CPTemplate*>*) parseTemplatesFrom:(NSDictionary*)config {
909
+ RNCPStore *store = [RNCPStore sharedManager];
910
+ NSMutableArray<__kindof CPTemplate*> *templates = [NSMutableArray new];
911
+ NSArray<NSDictionary*> *tpls = [RCTConvert NSDictionaryArray:config[@"templates"]];
912
+ for (NSDictionary *tpl in tpls) {
913
+ CPTemplate *templ = [store findTemplateById:tpl[@"id"]];
914
+ // @todo UITabSystemItem
915
+ [templates addObject:templ];
916
+ }
917
+ return templates;
918
+ }
919
+
920
+ - (NSArray<CPButton*>*) parseButtons:(NSArray*)buttons templateId:(NSString *)templateId {
921
+ NSMutableArray *result = [NSMutableArray array];
922
+ for (NSDictionary *button in buttons) {
923
+ CPButton *_button;
924
+ NSString *_id = [button objectForKey:@"id"];
925
+ NSString *type = [button objectForKey:@"type"];
926
+ if ([type isEqualToString:@"call"]) {
927
+ _button = [[CPContactCallButton alloc] initWithHandler:^(__kindof CPButton * _Nonnull contactButton) {
928
+ if (self->hasListeners) {
929
+ [self sendEventWithName:@"buttonPressed" body:@{@"id": _id, @"templateId":templateId}];
930
+ }
931
+ }];
932
+ } else if ([type isEqualToString:@"message"]) {
933
+ _button = [[CPContactMessageButton alloc] initWithPhoneOrEmail:[button objectForKey:@"phoneOrEmail"]];
934
+ } else if ([type isEqualToString:@"directions"]) {
935
+ _button = [[CPContactDirectionsButton alloc] initWithHandler:^(__kindof CPButton * _Nonnull contactButton) {
936
+ if (self->hasListeners) {
937
+ [self sendEventWithName:@"buttonPressed" body:@{@"id": _id, @"templateId":templateId}];
938
+ }
939
+ }];
940
+ }
941
+
942
+ BOOL _disabled = [button objectForKey:@"disabled"];
943
+ [_button setEnabled:!_disabled];
944
+
945
+ NSString *_title = [button objectForKey:@"title"];
946
+ [_button setTitle:_title];
947
+
948
+ [result addObject:_button];
949
+ }
950
+ return result;
951
+ }
952
+
953
+ - (NSArray<CPBarButton*>*) parseBarButtons:(NSArray*)barButtons templateId:(NSString *)templateId {
954
+ NSMutableArray *result = [NSMutableArray array];
955
+ for (NSDictionary *barButton in barButtons) {
956
+ CPBarButtonType _type;
957
+ NSString *_id = [barButton objectForKey:@"id"];
958
+ NSString *type = [barButton objectForKey:@"type"];
959
+ if (type && [type isEqualToString:@"image"]) {
960
+ _type = CPBarButtonTypeImage;
961
+ } else {
962
+ _type = CPBarButtonTypeText;
963
+ }
964
+ CPBarButton *_barButton = [[CPBarButton alloc] initWithType:_type handler:^(CPBarButton * _Nonnull barButton) {
965
+ if (self->hasListeners) {
966
+ [self sendEventWithName:@"barButtonPressed" body:@{@"id": _id, @"templateId":templateId}];
967
+ }
968
+ }];
969
+ BOOL _disabled = [barButton objectForKey:@"disabled"];
970
+ [_barButton setEnabled:!_disabled];
971
+
972
+ if (_type == CPBarButtonTypeText) {
973
+ NSString *_title = [barButton objectForKey:@"title"];
974
+ [_barButton setTitle:_title];
975
+ } else if (_type == CPBarButtonTypeImage) {
976
+ UIImage *_image = [RCTConvert UIImage:[barButton objectForKey:@"image"]];
977
+ [_barButton setImage:_image];
978
+ }
979
+ [result addObject:_barButton];
980
+ }
981
+ return result;
982
+ }
983
+
984
+ - (NSArray<CPListSection*>*)parseSections:(NSArray*)sections {
985
+ NSMutableArray *result = [NSMutableArray array];
986
+ int index = 0;
987
+ for (NSDictionary *section in sections) {
988
+ NSArray *items = [section objectForKey:@"items"];
989
+ NSString *_sectionIndexTitle = [section objectForKey:@"sectionIndexTitle"];
990
+ NSString *_header = [section objectForKey:@"header"];
991
+ NSArray *_items = [self parseListItems:items startIndex:index];
992
+ CPListSection *_section = [[CPListSection alloc] initWithItems:_items header:_header sectionIndexTitle:_sectionIndexTitle];
993
+ [result addObject:_section];
994
+ int count = (int) [items count];
995
+ index = index + count;
996
+ }
997
+ return result;
998
+ }
999
+
1000
+ - (NSArray<CPListItem*>*)parseListItems:(NSArray*)items startIndex:(int)startIndex {
1001
+ NSMutableArray *_items = [NSMutableArray array];
1002
+ int index = startIndex;
1003
+ for (NSDictionary *item in items) {
1004
+ BOOL _showsDisclosureIndicator = [[item objectForKey:@"showsDisclosureIndicator"] isEqualToNumber:[NSNumber numberWithInt:1]];
1005
+ NSString *_detailText = [item objectForKey:@"detailText"];
1006
+ NSString *_text = [item objectForKey:@"text"];
1007
+ UIImage *_image = [RCTConvert UIImage:[item objectForKey:@"image"]];
1008
+ CPListItem *_item;
1009
+ if (@available(iOS 14.0, *)) {
1010
+ CPListItemAccessoryType accessoryType = _showsDisclosureIndicator ? CPListItemAccessoryTypeDisclosureIndicator : CPListItemAccessoryTypeNone;
1011
+ _item = [[CPListItem alloc] initWithText:_text detailText:_detailText image:_image accessoryImage:nil accessoryType:accessoryType];
1012
+ } else {
1013
+ _item = [[CPListItem alloc] initWithText:_text detailText:_detailText image:_image showsDisclosureIndicator:_showsDisclosureIndicator];
1014
+ }
1015
+ if ([item objectForKey:@"isPlaying"]) {
1016
+ [_item setPlaying:[RCTConvert BOOL:[item objectForKey:@"isPlaying"]]];
1017
+ }
1018
+ if (item[@"imgUrl"]) {
1019
+ NSString *imgUrlString = [RCTConvert NSString:item[@"imgUrl"]];
1020
+ [self updateItemImageWithURL:_item imgUrl:imgUrlString];
1021
+ }
1022
+ [_item setUserInfo:@{ @"index": @(index) }];
1023
+ [_items addObject:_item];
1024
+ index = index + 1;
1025
+ }
1026
+ return _items;
1027
+ }
1028
+
1029
+ - (NSArray<CPInformationItem*>*)parseInformationItems:(NSArray*)items {
1030
+ NSMutableArray *_items = [NSMutableArray array];
1031
+ for (NSDictionary *item in items) {
1032
+ [_items addObject:[[CPInformationItem alloc] initWithTitle:item[@"title"] detail:item[@"detail"]]];
1033
+ }
1034
+
1035
+ return _items;
1036
+ }
1037
+
1038
+ - (NSArray<CPTextButton*>*)parseInformationActions:(NSArray*)actions templateId:(NSString *)templateId {
1039
+ NSMutableArray *_actions = [NSMutableArray array];
1040
+ for (NSDictionary *action in actions) {
1041
+ CPTextButton *_action = [[CPTextButton alloc] initWithTitle:action[@"title"] textStyle:CPTextButtonStyleNormal handler:^(__kindof CPTextButton * _Nonnull contactButton) {
1042
+ if (self->hasListeners) {
1043
+ [self sendEventWithName:@"actionButtonPressed" body:@{@"templateId":templateId, @"id": action[@"id"] }];
1044
+ }
1045
+ }];
1046
+ [_actions addObject:_action];
1047
+ }
1048
+
1049
+ return _actions;
1050
+ }
1051
+
1052
+
1053
+ - (NSArray<CPGridButton*>*)parseGridButtons:(NSArray*)buttons templateId:(NSString*)templateId {
1054
+ NSMutableArray *result = [NSMutableArray array];
1055
+ int index = 0;
1056
+ for (NSDictionary *button in buttons) {
1057
+ NSString *_id = [button objectForKey:@"id"];
1058
+ NSArray<NSString*> *_titleVariants = [button objectForKey:@"titleVariants"];
1059
+ UIImage *_image = [RCTConvert UIImage:[button objectForKey:@"image"]];
1060
+ CPGridButton *_button = [[CPGridButton alloc] initWithTitleVariants:_titleVariants image:_image handler:^(CPGridButton * _Nonnull barButton) {
1061
+ if (self->hasListeners) {
1062
+ [self sendEventWithName:@"gridButtonPressed" body:@{@"id": _id, @"templateId":templateId, @"index": @(index) }];
1063
+ }
1064
+ }];
1065
+ BOOL _disabled = [button objectForKey:@"disabled"];
1066
+ [_button setEnabled:!_disabled];
1067
+ [result addObject:_button];
1068
+ index = index + 1;
1069
+ }
1070
+ return result;
1071
+ }
1072
+
1073
+ - (CPTravelEstimates*)parseTravelEstimates: (NSDictionary*)json {
1074
+ NSString *units = [RCTConvert NSString:json[@"distanceUnits"]];
1075
+ double value = [RCTConvert double:json[@"distanceRemaining"]];
1076
+
1077
+ NSUnit *unit = [NSUnitLength kilometers];
1078
+ if (units && [units isEqualToString: @"meters"]) {
1079
+ unit = [NSUnitLength meters];
1080
+ }
1081
+ else if (units && [units isEqualToString: @"miles"]) {
1082
+ unit = [NSUnitLength miles];
1083
+ }
1084
+ else if (units && [units isEqualToString: @"feet"]) {
1085
+ unit = [NSUnitLength feet];
1086
+ }
1087
+ else if (units && [units isEqualToString: @"yards"]) {
1088
+ unit = [NSUnitLength yards];
1089
+ }
1090
+
1091
+ NSMeasurement *distance = [[NSMeasurement alloc] initWithDoubleValue:value unit:unit];
1092
+ double time = [RCTConvert double:json[@"timeRemaining"]];
1093
+
1094
+ return [[CPTravelEstimates alloc] initWithDistanceRemaining:distance timeRemaining:time];
1095
+ }
1096
+
1097
+ - (CPManeuver*)parseManeuver:(NSDictionary*)json {
1098
+ CPManeuver* maneuver = [[CPManeuver alloc] init];
1099
+
1100
+ if ([json objectForKey:@"junctionImage"]) {
1101
+ UIImage *junctionImage = [RCTConvert UIImage:json[@"junctionImage"]];
1102
+ [maneuver setJunctionImage:[self imageWithTint:junctionImage andTintColor:[UIColor whiteColor]]];
1103
+ }
1104
+
1105
+ if ([json objectForKey:@"initialTravelEstimates"]) {
1106
+ CPTravelEstimates* travelEstimates = [self parseTravelEstimates:json[@"initialTravelEstimates"]];
1107
+ [maneuver setInitialTravelEstimates:travelEstimates];
1108
+ }
1109
+
1110
+ if ([json objectForKey:@"symbolImage"]) {
1111
+ UIImage *symbolImage = [RCTConvert UIImage:json[@"symbolImage"]];
1112
+
1113
+ if ([json objectForKey:@"symbolImageSize"]) {
1114
+ NSDictionary *size = [RCTConvert NSDictionary:json[@"symbolImageSize"]];
1115
+ double width = [RCTConvert double:size[@"width"]];
1116
+ double height = [RCTConvert double:size[@"height"]];
1117
+ symbolImage = [self imageWithSize:symbolImage convertToSize:CGSizeMake(width, height)];
1118
+ }
1119
+
1120
+ BOOL shouldTint = [RCTConvert BOOL:json[@"tintSymbolImage"]];
1121
+ if ([json objectForKey:@"tintSymbolImage"]) {
1122
+ UIColor *tintColor = [RCTConvert UIColor:json[@"tintSymbolImage"]];
1123
+ UIImage *darkImage = symbolImage;
1124
+ UIImage *lightImage = [self imageWithTint:symbolImage andTintColor:tintColor];
1125
+ symbolImage = [self dynamicImageWithNormalImage:lightImage darkImage:darkImage];
1126
+ }
1127
+
1128
+
1129
+ [maneuver setSymbolImage:symbolImage];
1130
+ }
1131
+
1132
+ if ([json objectForKey:@"instructionVariants"]) {
1133
+ [maneuver setInstructionVariants:[RCTConvert NSStringArray:json[@"instructionVariants"]]];
1134
+ }
1135
+
1136
+ return maneuver;
1137
+ }
1138
+
1139
+ - (CPTripPreviewTextConfiguration*)parseTripPreviewTextConfiguration:(NSDictionary*)json {
1140
+ return [[CPTripPreviewTextConfiguration alloc] initWithStartButtonTitle:[RCTConvert NSString:json[@"startButtonTitle"]] additionalRoutesButtonTitle:[RCTConvert NSString:json[@"additionalRoutesButtonTitle"]] overviewButtonTitle:[RCTConvert NSString:json[@"overviewButtonTitle"]]];
1141
+ }
1142
+
1143
+ - (CPTrip*)parseTrip:(NSDictionary*)config {
1144
+ if ([config objectForKey:@"config"]) {
1145
+ config = [config objectForKey:@"config"];
1146
+ }
1147
+ MKMapItem *origin = [RCTConvert MKMapItem:config[@"origin"]];
1148
+ MKMapItem *destination = [RCTConvert MKMapItem:config[@"destination"]];
1149
+ NSMutableArray *routeChoices = [NSMutableArray array];
1150
+ if ([config objectForKey:@"routeChoices"]) {
1151
+ NSInteger index = 0;
1152
+ for (NSDictionary *routeChoice in [RCTConvert NSArray:config[@"routeChoices"]]) {
1153
+ CPRouteChoice *cpRouteChoice = [RCTConvert CPRouteChoice:routeChoice];
1154
+ NSMutableDictionary *userInfo = cpRouteChoice.userInfo;
1155
+ if (!userInfo) {
1156
+ userInfo = [[NSMutableDictionary alloc] init];
1157
+ cpRouteChoice.userInfo = userInfo;
1158
+ }
1159
+ [userInfo setValue:[NSNumber numberWithInteger:index] forKey:@"index"];
1160
+ [routeChoices addObject:cpRouteChoice];
1161
+ index++;
1162
+ }
1163
+ }
1164
+ return [[CPTrip alloc] initWithOrigin:origin destination:destination routeChoices:routeChoices];
1165
+ }
1166
+
1167
+ - (CPNavigationAlert*)parseNavigationAlert:(NSDictionary*)json templateId:(NSString*)templateId {
1168
+ CPImageSet *imageSet;
1169
+ if ([json objectForKey:@"lightImage"] && [json objectForKey:@"darkImage"]) {
1170
+ imageSet = [[CPImageSet alloc] initWithLightContentImage:[RCTConvert UIImage:json[@"lightImage"]] darkContentImage:[RCTConvert UIImage:json[@"darkImage"]]];
1171
+ }
1172
+ CPAlertAction *secondaryAction = [json objectForKey:@"secondaryAction"] ? [self parseAlertAction:json[@"secondaryAction"] body:@{ @"templateId": templateId, @"secondary": @(YES) }] : nil;
1173
+
1174
+ return [[CPNavigationAlert alloc] initWithTitleVariants:[RCTConvert NSStringArray:json[@"titleVariants"]] subtitleVariants:[RCTConvert NSStringArray:json[@"subtitleVariants"]] imageSet:imageSet primaryAction:[self parseAlertAction:json[@"primaryAction"] body:@{ @"templateId": templateId, @"primary": @(YES) }] secondaryAction:secondaryAction duration:[RCTConvert double:json[@"duration"]]];
1175
+ }
1176
+
1177
+ - (CPAlertAction*)parseAlertAction:(NSDictionary*)json body:(NSDictionary*)body {
1178
+ return [[CPAlertAction alloc] initWithTitle:[RCTConvert NSString:json[@"title"]] style:(CPAlertActionStyle) [RCTConvert NSUInteger:json[@"style"]] handler:^(CPAlertAction * _Nonnull action) {
1179
+ if (self->hasListeners) {
1180
+ [self sendEventWithName:@"alertActionPressed" body:body];
1181
+ }
1182
+ }];
1183
+ }
1184
+
1185
+ - (NSArray<CPVoiceControlState*>*)parseVoiceControlStates:(NSArray<NSDictionary*>*)items {
1186
+ NSMutableArray<CPVoiceControlState*>* res = [NSMutableArray array];
1187
+ for (NSDictionary *item in items) {
1188
+ [res addObject:[self parseVoiceControlState:item]];
1189
+ }
1190
+ return res;
1191
+ }
1192
+
1193
+ - (CPVoiceControlState*)parseVoiceControlState:(NSDictionary*)json {
1194
+ return [[CPVoiceControlState alloc] initWithIdentifier:[RCTConvert NSString:json[@"identifier"]] titleVariants:[RCTConvert NSStringArray:json[@"titleVariants"]] image:[RCTConvert UIImage:json[@"image"]] repeats:[RCTConvert BOOL:json[@"repeats"]]];
1195
+ }
1196
+
1197
+ - (NSString*)panDirectionToString:(CPPanDirection)panDirection {
1198
+ switch (panDirection) {
1199
+ case CPPanDirectionUp: return @"up";
1200
+ case CPPanDirectionRight: return @"right";
1201
+ case CPPanDirectionDown: return @"down";
1202
+ case CPPanDirectionLeft: return @"left";
1203
+ case CPPanDirectionNone: return @"none";
1204
+ }
1205
+ }
1206
+
1207
+ - (NSDictionary*)navigationAlertToJson:(CPNavigationAlert*)navigationAlert dismissalContext:(CPNavigationAlertDismissalContext)dismissalContext {
1208
+ NSString *dismissalCtx = @"none";
1209
+ if (dismissalContext) {
1210
+ switch (dismissalContext) {
1211
+ case CPNavigationAlertDismissalContextTimeout:
1212
+ dismissalCtx = @"timeout";
1213
+ break;
1214
+ case CPNavigationAlertDismissalContextSystemDismissed:
1215
+ dismissalCtx = @"system";
1216
+ break;
1217
+ case CPNavigationAlertDismissalContextUserDismissed:
1218
+ dismissalCtx = @"user";
1219
+ break;
1220
+ }
1221
+ }
1222
+
1223
+ return @{
1224
+ @"todo": @(YES),
1225
+ @"reason": dismissalCtx
1226
+ };
1227
+ }
1228
+ - (NSDictionary*)navigationAlertToJson:(CPNavigationAlert*)navigationAlert {
1229
+ return @{ @"todo": @(YES) };
1230
+ // NSMutableDictionary *res = [[NSMutableDictionary alloc] init];
1231
+ // return @{
1232
+ // };
1233
+ }
1234
+
1235
+ - (void)sendTemplateEventWithName:(CPTemplate *)template name:(NSString*)name {
1236
+ [self sendTemplateEventWithName:template name:name json:@{}];
1237
+ }
1238
+
1239
+ - (void)sendTemplateEventWithName:(CPTemplate *)template name:(NSString*)name json:(NSDictionary*)json {
1240
+ NSMutableDictionary *body = [[NSMutableDictionary alloc] initWithDictionary:json];
1241
+ NSDictionary *userInfo = [template userInfo];
1242
+ if(userInfo){
1243
+ [body setObject:[userInfo objectForKey:@"templateId"] forKey:@"templateId"];
1244
+ if(hasListeners) {
1245
+ [self sendEventWithName:name body:body];
1246
+ }
1247
+ }
1248
+ }
1249
+
1250
+
1251
+ # pragma MapTemplate
1252
+
1253
+ - (void)mapTemplate:(CPMapTemplate *)mapTemplate selectedPreviewForTrip:(CPTrip *)trip usingRouteChoice:(CPRouteChoice *)routeChoice {
1254
+ NSDictionary *userInfo = trip.userInfo;
1255
+ NSString *tripId = [userInfo valueForKey:@"id"];
1256
+
1257
+ NSDictionary *routeUserInfo = routeChoice.userInfo;
1258
+ NSString *routeIndex = [routeUserInfo valueForKey:@"index"];
1259
+ [self sendTemplateEventWithName:mapTemplate name:@"selectedPreviewForTrip" json:@{ @"tripId": tripId, @"routeIndex": routeIndex}];
1260
+ }
1261
+
1262
+ - (void)mapTemplate:(CPMapTemplate *)mapTemplate startedTrip:(CPTrip *)trip usingRouteChoice:(CPRouteChoice *)routeChoice {
1263
+ NSDictionary *userInfo = trip.userInfo;
1264
+ NSString *tripId = [userInfo valueForKey:@"id"];
1265
+
1266
+ NSDictionary *routeUserInfo = routeChoice.userInfo;
1267
+ NSString *routeIndex = [routeUserInfo valueForKey:@"index"];
1268
+
1269
+ [self sendTemplateEventWithName:mapTemplate name:@"startedTrip" json:@{ @"tripId": tripId, @"routeIndex": routeIndex}];
1270
+ }
1271
+
1272
+ - (void)mapTemplateDidCancelNavigation:(CPMapTemplate *)mapTemplate {
1273
+ [self sendTemplateEventWithName:mapTemplate name:@"didCancelNavigation"];
1274
+ }
1275
+
1276
+ //- (BOOL)mapTemplate:(CPMapTemplate *)mapTemplate shouldShowNotificationForManeuver:(CPManeuver *)maneuver {
1277
+ // // @todo
1278
+ //}
1279
+ //- (BOOL)mapTemplate:(CPMapTemplate *)mapTemplate shouldUpdateNotificationForManeuver:(CPManeuver *)maneuver withTravelEstimates:(CPTravelEstimates *)travelEstimates {
1280
+ // // @todo
1281
+ //}
1282
+ //- (BOOL)mapTemplate:(CPMapTemplate *)mapTemplate shouldShowNotificationForNavigationAlert:(CPNavigationAlert *)navigationAlert {
1283
+ // // @todo
1284
+ //}
1285
+
1286
+ - (void)mapTemplate:(CPMapTemplate *)mapTemplate willShowNavigationAlert:(CPNavigationAlert *)navigationAlert {
1287
+ [self sendTemplateEventWithName:mapTemplate name:@"willShowNavigationAlert" json:[self navigationAlertToJson:navigationAlert]];
1288
+ }
1289
+ - (void)mapTemplate:(CPMapTemplate *)mapTemplate didShowNavigationAlert:(CPNavigationAlert *)navigationAlert {
1290
+ [self sendTemplateEventWithName:mapTemplate name:@"didShowNavigationAlert" json:[self navigationAlertToJson:navigationAlert]];
1291
+ }
1292
+ - (void)mapTemplate:(CPMapTemplate *)mapTemplate willDismissNavigationAlert:(CPNavigationAlert *)navigationAlert dismissalContext:(CPNavigationAlertDismissalContext)dismissalContext {
1293
+ [self sendTemplateEventWithName:mapTemplate name:@"willDismissNavigationAlert" json:[self navigationAlertToJson:navigationAlert dismissalContext:dismissalContext]];
1294
+ }
1295
+ - (void)mapTemplate:(CPMapTemplate *)mapTemplate didDismissNavigationAlert:(CPNavigationAlert *)navigationAlert dismissalContext:(CPNavigationAlertDismissalContext)dismissalContext {
1296
+ [self sendTemplateEventWithName:mapTemplate name:@"didDismissNavigationAlert" json:[self navigationAlertToJson:navigationAlert dismissalContext:dismissalContext]];
1297
+ }
1298
+
1299
+ - (void)mapTemplateDidShowPanningInterface:(CPMapTemplate *)mapTemplate {
1300
+ [self sendTemplateEventWithName:mapTemplate name:@"didShowPanningInterface"];
1301
+ }
1302
+ - (void)mapTemplateWillDismissPanningInterface:(CPMapTemplate *)mapTemplate {
1303
+ [self sendTemplateEventWithName:mapTemplate name:@"willDismissPanningInterface"];
1304
+ }
1305
+ - (void)mapTemplateDidDismissPanningInterface:(CPMapTemplate *)mapTemplate {
1306
+ [self sendTemplateEventWithName:mapTemplate name:@"didDismissPanningInterface"];
1307
+ }
1308
+ - (void)mapTemplateDidBeginPanGesture:(CPMapTemplate *)mapTemplate {
1309
+ [self sendTemplateEventWithName:mapTemplate name:@"didBeginPanGesture"];
1310
+ }
1311
+ - (void)mapTemplate:(CPMapTemplate *)mapTemplate panWithDirection:(CPPanDirection)direction {
1312
+ [self sendTemplateEventWithName:mapTemplate name:@"panWithDirection" json:@{ @"direction": [self panDirectionToString:direction] }];
1313
+ }
1314
+ - (void)mapTemplate:(CPMapTemplate *)mapTemplate panBeganWithDirection:(CPPanDirection)direction {
1315
+ [self sendTemplateEventWithName:mapTemplate name:@"panBeganWithDirection" json:@{ @"direction": [self panDirectionToString:direction] }];
1316
+ }
1317
+ - (void)mapTemplate:(CPMapTemplate *)mapTemplate panEndedWithDirection:(CPPanDirection)direction {
1318
+ [self sendTemplateEventWithName:mapTemplate name:@"panEndedWithDirection" json:@{ @"direction": [self panDirectionToString:direction] }];
1319
+ }
1320
+ - (void)mapTemplate:(CPMapTemplate *)mapTemplate didEndPanGestureWithVelocity:(CGPoint)velocity {
1321
+ [self sendTemplateEventWithName:mapTemplate name:@"didEndPanGestureWithVelocity" json:@{ @"velocity": @{ @"x": @(velocity.x), @"y": @(velocity.y) }}];
1322
+ }
1323
+ - (void)mapTemplate:(CPMapTemplate *)mapTemplate didUpdatePanGestureWithTranslation:(CGPoint)translation velocity:(CGPoint)velocity {
1324
+ [self sendTemplateEventWithName:mapTemplate name:@"didUpdatePanGestureWithTranslation" json:@{ @"translation": @{ @"x": @(translation.x), @"y": @(translation.y) }, @"velocity": @{ @"x": @(velocity.x), @"y": @(velocity.y) }}];
1325
+ }
1326
+
1327
+
1328
+
1329
+ # pragma SearchTemplate
1330
+
1331
+ - (void)searchTemplate:(CPSearchTemplate *)searchTemplate selectedResult:(CPListItem *)item completionHandler:(void (^)(void))completionHandler {
1332
+ NSNumber* index = [item.userInfo objectForKey:@"index"];
1333
+ [self sendTemplateEventWithName:searchTemplate name:@"selectedResult" json:@{ @"index": index }];
1334
+ self.selectedResultBlock = completionHandler;
1335
+ }
1336
+
1337
+ - (void)searchTemplateSearchButtonPressed:(CPSearchTemplate *)searchTemplate {
1338
+ [self sendTemplateEventWithName:searchTemplate name:@"searchButtonPressed"];
1339
+ }
1340
+
1341
+ - (void)searchTemplate:(CPSearchTemplate *)searchTemplate updatedSearchText:(NSString *)searchText completionHandler:(void (^)(NSArray<CPListItem *> * _Nonnull))completionHandler {
1342
+ [self sendTemplateEventWithName:searchTemplate name:@"updatedSearchText" json:@{ @"searchText": searchText }];
1343
+ self.searchResultBlock = completionHandler;
1344
+ }
1345
+
1346
+ # pragma ListTemplate
1347
+
1348
+ - (void)listTemplate:(CPListTemplate *)listTemplate didSelectListItem:(CPListItem *)item completionHandler:(void (^)(void))completionHandler {
1349
+ NSNumber* index = [item.userInfo objectForKey:@"index"];
1350
+ [self sendTemplateEventWithName:listTemplate name:@"didSelectListItem" json:@{ @"index": index }];
1351
+ self.selectedResultBlock = completionHandler;
1352
+ }
1353
+
1354
+ # pragma TabBarTemplate
1355
+ - (void)tabBarTemplate:(CPTabBarTemplate *)tabBarTemplate didSelectTemplate:(__kindof CPTemplate *)selectedTemplate {
1356
+ NSString* selectedTemplateId = [[selectedTemplate userInfo] objectForKey:@"templateId"];
1357
+ [self sendTemplateEventWithName:tabBarTemplate name:@"didSelectTemplate" json:@{@"selectedTemplateId":selectedTemplateId}];
1358
+ }
1359
+
1360
+ # pragma PointOfInterest
1361
+ -(void)pointOfInterestTemplate:(CPPointOfInterestTemplate *)pointOfInterestTemplate didChangeMapRegion:(MKCoordinateRegion)region {
1362
+ // noop
1363
+
1364
+ // NSDictionary *regionDictionary = @{
1365
+ // @"latitude": @(region.center.latitude),
1366
+ // @"longitude": @(region.center.longitude),
1367
+ // @"latitudeDelta": @(region.span.latitudeDelta),
1368
+ // @"longitudeDelta": @(region.span.longitudeDelta)
1369
+ // };
1370
+ //
1371
+ // [self sendTemplateEventWithName:pointOfInterestTemplate name:@"didChangeMapRegion" json:regionDictionary];
1372
+ }
1373
+
1374
+ -(void)pointOfInterestTemplate:(CPPointOfInterestTemplate *)pointOfInterestTemplate didSelectPointOfInterest:(CPPointOfInterest *)pointOfInterest {
1375
+
1376
+
1377
+ // RNCPStore *store = [RNCPStore sharedManager];
1378
+ // CPTemplateApplicationScene *templateApplicationScene = store.scene;
1379
+ //
1380
+
1381
+
1382
+ //
1383
+ // [pointOfInterest.location openInMapsWithLaunchOptions:nil fromScene:templateApplicationScene completionHandler:^(BOOL success) {
1384
+ // NSLog(@"Selected map item: %@",pointOfInterest.location);
1385
+ // }];
1386
+ //
1387
+
1388
+ }
1389
+
1390
+ - (void)pointOfInterestTemplate:(CPPointOfInterestTemplate *)poiTemplate didSelectMapItem:(MKMapItem *)mapItem {
1391
+ // Handle the selection of the map item here
1392
+ NSLog(@"Selected map item: %@", mapItem);
1393
+
1394
+ [mapItem openInMapsWithLaunchOptions:nil fromScene:poiTemplate completionHandler:^(BOOL success) {
1395
+ NSLog(@"Selected map item: %@", mapItem);
1396
+ }];
1397
+ }
1398
+
1399
+ - (CPPointOfInterestTemplate *)createCarPlayPOISCreen:(CPInterfaceController *)cpInterface responseList:(NSDictionary *)responseData templateId:(NSString *)templateId {
1400
+
1401
+ NSArray<NSDictionary*> *items = [RCTConvert NSDictionaryArray:responseData[@"items"]];
1402
+
1403
+ NSMutableArray<CPPointOfInterest *> *pois = [NSMutableArray array];
1404
+
1405
+ for (NSDictionary *item in items) {
1406
+
1407
+ double latitudeValue = item[@"latitude"] && [item[@"latitude"] isKindOfClass:[NSNumber class]] ? [item[@"latitude"] doubleValue]: 0; // Extracting double value from NSNumber
1408
+ double longitudeValue = item[@"longitude"] && [item[@"longitude"] isKindOfClass:[NSNumber class]] ? [item[@"longitude"] doubleValue] : 0; // Extracting double value from NSNumber
1409
+ [pois addObject:createPOI(cpInterface,item,latitudeValue,longitudeValue,templateId)];
1410
+ }
1411
+
1412
+ CPPointOfInterestTemplate *template = [[CPPointOfInterestTemplate alloc] initWithTitle:responseData[@"title"] pointsOfInterest:pois selectedIndex:NSNotFound];
1413
+
1414
+
1415
+
1416
+ return template;
1417
+ }
1418
+
1419
+ // Function to create a composite image with a small icon positioned to the right side and bottom of the main icon,
1420
+ // or a circular filled background with a specified color if the small icon is not present
1421
+ - (UIImage *)compositeImageWithMainIcon:(UIImage *)mainIcon smallIcon:(UIImage *)smallIcon backgroundColor:(UIColor *)backgroundColor {
1422
+ // Specify the desired width and height for the composite image
1423
+ CGFloat compositeWidth = MAX(mainIcon.size.width, smallIcon.size.width); // Use the maximum width of the two icons
1424
+ CGFloat compositeHeight = MAX(mainIcon.size.height, smallIcon.size.height); // Use the maximum height of the two icons
1425
+
1426
+ // Begin a new image context, to draw our composite image
1427
+ UIGraphicsBeginImageContextWithOptions(CGSizeMake(compositeWidth, compositeHeight), NO, 0.0);
1428
+
1429
+ // Calculate the position for drawing the main icon at the top left
1430
+ CGPoint mainIconPosition = CGPointZero;
1431
+
1432
+ if (smallIcon) {
1433
+ // If small icon is available, draw it at the right side and bottom of the main icon
1434
+ [mainIcon drawAtPoint:CGPointZero]; // Draw the main icon at the top left
1435
+
1436
+ // Calculate the position for drawing the small icon to the right side and bottom of the main icon
1437
+ CGPoint smallIconPosition = CGPointMake(mainIcon.size.width, mainIcon.size.height);
1438
+ [smallIcon drawAtPoint:smallIconPosition];
1439
+ } else {
1440
+ // If small icon is not available, create a circular filled background with the specified color
1441
+ // Draw circular filled background using the specified color
1442
+ CGContextRef context = UIGraphicsGetCurrentContext();
1443
+ CGRect circleRect = CGRectMake(mainIcon.size.width, mainIcon.size.height, compositeWidth - mainIcon.size.width, compositeHeight - mainIcon.size.height);
1444
+ CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
1445
+ CGContextFillEllipseInRect(context, circleRect);
1446
+
1447
+ // Draw the main icon at the top left
1448
+ [mainIcon drawAtPoint:CGPointZero];
1449
+ }
1450
+
1451
+ // Get the composite image from the context
1452
+ UIImage *compositeImage = UIGraphicsGetImageFromCurrentImageContext();
1453
+
1454
+ // End the context
1455
+ UIGraphicsEndImageContext();
1456
+
1457
+ // Return the composite image
1458
+ return compositeImage;
1459
+ }
1460
+
1461
+
1462
+ CPPointOfInterest *createPOI(CPInterfaceController *cpInterface,
1463
+ NSDictionary *responseObj,
1464
+ double lat,
1465
+ double lng,
1466
+ NSString *templateId) {
1467
+
1468
+ CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(lat, lng);
1469
+
1470
+ MKPlacemark *placemark =
1471
+ [[MKPlacemark alloc] initWithCoordinate:coordinate];
1472
+
1473
+ MKMapItem *location =
1474
+ [[MKMapItem alloc] initWithPlacemark:placemark];
1475
+
1476
+ // -----------------------------
1477
+ // Safe Title Handling
1478
+ // -----------------------------
1479
+ NSString *name = responseObj[@"title"];
1480
+
1481
+ name = (name &&
1482
+ ![name isKindOfClass:[NSNull class]] &&
1483
+ name.length > 0)
1484
+ ? name
1485
+ : @" ";
1486
+
1487
+ NSLog(@"UNIT ID ===> %@", name);
1488
+
1489
+ // -----------------------------
1490
+ // Images
1491
+ // -----------------------------
1492
+ UIImage *selectedImage =
1493
+ [[RNCarPlay alloc] imageFromBase64String:responseObj[@"imageUri"]];
1494
+
1495
+ selectedImage =
1496
+ (selectedImage &&
1497
+ ![selectedImage isKindOfClass:[NSNull class]])
1498
+ ? [[RNCarPlay alloc] imageWithSize:selectedImage
1499
+ convertToSize:CGSizeMake(100, 100)]
1500
+ : nil;
1501
+
1502
+ UIImage *pinImage =
1503
+ (selectedImage &&
1504
+ ![selectedImage isKindOfClass:[NSNull class]])
1505
+ ? [[RNCarPlay alloc] imageWithSize:selectedImage
1506
+ convertToSize:CGSizeMake(25, 25)]
1507
+ : nil;
1508
+
1509
+ // IMPORTANT:
1510
+ // Prevent CarPlay iOS 26 tint/template rendering bug
1511
+ if (pinImage) {
1512
+ pinImage =
1513
+ [pinImage imageWithRenderingMode:
1514
+ UIImageRenderingModeAlwaysOriginal];
1515
+ }
1516
+
1517
+ if (selectedImage) {
1518
+ selectedImage =
1519
+ [selectedImage imageWithRenderingMode:
1520
+ UIImageRenderingModeAlwaysOriginal];
1521
+ }
1522
+
1523
+ // -----------------------------
1524
+ // Safe Text Handling
1525
+ // Use nil instead of @""
1526
+ // -----------------------------
1527
+ NSString *subtitle = responseObj[@"subtitle"];
1528
+
1529
+ subtitle = (subtitle &&
1530
+ ![subtitle isKindOfClass:[NSNull class]] &&
1531
+ subtitle.length > 0)
1532
+ ? subtitle
1533
+ : nil;
1534
+
1535
+ NSString *summary = responseObj[@"summary"];
1536
+
1537
+ summary = (summary &&
1538
+ ![summary isKindOfClass:[NSNull class]] &&
1539
+ summary.length > 0)
1540
+ ? summary
1541
+ : nil;
1542
+
1543
+ NSString *detailSubtitle =
1544
+ responseObj[@"detailSubtitle"];
1545
+
1546
+ detailSubtitle = (detailSubtitle &&
1547
+ ![detailSubtitle isKindOfClass:[NSNull class]] &&
1548
+ detailSubtitle.length > 0)
1549
+ ? detailSubtitle
1550
+ : nil;
1551
+
1552
+ NSString *detailSummary =
1553
+ responseObj[@"detailSummary"];
1554
+
1555
+ detailSummary = (detailSummary &&
1556
+ ![detailSummary isKindOfClass:[NSNull class]] &&
1557
+ detailSummary.length > 0)
1558
+ ? detailSummary
1559
+ : nil;
1560
+
1561
+ // -----------------------------
1562
+ // Create POI
1563
+ // -----------------------------
1564
+ CPPointOfInterest *poi;
1565
+
1566
+ if (@available(iOS 16.0, *)) {
1567
+
1568
+ poi = [[CPPointOfInterest alloc]
1569
+ initWithLocation:location
1570
+ title:name
1571
+ subtitle:subtitle
1572
+ summary:summary
1573
+ detailTitle:name
1574
+ detailSubtitle:detailSubtitle
1575
+ detailSummary:detailSummary
1576
+ pinImage:pinImage
1577
+ selectedPinImage:selectedImage];
1578
+
1579
+ } else {
1580
+
1581
+ poi = [[CPPointOfInterest alloc]
1582
+ initWithLocation:location
1583
+ title:name
1584
+ subtitle:subtitle
1585
+ summary:summary
1586
+ detailTitle:name
1587
+ detailSubtitle:detailSubtitle
1588
+ detailSummary:detailSummary
1589
+ pinImage:pinImage];
1590
+ }
1591
+
1592
+ // -----------------------------
1593
+ // First Button
1594
+ // -----------------------------
1595
+ NSDictionary *firstButtonActionInfo =
1596
+ responseObj[@"firstButtonActionInfo"];
1597
+
1598
+ BOOL isVisibleForFirstBtn =
1599
+ [firstButtonActionInfo[@"isVisible"] boolValue];
1600
+
1601
+ if (isVisibleForFirstBtn) {
1602
+
1603
+ poi.primaryButton =
1604
+ [[CPTextButton alloc]
1605
+ initWithTitle:firstButtonActionInfo[@"title"]
1606
+ textStyle:CPTextButtonStyleNormal
1607
+ handler:^(CPTextButton * _Nonnull button) {
1608
+
1609
+ [[RNCarPlay new]
1610
+ sendEventWithName:@"actionButtonPressed"
1611
+ body:@{
1612
+ @"templateId": templateId,
1613
+ @"id": firstButtonActionInfo[@"actionType"],
1614
+ @"responseObj": responseObj,
1615
+ @"latitude": @(lat),
1616
+ @"longitude": @(lng)
1617
+ }];
1618
+ }];
1619
+ }
1620
+
1621
+ // -----------------------------
1622
+ // Second Button
1623
+ // -----------------------------
1624
+ NSDictionary *secondButtonActionInfo =
1625
+ responseObj[@"secondButtonActionInfo"];
1626
+
1627
+ BOOL isVisibleForSecondBtn =
1628
+ [secondButtonActionInfo[@"isVisible"] boolValue];
1629
+
1630
+ if (isVisibleForSecondBtn) {
1631
+
1632
+ poi.secondaryButton =
1633
+ [[CPTextButton alloc]
1634
+ initWithTitle:secondButtonActionInfo[@"title"]
1635
+ textStyle:CPTextButtonStyleNormal
1636
+ handler:^(CPTextButton * _Nonnull button) {
1637
+
1638
+ NSString *mapsURLString =
1639
+ [NSString stringWithFormat:
1640
+ @"http://maps.apple.com/?ll=%f,%f",
1641
+ lat,
1642
+ lng];
1643
+
1644
+ NSLog(@"Maps URL: %@", mapsURLString);
1645
+
1646
+ [[RNCarPlay new]
1647
+ sendEventWithName:@"actionButtonPressed"
1648
+ body:@{
1649
+ @"templateId": templateId,
1650
+ @"id": secondButtonActionInfo[@"actionType"],
1651
+ @"responseObj": responseObj,
1652
+ @"latitude": @(lat),
1653
+ @"longitude": @(lng)
1654
+ }];
1655
+ }];
1656
+ }
1657
+
1658
+ return poi;
1659
+ }
1660
+
1661
+
1662
+
1663
+ // This method kept for reference
1664
+ CPPointOfInterest *createPOI2( CPInterfaceController *cpInterface, NSDictionary * responseObj, double lat, double lng, NSString * templateId) {
1665
+
1666
+ CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(lat, lng);
1667
+ MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinate];
1668
+ MKMapItem *location = [[MKMapItem alloc] initWithPlacemark:placemark];
1669
+ NSString * name = responseObj[@"title"];
1670
+
1671
+
1672
+ NSLog(@"UNIT ID ===> %@",name);
1673
+
1674
+
1675
+ // Assuming you have the image "MapMarker" in your bundle
1676
+ // UIImage *image = [RCTConvert UIImage:responseObj[@"icon"]];
1677
+
1678
+
1679
+
1680
+ UIImage * selectedImage = [[RNCarPlay alloc] imageFromBase64String:responseObj[@"imageUri"]];
1681
+
1682
+ selectedImage = (selectedImage && ![selectedImage isKindOfClass:[NSNull class]]) ? [[RNCarPlay alloc] imageWithSize:selectedImage convertToSize:CGSizeMake(100, 100)] : nil;
1683
+
1684
+ UIImage * pinImage = (selectedImage && ![selectedImage isKindOfClass:[NSNull class]]) ? [[RNCarPlay alloc] imageWithSize:selectedImage convertToSize:CGSizeMake(25, 25)] : nil;
1685
+
1686
+ NSString *subtitle = responseObj[@"subtitle"];
1687
+ subtitle = (subtitle && subtitle != [NSNull null]) ? subtitle : @"";
1688
+
1689
+ NSString *summary = responseObj[@"summary"];
1690
+ summary = (summary && summary != [NSNull null]) ? summary : @"";
1691
+
1692
+ NSString *detailSubtitle = responseObj[@"detailSubtitle"];
1693
+ detailSubtitle = (detailSubtitle && detailSubtitle != [NSNull null]) ? detailSubtitle : @"";
1694
+
1695
+ NSString *detailSummary = responseObj[@"detailSummary"];
1696
+ detailSummary = (detailSummary && detailSummary != [NSNull null]) ? detailSummary : @"";
1697
+
1698
+
1699
+ CPPointOfInterest *poi;
1700
+
1701
+ if (@available(iOS 16.0, *)) {
1702
+ poi = [[CPPointOfInterest alloc] initWithLocation:location
1703
+ title:name
1704
+ subtitle:subtitle
1705
+ summary:summary
1706
+ detailTitle:name
1707
+ detailSubtitle:detailSubtitle
1708
+ detailSummary:detailSummary
1709
+ pinImage:pinImage
1710
+ selectedPinImage:selectedImage];
1711
+ }
1712
+ else{
1713
+ poi = [[CPPointOfInterest alloc] initWithLocation:location
1714
+ title:name
1715
+ subtitle:subtitle
1716
+ summary:summary
1717
+ detailTitle:name
1718
+ detailSubtitle:detailSubtitle
1719
+ detailSummary:detailSummary
1720
+ pinImage:pinImage];
1721
+ }
1722
+
1723
+ NSDictionary *firstButtonActionInfo = responseObj[@"firstButtonActionInfo"];
1724
+ NSNumber *isVisibleNumber = firstButtonActionInfo[@"isVisible"];
1725
+ BOOL isVisibleForFirstBtn = [isVisibleNumber boolValue];
1726
+
1727
+ if(isVisibleForFirstBtn){
1728
+ poi.primaryButton = [[CPTextButton alloc] initWithTitle:firstButtonActionInfo[@"title"] textStyle:CPTextButtonStyleNormal handler:^(CPTextButton * _Nonnull button) {
1729
+ // [cpInterface pushTemplate:createCarParkDetailScreen(cpInterface, responseObj[@"eventDetail"])
1730
+ // animated:true completion:nil];
1731
+
1732
+ [[RNCarPlay new] sendEventWithName:@"actionButtonPressed"
1733
+ body:@{@"templateId": templateId,
1734
+ @"id": firstButtonActionInfo[@"actionType"],
1735
+ @"responseObj":responseObj,
1736
+ @"latitude": @(lat),
1737
+ @"longitude": @(lng)}];
1738
+ }];
1739
+ }
1740
+
1741
+ NSDictionary *secondButtonActionInfo = responseObj[@"secondButtonActionInfo"];
1742
+ BOOL isVisibleForSecondBtn= [secondButtonActionInfo[@"isVisible"] boolValue];
1743
+
1744
+ if(isVisibleForSecondBtn){
1745
+ poi.secondaryButton = [[CPTextButton alloc] initWithTitle:responseObj[@"secondButtonActionInfo"][@"title"] textStyle:CPTextButtonStyleNormal handler:^(CPTextButton * _Nonnull button) {
1746
+
1747
+ NSString *mapsURLString = [NSString stringWithFormat:@"http://maps.apple.com/?ll=%f,%f", lat, lng];
1748
+
1749
+ //[[RNCarPlay new] openUrl:mapsURLString];
1750
+
1751
+ [[RNCarPlay new] sendEventWithName:@"actionButtonPressed"
1752
+ body:@{@"templateId": templateId,
1753
+ @"id": responseObj[@"secondButtonActionInfo"][@"actionType"],
1754
+ @"responseObj":responseObj,
1755
+ @"latitude": @(lat),
1756
+ @"longitude": @(lng)}];
1757
+ }];
1758
+ }
1759
+
1760
+ return poi;
1761
+ }
1762
+
1763
+ + (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
1764
+ //UIGraphicsBeginImageContext(newSize);
1765
+ // In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
1766
+ // Pass 1.0 to force exact pixel size.
1767
+ UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
1768
+ [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
1769
+ UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
1770
+ UIGraphicsEndImageContext();
1771
+ return newImage;
1772
+ }
1773
+
1774
+ // Function to convert Base64 encoded string to UIImage
1775
+ - (UIImage *)imageFromBase64String:(NSString *)base64String {
1776
+ // Check if the string is not empty
1777
+ if (base64String && ![base64String isEqualToString:@""]) {
1778
+ // Convert the Base64 encoded string to NSData
1779
+ NSData *imageData = [[NSData alloc] initWithBase64EncodedString:base64String options:NSDataBase64DecodingIgnoreUnknownCharacters];
1780
+
1781
+ // Initialize UIImage with the NSData
1782
+ UIImage *image = [UIImage imageWithData:imageData];
1783
+
1784
+ return image;
1785
+ } else {
1786
+ // Return nil if the string is empty
1787
+ return nil;
1788
+ }
1789
+ }
1790
+
1791
+
1792
+ CPTemplate *createCarParkDetailScreen(CPInterfaceController *cpInterface, NSArray * eventDetailArr) {
1793
+
1794
+ NSMutableArray<CPInformationItem *> *lines = [NSMutableArray array];
1795
+
1796
+ for (NSDictionary *item in eventDetailArr) {
1797
+ [lines addObject:[[CPInformationItem alloc] initWithTitle:item[@"title"] detail:item[@"detail"]]];
1798
+ }
1799
+
1800
+ NSMutableArray<CPTextButton *> *actions = [NSMutableArray array];
1801
+
1802
+ [actions addObject:[[CPTextButton alloc] initWithTitle:@"Done" textStyle:CPTextButtonStyleConfirm handler:^(CPTextButton * _Nonnull button) {
1803
+ [cpInterface popTemplateAnimated:YES completion:nil];
1804
+ }]];
1805
+
1806
+ CPInformationTemplate *template = [[CPInformationTemplate alloc] initWithTitle:@"Details" layout:CPInformationTemplateLayoutTwoColumn items:lines actions:actions];
1807
+
1808
+ return template;
1809
+ }
1810
+
1811
+ # pragma InterfaceController
1812
+
1813
+ - (void)templateDidAppear:(CPTemplate *)aTemplate animated:(BOOL)animated {
1814
+ [self sendTemplateEventWithName:aTemplate name:@"didAppear" json:@{ @"animated": @(animated) }];
1815
+ }
1816
+
1817
+ - (void)templateDidDisappear:(CPTemplate *)aTemplate animated:(BOOL)animated {
1818
+ [self sendTemplateEventWithName:aTemplate name:@"didDisappear" json:@{ @"animated": @(animated) }];
1819
+ }
1820
+
1821
+ - (void)templateWillAppear:(CPTemplate *)aTemplate animated:(BOOL)animated {
1822
+ [self sendTemplateEventWithName:aTemplate name:@"willAppear" json:@{ @"animated": @(animated) }];
1823
+ }
1824
+
1825
+ - (void)templateWillDisappear:(CPTemplate *)aTemplate animated:(BOOL)animated {
1826
+ [self sendTemplateEventWithName:aTemplate name:@"willDisappear" json:@{ @"animated": @(animated) }];
1827
+ }
1828
+
1829
+ # pragma NowPlaying
1830
+
1831
+ - (void)nowPlayingTemplateUpNextButtonTapped:(CPNowPlayingTemplate *)nowPlayingTemplate {
1832
+ [self sendTemplateEventWithName:nowPlayingTemplate name:@"upNextButtonPressed"];
1833
+ }
1834
+
1835
+ - (void)nowPlayingTemplateAlbumArtistButtonTapped:(CPNowPlayingTemplate *)nowPlayingTemplate {
1836
+ [self sendTemplateEventWithName:nowPlayingTemplate name:@"albumArtistButtonPressed"];
1837
+ }
1838
+
1839
+ @end