Skip to content

Commit

Permalink
Merge branch 'master' into feature/nowplaying-image-button
Browse files Browse the repository at this point in the history
  • Loading branch information
birkir authored Jun 12, 2024
2 parents 06f47d6 + 923f96b commit ed383a9
Show file tree
Hide file tree
Showing 11 changed files with 307 additions and 55 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ npx pod-install

# Run the iOS or Android app
yarn run ios|android

# Run the Metro server
yarn run start
```

## Declarative vs. Imperative
Expand Down
1 change: 1 addition & 0 deletions apps/example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ if (Platform.OS === 'android') {
AppRegistry.registerComponent('RNCarPlayScene', () => AndroidAuto);
// AppRegistry.registerRunnable('androidAuto', AndroidAutoModule);
} else {
AppRegistry.registerComponent('Example', () => App);
AppRegistry.registerComponent('RNCarPlayScene', () => App);
}
2 changes: 1 addition & 1 deletion packages/react-native-carplay/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:@"focusedImage"]) {
[mapButton setImage:[RCTConvert UIImage:json[@"focusedImage"]]];
[mapButton setFocusedImage:[RCTConvert UIImage:json[@"focusedImage"]]];
}

if ([json objectForKey:@"disabled"]) {
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-carplay/ios/RNCarPlay.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ typedef void(^SelectedResultBlock)(void);

+ (void) connectWithInterfaceController:(CPInterfaceController*)interfaceController window:(CPWindow*)window;
+ (void) disconnect;
- (NSArray<CPListSection*>*) parseSections:(NSArray*)sections;
- (NSArray<CPListSection*>*) parseSections:(NSArray*)sections templateId:(NSString *)templateId;

@end
201 changes: 167 additions & 34 deletions packages/react-native-carplay/ios/RNCarPlay.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "RNCarPlay.h"
#import "RNCarPlayViewController.h"
#import <React/RCTConvert.h>
#import <React/RCTRootView.h>

Expand Down Expand Up @@ -44,7 +45,7 @@ + (void) disconnect {
RNCarPlay *cp = [RNCarPlay allocWithZone:nil];
RNCPStore *store = [RNCPStore sharedManager];
[store setConnected:false];
[[store.window subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
store.window.rootViewController = nil;

if (cp.bridge) {
[cp sendEventWithName:@"didDisconnect" body:@{}];
Expand All @@ -67,6 +68,7 @@ + (id)allocWithZone:(NSZone *)zone {
return @[
@"didConnect",
@"didDisconnect",
@"didPressMenuItem",
// interface
@"barButtonPressed",
@"backButtonPressed",
Expand All @@ -81,6 +83,7 @@ + (id)allocWithZone:(NSZone *)zone {
@"actionButtonPressed",
// list
@"didSelectListItem",
@"didSelectListItemRowImage",
// search
@"updatedSearchText",
@"searchButtonPressed",
Expand Down Expand Up @@ -179,6 +182,33 @@ - (void)updateItemImageWithURL:(CPListItem *)item imgUrl:(NSString *)imgUrlStrin
[task resume];
}

- (void)updateListRowItemImageWithURL:(CPListImageRowItem *)item imgUrl:(NSString *)imgUrlString index:(int)index {
NSURL *imgUrl = [NSURL URLWithString:imgUrlString];

NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:imgUrl completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
UIImage *image = [UIImage imageWithData:data];

dispatch_async(dispatch_get_main_queue(), ^{
NSMutableArray* newImages = [item.gridImages mutableCopy];

@try {
newImages[index] = image;
}
@catch (NSException *exception) {
// Best effort updating the array
NSLog(@"Failed to update images array of CPListImageRowItem");
}

[item updateImages:newImages];
});
} else {
NSLog(@"Failed to load image for CPListImageRowItem from URL: %@", imgUrl);
}
}];
[task resume];
}

RCT_EXPORT_METHOD(checkForConnection) {
RNCPStore *store = [RNCPStore sharedManager];
if ([store isConnected] && hasListeners) {
Expand Down Expand Up @@ -212,7 +242,7 @@ - (void)updateItemImageWithURL:(CPListItem *)item imgUrl:(NSString *)imgUrlStrin
carPlayTemplate = gridTemplate;
}
else if ([type isEqualToString:@"list"]) {
NSArray *sections = [self parseSections:[RCTConvert NSArray:config[@"sections"]]];
NSArray *sections = [self parseSections:[RCTConvert NSArray:config[@"sections"]] templateId:templateId];
CPListTemplate *listTemplate;
if (@available(iOS 15.0, *)) {
if ([config objectForKey:@"assistant"]) {
Expand Down Expand Up @@ -617,7 +647,7 @@ - (void)updateItemImageWithURL:(CPListItem *)item imgUrl:(NSString *)imgUrlStrin
CPTemplate *template = [store findTemplateById:templateId];
if (template) {
CPListTemplate *listTemplate = (CPListTemplate*) template;
[listTemplate updateSections:[self parseSections:sections]];
[listTemplate updateSections:[self parseSections:sections templateId:templateId]];
} else {
NSLog(@"Failed to find template %@", template);
}
Expand Down Expand Up @@ -703,6 +733,24 @@ - (void)updateItemImageWithURL:(CPListItem *)item imgUrl:(NSString *)imgUrlStrin
}
}

RCT_EXPORT_METHOD(getMaximumListItemImageSize:(NSString *)templateId
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
RNCPStore *store = [RNCPStore sharedManager];
CPTemplate *template = [store findTemplateById:templateId];
if (template) {
CPListTemplate *listTemplate = (CPListTemplate*) template;
NSDictionary *sizeDict = @{
@"width": @(CPListItem.maximumImageSize.width),
@"height": @(CPListItem.maximumImageSize.height)
};
resolve(sizeDict);
} else {
NSLog(@"Failed to find template %@", template);
reject(@"template_not_found", @"Template not found in store", nil);
}
}

RCT_EXPORT_METHOD(getMaximumListSectionCount:(NSString *)templateId
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
Expand All @@ -717,6 +765,38 @@ - (void)updateItemImageWithURL:(CPListItem *)item imgUrl:(NSString *)imgUrlStrin
}
}

RCT_EXPORT_METHOD(getMaximumNumberOfGridImages:(NSString *)templateId
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
RNCPStore *store = [RNCPStore sharedManager];
CPTemplate *template = [store findTemplateById:templateId];
if (template) {
CPListTemplate *listTemplate = (CPListTemplate*) template;
resolve(@(CPMaximumNumberOfGridImages));
} else {
NSLog(@"Failed to find template %@", template);
reject(@"template_not_found", @"Template not found in store", nil);
}
}

RCT_EXPORT_METHOD(getMaximumListImageRowItemImageSize:(NSString *)templateId
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
RNCPStore *store = [RNCPStore sharedManager];
CPTemplate *template = [store findTemplateById:templateId];
if (template) {
CPListTemplate *listTemplate = (CPListTemplate*) template;
NSDictionary *sizeDict = @{
@"width": @(CPListImageRowItem.maximumImageSize.width),
@"height": @(CPListImageRowItem.maximumImageSize.height)
};
resolve(sizeDict);
} else {
NSLog(@"Failed to find template %@", template);
reject(@"template_not_found", @"Template not found in store", nil);
}
}

RCT_EXPORT_METHOD(updateMapTemplateConfig:(NSString *)templateId config:(NSDictionary*)config) {
CPTemplate *template = [[RNCPStore sharedManager] findTemplateById:templateId];
if (template) {
Expand Down Expand Up @@ -816,8 +896,8 @@ - (void)updateItemImageWithURL:(CPListItem *)item imgUrl:(NSString *)imgUrlStrin
}
}

RCT_EXPORT_METHOD(reactToUpdatedSearchText:(NSArray *)items) {
NSArray *sectionsItems = [self parseListItems:items startIndex:0];
RCT_EXPORT_METHOD(reactToUpdatedSearchText:(NSArray *)items templateId:(NSString *)templateId) {
NSArray *sectionsItems = [self parseListItems:items startIndex:0 templateId:templateId];

if (self.searchResultBlock) {
self.searchResultBlock(sectionsItems);
Expand Down Expand Up @@ -899,9 +979,8 @@ - (void) applyConfigForMapTemplate:(CPMapTemplate*)mapTemplate templateId:(NSStr

if ([config objectForKey:@"render"]) {
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:self.bridge moduleName:templateId initialProperties:@{}];
[rootView setFrame:store.window.frame];
[[store.window subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
[store.window addSubview:rootView];
RNCarPlayViewController *viewController = [[RNCarPlayViewController alloc] initWithRootView:rootView];
store.window.rootViewController = viewController;
}
}

Expand Down Expand Up @@ -981,14 +1060,14 @@ - (void) applyConfigForMapTemplate:(CPMapTemplate*)mapTemplate templateId:(NSStr
return result;
}

- (NSArray<CPListSection*>*)parseSections:(NSArray*)sections {
- (NSArray<CPListSection*>*)parseSections:(NSArray*)sections templateId:(NSString *)templateId {
NSMutableArray *result = [NSMutableArray array];
int index = 0;
for (NSDictionary *section in sections) {
NSArray *items = [section objectForKey:@"items"];
NSString *_sectionIndexTitle = [section objectForKey:@"sectionIndexTitle"];
NSString *_header = [section objectForKey:@"header"];
NSArray *_items = [self parseListItems:items startIndex:index];
NSArray *_items = [self parseListItems:items startIndex:index templateId:templateId];
CPListSection *_section = [[CPListSection alloc] initWithItems:_items header:_header sectionIndexTitle:_sectionIndexTitle];
[result addObject:_section];
int count = (int) [items count];
Expand All @@ -997,41 +1076,95 @@ - (void) applyConfigForMapTemplate:(CPMapTemplate*)mapTemplate templateId:(NSStr
return result;
}

- (NSArray<CPListItem*>*)parseListItems:(NSArray*)items startIndex:(int)startIndex {
- (NSArray<CPSelectableListItem>*)parseListItems:(NSArray*)items startIndex:(int)startIndex templateId:(NSString *)templateId {
NSMutableArray *_items = [NSMutableArray array];
int index = startIndex;
int listIndex = startIndex;
for (NSDictionary *item in items) {
BOOL _showsDisclosureIndicator = [[item objectForKey:@"showsDisclosureIndicator"] isEqualToNumber:[NSNumber numberWithInt:1]];
NSString *_detailText = [item objectForKey:@"detailText"];
NSString *_text = [item objectForKey:@"text"];
UIImage *_image = [RCTConvert UIImage:[item objectForKey:@"image"]];
CPListItem *_item;
if (@available(iOS 14.0, *)) {
CPListItemAccessoryType accessoryType = _showsDisclosureIndicator ? CPListItemAccessoryTypeDisclosureIndicator : CPListItemAccessoryTypeNone;
_item = [[CPListItem alloc] initWithText:_text detailText:_detailText image:_image accessoryImage:nil accessoryType:accessoryType];
NSObject *_imageObj = [item objectForKey:@"image"];

NSArray *_imageItems = [item objectForKey:@"images"];
NSArray *_imageUrls = [item objectForKey:@"imgUrls"];

if (_imageItems == nil && _imageUrls == nil) {
UIImage *_image = [RCTConvert UIImage:_imageObj];
CPListItem *_item;
if (@available(iOS 14.0, *)) {
CPListItemAccessoryType accessoryType = _showsDisclosureIndicator ? CPListItemAccessoryTypeDisclosureIndicator : CPListItemAccessoryTypeNone;
_item = [[CPListItem alloc] initWithText:_text detailText:_detailText image:_image accessoryImage:nil accessoryType:accessoryType];
} else {
_item = [[CPListItem alloc] initWithText:_text detailText:_detailText image:_image showsDisclosureIndicator:_showsDisclosureIndicator];
}
if ([item objectForKey:@"isPlaying"]) {
[_item setPlaying:[RCTConvert BOOL:[item objectForKey:@"isPlaying"]]];
}
if (item[@"imgUrl"]) {
NSString *imgUrlString = [RCTConvert NSString:item[@"imgUrl"]];
[self updateItemImageWithURL:_item imgUrl:imgUrlString];
}
[_item setUserInfo:@{ @"index": @(listIndex) }];
[_items addObject:_item];
} else {
_item = [[CPListItem alloc] initWithText:_text detailText:_detailText image:_image showsDisclosureIndicator:_showsDisclosureIndicator];
}
if ([item objectForKey:@"isPlaying"]) {
[_item setPlaying:[RCTConvert BOOL:[item objectForKey:@"isPlaying"]]];
}
if (@available(iOS 14.0, *) && [item objectForKey:@"playbackProgress"]) {
[_item setPlaybackProgress:[RCTConvert CGFloat:[item objectForKey:@"playbackProgress"]]];
}
if (@available(iOS 14.0, *) && [item objectForKey:@"accessoryImage"]) {
[_item setAccessoryImage:[RCTConvert UIImage:[item objectForKey:@"accessoryImage"]]];
}
if (item[@"imgUrl"]) {
NSString *imgUrlString = [RCTConvert NSString:item[@"imgUrl"]];
[self updateItemImageWithURL:_item imgUrl:imgUrlString];
// parse images
NSMutableArray * _images = [NSMutableArray array];

if (_imageItems != nil) {
NSArray* slicedArray = [_imageItems subarrayWithRange:NSMakeRange(0, MIN(CPMaximumNumberOfGridImages, _imageItems.count))];

for (NSObject *imageObj in slicedArray){
UIImage *_image = [RCTConvert UIImage:imageObj];
[_images addObject:_image];
}
}
if (@available(iOS 14.0, *)) {

CPListImageRowItem *_item;
if (_images.count > 0)
{
_item = [[CPListImageRowItem alloc] initWithText:_text images:_images];
}
else
{
// Show only as much images as allowed.
NSArray* _slicedArray = [_imageUrls subarrayWithRange:NSMakeRange(0, MIN(CPMaximumNumberOfGridImages, _imageUrls.count))];//

// create array with empty UI images, that will be replaced later
NSMutableArray *_imagesArray = [NSMutableArray arrayWithCapacity:_slicedArray.count];
for (NSUInteger i = 0; i < _slicedArray.count; i++) {
[_imagesArray addObject:[[UIImage alloc] init]];
}
_item = [[CPListImageRowItem alloc] initWithText:_text images:_imagesArray];


int _index = 0;
for (NSString* imgUrl in _slicedArray) {
[self updateListRowItemImageWithURL:_item imgUrl:imgUrl index:_index];
_index++;
}
}

[_item setListImageRowHandler:^(CPListImageRowItem * _Nonnull item, NSInteger index, dispatch_block_t _Nonnull completionBlock) {
// Find the current template
RNCPStore *store = [RNCPStore sharedManager];
CPTemplate *template = [store findTemplateById:templateId];
if (template) {
[self sendTemplateEventWithName:template name:@"didSelectListItemRowImage" json:@{ @"index": @(listIndex), @"imageIndex": @(index)}];
}
}];

[_item setUserInfo:@{ @"index": @(listIndex) }];
[_items addObject:_item];
}

}
[_item setUserInfo:@{ @"index": @(index) }];
[_items addObject:_item];
index = index + 1;
listIndex = listIndex + 1;
}
return _items;
}


- (NSArray<CPInformationItem*>*)parseInformationItems:(NSArray*)items {
NSMutableArray *_items = [NSMutableArray array];
for (NSDictionary *item in items) {
Expand Down
10 changes: 10 additions & 0 deletions packages/react-native-carplay/ios/RNCarPlayViewController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// RNCarPlayViewController.h
// Created by Susan Thapa on 27/02/2024.
//
#import <UIKit/UIKit.h>
#import <React/RCTRootView.h>

@interface RNCarPlayViewController : UIViewController
- (instancetype)initWithRootView:(RCTRootView *)rootView;
@end
Loading

0 comments on commit ed383a9

Please sign in to comment.