Skip to content

Commit

Permalink
feat: carplay ios file
Browse files Browse the repository at this point in the history
  • Loading branch information
birkir committed Oct 16, 2020
1 parent 1874ba7 commit 4ddc598
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 29 deletions.
2 changes: 2 additions & 0 deletions ios/RCTConvert+RNCarPlay.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@
+ (CPMapButton*)CPMapButton:(id)json withHandler:(void (^)(CPMapButton * _Nonnull mapButton))handler;
+ (CPRouteChoice*)CPRouteChoice:(id)json;
+ (MKMapItem*)MKMapItem:(id)json;
+ (CPPointOfInterest*)CPPointOfInterest:(id)json;
+ (CPAlertActionStyle)CPAlertActionStyle:(id)json;
@end
24 changes: 23 additions & 1 deletion ios/RCTConvert+RNCarPlay.m
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ + (CPMapButton*)CPMapButton:(id)json withHandler:(void (^)(CPMapButton * _Nonnul
if ([json objectForKey:@"hidden"]) {
[mapButton setHidden:[RCTConvert BOOL:json[@"hidden"]]];
}

return mapButton;
}

Expand All @@ -59,4 +59,26 @@ + (CPRouteChoice*)CPRouteChoice:(id)json {
return [[CPRouteChoice alloc] initWithSummaryVariants:[RCTConvert NSStringArray:json[@"additionalInformationVariants"]] additionalInformationVariants:[RCTConvert NSStringArray:json[@"selectionSummaryVariants"]] selectionSummaryVariants:[RCTConvert NSStringArray:json[@"summaryVariants"]]];
}

+ (CPPointOfInterest*)CPPointOfInterest:(id)json {
MKMapItem *location = [RCTConvert MKMapItem:json[@"location"]];
NSString *title = [RCTConvert NSString:json[@"title"]];
NSString *subtitle = [RCTConvert NSString:json[@"subtitle"]];
NSString *summary = [RCTConvert NSString:json[@"summary"]];
NSString *detailTitle = [RCTConvert NSString:json[@"detailTitle"]];
NSString *detailSubtitle = [RCTConvert NSString:json[@"detailSubtitle"]];
NSString *detailSummary = [RCTConvert NSString:json[@"detailSummary"]];

CPPointOfInterest *poi = [[CPPointOfInterest alloc] initWithLocation:location title:title subtitle:subtitle summary:summary detailTitle:detailTitle detailSubtitle:detailSubtitle detailSummary:detailSummary pinImage:nil];
return poi;
}

+ (CPAlertActionStyle)CPAlertActionStyle:(NSString*) json {
if ([json isEqualToString:@"cancel"]) {
return CPAlertActionStyleCancel;
} else if ([json isEqualToString:@"destructive"]) {
return CPAlertActionStyleDestructive;
}
return CPAlertActionStyleDefault;
}

@end
2 changes: 1 addition & 1 deletion ios/RNCarPlay.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
typedef void(^SearchResultUpdateBlock)(NSArray<CPListItem *> * _Nonnull);
typedef void(^SelectedResultBlock)(void);

@interface RNCarPlay : RCTEventEmitter<RCTBridgeModule, CPInterfaceControllerDelegate, CPSearchTemplateDelegate, CPListTemplateDelegate, CPMapTemplateDelegate> {
@interface RNCarPlay : RCTEventEmitter<RCTBridgeModule, CPInterfaceControllerDelegate, CPSearchTemplateDelegate, CPListTemplateDelegate, CPMapTemplateDelegate, CPTabBarTemplateDelegate, CPPointOfInterestTemplateDelegate> {
CPInterfaceController *interfaceController;
CPWindow *window;
SearchResultUpdateBlock searchResultBlock;
Expand Down
207 changes: 180 additions & 27 deletions ios/RNCarPlay.m
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,16 @@ + (id)allocWithZone:(NSZone *)zone {
@"willDisappear",
// grid
@"gridButtonPressed",
// information
@"actionButtonPressed",
// list
@"didSelectListItem",
// search
@"updatedSearchText",
@"searchButtonPressed",
@"selectedResult",
// tabbar
@"didSelectTemplate",
// map
@"mapButtonPressed",
@"didUpdatePanGestureWithTranslation",
Expand Down Expand Up @@ -97,6 +101,11 @@ - (dispatch_queue_t)methodQueue
NSArray *leadingNavigationBarButtons = [self parseBarButtons:[RCTConvert NSArray:config[@"leadingNavigationBarButtons"]] templateId:templateId];
NSArray *trailingNavigationBarButtons = [self parseBarButtons:[RCTConvert NSArray:config[@"trailingNavigationBarButtons"]] templateId:templateId];

NSLog(@"id: %@", templateId);
NSLog(@"type: %@", type);
NSLog(@"title %@", title);
NSLog(@"config %@", config);

CPTemplate *template = [[CPTemplate alloc] init];

if ([type isEqualToString:@"search"]) {
Expand Down Expand Up @@ -128,34 +137,115 @@ - (dispatch_queue_t)methodQueue
mapTemplate.mapDelegate = self;

template = mapTemplate;
} else if ([type isEqualToString:@"voice"]) {
} else if ([type isEqualToString:@"voicecontrol"]) {
CPVoiceControlTemplate *voiceTemplate = [[CPVoiceControlTemplate alloc] initWithVoiceControlStates: [self parseVoiceControlStates:config[@"voiceControlStates"]]];
template = voiceTemplate;
} else if ([type isEqualToString:@"nowplaying"]) {
NSCoder *myCoder = [[NSCoder alloc] init];
CPNowPlayingTemplate *nowPlayingTemplate = [[CPNowPlayingTemplate alloc] initWithCoder:myCoder];

[nowPlayingTemplate setAlbumArtistButtonEnabled:YES];
[nowPlayingTemplate setUpNextTitle:@""];
[nowPlayingTemplate setUpNextButtonEnabled:YES];
// [nowPlayingTemplate updateNowPlayingButtons:@{}];

template = nowPlayingTemplate;
if (@available(iOS 14.0, *)) {
CPNowPlayingTemplate *nowPlayingTemplate = [CPNowPlayingTemplate sharedTemplate];
[nowPlayingTemplate setAlbumArtistButtonEnabled:[RCTConvert BOOL:config[@"albumArtistButton"]]];
[nowPlayingTemplate setUpNextTitle:[RCTConvert NSString:config[@"upNextTitle"]]];
[nowPlayingTemplate setUpNextButtonEnabled:[RCTConvert BOOL:config[@"upNextButton"]]];
template = nowPlayingTemplate;
}
} else if ([type isEqualToString:@"tabbar"]) {

NSArray<CPTemplate*> *arr = [[NSArray alloc] init];

[arr arrayByAddingObject:[store findTemplateById:@"abc"]];

CPTabBarTemplate *tabBarTemplate = [[CPTabBarTemplate alloc] initWithTemplates:arr];
template = tabBarTemplate;
if (@available(iOS 14.0, *)) {
CPTabBarTemplate *tabBarTemplate = [[CPTabBarTemplate alloc] initWithTemplates:[self parseTemplatesFrom:config]];
tabBarTemplate.delegate = self;
template = tabBarTemplate;
}
} else if ([type isEqualToString:@"contact"]) {
if (@available(iOS 14.0, *)) {
CPContact *contact = [[CPContact alloc] init];
[contact setName:config[@"name"]];
[contact setSubtitle:config[@"subtitle"]];
[contact setActions:[self parseButtons:config[@"actions"] templateId:templateId]];
CPContactTemplate *contactTemplate = [[CPContactTemplate alloc] initWithContact:contact];
template = contactTemplate;
}
} else if ([type isEqualToString:@"actionsheet"]) {
NSString *title = [RCTConvert NSString:config[@"title"]];
NSString *message = [RCTConvert NSString:config[@"message"]];
NSMutableArray<CPAlertAction *> *actions = [NSMutableArray new];
NSArray<NSDictionary*> *_actions = [RCTConvert NSDictionaryArray:config[@"actions"]];
for (NSDictionary *_action in _actions) {
CPAlertAction *action = [[CPAlertAction alloc] initWithTitle:[RCTConvert NSString:_action[@"title"]] style:[RCTConvert CPAlertActionStyle:_action[@"style"]] handler:^(CPAlertAction *a) {
[self sendEventWithName:@"actionButtonPressed" body:@{@"templateId":templateId, @"id": _action[@"id"] }];
}];
[actions addObject:action];
}
CPActionSheetTemplate *actionSheetTemplate = [[CPActionSheetTemplate alloc] initWithTitle:title message:message actions:actions];
template = actionSheetTemplate;
} else if ([type isEqualToString:@"alert"]) {
NSMutableArray<CPAlertAction *> *actions = [NSMutableArray new];
NSArray<NSDictionary*> *_actions = [RCTConvert NSDictionaryArray:config[@"actions"]];
for (NSDictionary *_action in _actions) {
CPAlertAction *action = [[CPAlertAction alloc] initWithTitle:[RCTConvert NSString:_action[@"title"]] style:[RCTConvert CPAlertActionStyle:_action[@"style"]] handler:^(CPAlertAction *a) {
[self sendEventWithName:@"actionButtonPressed" body:@{@"templateId":templateId, @"id": _action[@"id"] }];
}];
[actions addObject:action];
}
NSArray<NSString*>* titleVariants = [RCTConvert NSArray:config[@"titleVariants"]];
CPAlertTemplate *alertTemplate = [[CPAlertTemplate alloc] initWithTitleVariants:titleVariants actions:actions];
template = alertTemplate;
} else if ([type isEqualToString:@"poi"]) {
if (@available(iOS 14.0, *)) {
NSString *title = [RCTConvert NSString:config[@"title"]];
NSMutableArray<__kindof CPPointOfInterest *> * items = [NSMutableArray new];
NSUInteger selectedIndex = 0;

NSArray<NSDictionary*> *_items = [RCTConvert NSDictionaryArray:config[@"items"]];
for (NSDictionary *_item in _items) {
CPPointOfInterest *poi = [RCTConvert CPPointOfInterest:_item];
[poi setUserInfo:_item];
[items addObject:poi];
}

CPPointOfInterestTemplate *poiTemplate = [[CPPointOfInterestTemplate alloc] initWithTitle:title pointsOfInterest:items selectedIndex:selectedIndex];
poiTemplate.pointOfInterestDelegate = self;
template = poiTemplate;
}
} else if ([type isEqualToString:@"information"]) {
if (@available(iOS 14.0, *)) {
NSString *title = [RCTConvert NSString:config[@"title"]];
CPInformationTemplateLayout layout = [RCTConvert BOOL:config[@"leading"]] ? CPInformationTemplateLayoutLeading : CPInformationTemplateLayoutTwoColumn;
NSMutableArray<__kindof CPInformationItem *> * items = [NSMutableArray new];
NSMutableArray<__kindof CPTextButton *> * actions = [NSMutableArray new];

NSArray<NSDictionary*> *_items = [RCTConvert NSDictionaryArray:config[@"items"]];
for (NSDictionary *_item in _items) {
[items addObject:[[CPInformationItem alloc] initWithTitle:_item[@"title"] detail:_item[@"detail"]]];
}

NSArray<NSDictionary*> *_actions = [RCTConvert NSDictionaryArray:config[@"actions"]];
for (NSDictionary *_action in _actions) {
CPTextButton *action = [[CPTextButton alloc] initWithTitle:_action[@"title"] textStyle:CPTextButtonStyleNormal handler:^(__kindof CPTextButton * _Nonnull contactButton) {
[self sendEventWithName:@"actionButtonPressed" body:@{@"templateId":templateId, @"id": _action[@"id"] }];
}];
[actions addObject:action];
}

CPInformationTemplate *informationTemplate = [[CPInformationTemplate alloc] initWithTitle:title layout:layout items:items actions:actions];
template = informationTemplate;
}
}

[template setUserInfo:@{ @"templateId": templateId }];

[store setTemplate:templateId template:template];
}

RCT_EXPORT_METHOD(updateTemplates:(NSString*)templateId config:(NSDictionary*)config) {
if (@available(iOS 14.0, *)) {
RNCPStore *store = [RNCPStore sharedManager];
CPTemplate *template = [store findTemplateById:templateId];
if (template) {
CPTabBarTemplate *tabBarTemplate = (CPTabBarTemplate*) template;
[tabBarTemplate updateTemplates:[self parseTemplatesFrom:config]];
}
}
}

RCT_EXPORT_METHOD(createTrip:(NSString*)tripId config:(NSDictionary*)config) {
RNCPStore *store = [RNCPStore sharedManager];
CPTrip *trip = [self parseTrip:config];
Expand Down Expand Up @@ -257,7 +347,14 @@ - (dispatch_queue_t)methodQueue
RNCPStore *store = [RNCPStore sharedManager];
CPTemplate *template = [store findTemplateById:templateId];
if (template) {
[store.interfaceController pushTemplate:template animated:animated];
if (@available(iOS 14.0, *)) {
[store.interfaceController pushTemplate:template animated:animated completion:^(BOOL done, NSError * _Nullable err) {
NSLog(@"error %@", err);
// noop
}];
} else {
[store.interfaceController pushTemplate:template animated:animated];
}
} else {
NSLog(@"Failed to find template %@", template);
}
Expand Down Expand Up @@ -407,11 +504,11 @@ - (void) applyConfigForMapTemplate:(CPMapTemplate*)mapTemplate templateId:(NSStr
if ([config objectForKey:@"guidanceBackgroundColor"]) {
[mapTemplate setGuidanceBackgroundColor:[RCTConvert UIColor:config[@"guidanceBackgroundColor"]]];
}

if ([config objectForKey:@"tripEstimateStyle"]) {
[mapTemplate setTripEstimateStyle:[RCTConvert CPTripEstimateStyle:config[@"tripEstimateStyle"]]];
}

if ([config objectForKey:@"mapButtons"]) {
NSArray *mapButtons = [RCTConvert NSArray:config[@"mapButtons"]];
NSMutableArray *result = [NSMutableArray array];
Expand All @@ -427,7 +524,7 @@ - (void) applyConfigForMapTemplate:(CPMapTemplate*)mapTemplate templateId:(NSStr
if ([config objectForKey:@"automaticallyHidesNavigationBar"]) {
[mapTemplate setAutomaticallyHidesNavigationBar:[RCTConvert BOOL:config[@"automaticallyHidesNavigationBar"]]];
}

if ([config objectForKey:@"hidesButtonsWithNavigationBar"]) {
[mapTemplate setHidesButtonsWithNavigationBar:[RCTConvert BOOL:config[@"hidesButtonsWithNavigationBar"]]];
}
Expand All @@ -440,6 +537,47 @@ - (void) applyConfigForMapTemplate:(CPMapTemplate*)mapTemplate templateId:(NSStr
}
}

- (NSArray<__kindof CPTemplate*>*) parseTemplatesFrom:(NSDictionary*)config {
RNCPStore *store = [RNCPStore sharedManager];
NSMutableArray<__kindof CPTemplate*> *templates = [NSMutableArray new];
NSArray<NSDictionary*> *tpls = [RCTConvert NSDictionaryArray:config[@"templates"]];
for (NSDictionary *tpl in tpls) {
CPTemplate *templ = [store findTemplateById:tpl[@"id"]];
// @todo UITabSystemItem
[templates addObject:templ];
}
return templates;
}

- (NSArray<CPButton*>*) parseButtons:(NSArray*)buttons templateId:(NSString *)templateId API_AVAILABLE(ios(14.0)){
NSMutableArray *result = [NSMutableArray array];
for (NSDictionary *button in buttons) {
CPButton *_button;
NSString *_id = [button objectForKey:@"id"];
NSString *type = [button objectForKey:@"type"];
if ([type isEqualToString:@"call"]) {
_button = [[CPContactCallButton alloc] initWithHandler:^(__kindof CPButton * _Nonnull contactButton) {
[self sendEventWithName:@"buttonPressed" body:@{@"id": _id, @"templateId":templateId}];
}];
} else if ([type isEqualToString:@"message"]) {
_button = [[CPContactMessageButton alloc] initWithPhoneOrEmail:[button objectForKey:@"phoneOrEmail"]];
} else if ([type isEqualToString:@"directions"]) {
_button = [[CPContactDirectionsButton alloc] initWithHandler:^(__kindof CPButton * _Nonnull contactButton) {
[self sendEventWithName:@"buttonPressed" body:@{@"id": _id, @"templateId":templateId}];
}];
}

BOOL _disabled = [button objectForKey:@"disabled"];
[_button setEnabled:!_disabled];

NSString *_title = [button objectForKey:@"title"];
[_button setTitle:_title];

[result addObject:_button];
}
return result;
}

- (NSArray<CPBarButton*>*) parseBarButtons:(NSArray*)barButtons templateId:(NSString *)templateId {
NSMutableArray *result = [NSMutableArray array];
for (NSDictionary *barButton in barButtons) {
Expand Down Expand Up @@ -527,25 +665,25 @@ - (CPTravelEstimates*)parseTravelEstimates: (NSDictionary*)json {

- (CPManeuver*)parseManeuver:(NSDictionary*)json {
CPManeuver* maneuver = [[CPManeuver alloc] init];

if ([json objectForKey:@"junctionImage"]) {
[maneuver setJunctionImage:[RCTConvert UIImage:json[@"junctionImage"]]];
}

if ([json objectForKey:@"initialTravelEstimates"]) {
CPTravelEstimates* travelEstimates = [self parseTravelEstimates:json[@"initialTravelEstimates"]];
[maneuver setInitialTravelEstimates:travelEstimates];
}

if ([json objectForKey:@"symbolLight"] && [json objectForKey:@"symbolDark"]) {
CPImageSet *symbolSet = [[CPImageSet alloc] initWithLightContentImage:[RCTConvert UIImage:json[@"symbolLight"]] darkContentImage:[RCTConvert UIImage:json[@"symbolDark"]]];
[maneuver setSymbolSet:symbolSet];
}

if ([json objectForKey:@"instructionVariants"]) {
[maneuver setInstructionVariants:[RCTConvert NSStringArray:json[@"instructionVariants"]]];
}

return maneuver;
}

Expand Down Expand Up @@ -624,7 +762,7 @@ - (NSDictionary*)navigationAlertToJson:(CPNavigationAlert*)navigationAlert dismi
break;
}
}

return @{
@"todo": @(YES),
@"reason": dismissalCtx
Expand Down Expand Up @@ -739,6 +877,21 @@ - (void)listTemplate:(CPListTemplate *)listTemplate didSelectListItem:(CPListIte
self.selectedResultBlock = completionHandler;
}

# pragma TabBarTemplate
- (void)tabBarTemplate:(CPTabBarTemplate *)tabBarTemplate didSelectTemplate:(__kindof CPTemplate *)selectedTemplate API_AVAILABLE(ios(14.0)){
NSString* selectedTemplateId = [[selectedTemplate userInfo] objectForKey:@"templateId"];
[self sendTemplateEventWithName:tabBarTemplate name:@"didSelectTemplate" json:@{@"selectedTemplateId":selectedTemplateId}];
}

# pragma PointOfInterest
-(void)pointOfInterestTemplate:(CPPointOfInterestTemplate *)pointOfInterestTemplate didChangeMapRegion:(MKCoordinateRegion)region API_AVAILABLE(ios(14.0)){
// noop
}

-(void)pointOfInterestTemplate:(CPPointOfInterestTemplate *)pointOfInterestTemplate didSelectPointOfInterest:(CPPointOfInterest *)pointOfInterest API_AVAILABLE(ios(14.0)){
[self sendTemplateEventWithName:pointOfInterestTemplate name:@"didSelectPointOfInterest" json:[pointOfInterest userInfo]];
}

# pragma InterfaceController

- (void)templateDidAppear:(CPTemplate *)aTemplate animated:(BOOL)animated {
Expand Down

0 comments on commit 4ddc598

Please sign in to comment.