Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SDL-0196] Support Static Icon in Screen Manager #1106

Merged
26 changes: 26 additions & 0 deletions SmartDeviceLink/SDLArtwork.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#import <UIKit/UIKit.h>

#import "SDLFile.h"
#import "SDLStaticIconName.h"

@class SDLImage;

typedef NS_ENUM(NSUInteger, SDLArtworkImageFormat) {
SDLArtworkImageFormatPNG,
Expand All @@ -26,6 +29,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (assign, nonatomic, readonly) BOOL isTemplate;

/**
The Image RPC representing this artwork. Generally for use internally, you should instead pass an artwork to a Screen Manager method.
*/
@property (strong, nonatomic, readonly) SDLImage *imageRPC;

/**
* Convenience helper to create an ephemeral artwork from an image.
*
Expand Down Expand Up @@ -59,6 +67,15 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (instancetype)artworkWithImage:(UIImage *)image asImageFormat:(SDLArtworkImageFormat)imageFormat NS_SWIFT_UNAVAILABLE("Use the standard initializer and set persistant to false");

/**
Create an SDLArtwork that represents a static icon. This can only be passed to the screen manager; passing this directly to the file manager will fail.
@param staticIcon The static icon to be shown on the remote system.
@return An instance of this class to be passed to a screen manager.
*/
+ (instancetype)artworkWithStaticIcon:(SDLStaticIconName)staticIcon NS_SWIFT_UNAVAILABLE("Use the standard initializer");

/**
* Convenience helper to create a persistent artwork from an image.
*
Expand Down Expand Up @@ -115,6 +132,15 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (instancetype)initWithImage:(UIImage *)image persistent:(BOOL)persistent asImageFormat:(SDLArtworkImageFormat)imageFormat;

/**
Create an SDLArtwork that represents a static icon. This can only be passed to the screen manager; passing this directly to the file manager will fail.
@param staticIcon The static icon to be shown on the remote system.
@return An instance of this class to be passed to a screen manager.
*/
- (instancetype)initWithStaticIcon:(SDLStaticIconName)staticIcon;

@end

NS_ASSUME_NONNULL_END
43 changes: 37 additions & 6 deletions SmartDeviceLink/SDLArtwork.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@
// Copyright © 2015 smartdevicelink. All rights reserved.
//

#import <CommonCrypto/CommonDigest.h>

#import "SDLArtwork.h"
#import "SDLFileType.h"
#import <CommonCrypto/CommonDigest.h>
#import "SDLImage.h"

NS_ASSUME_NONNULL_BEGIN

@interface SDLFile ()

@property (assign, nonatomic, readwrite) BOOL isStaticIcon;

@end

@interface SDLArtwork ()

@property (strong, nonatomic) UIImage *image;
Expand All @@ -22,11 +30,6 @@ @interface SDLArtwork ()

@implementation SDLArtwork

- (void)setImage:(UIImage *)image {
_image = image;
_isTemplate = (image.renderingMode == UIImageRenderingModeAlwaysTemplate);
}

#pragma mark - Lifecycle

+ (instancetype)artworkWithImage:(UIImage *)image name:(NSString *)name asImageFormat:(SDLArtworkImageFormat)imageFormat {
Expand All @@ -37,6 +40,10 @@ + (instancetype)artworkWithImage:(UIImage *)image asImageFormat:(SDLArtworkImage
return [[self alloc] initWithImage:image persistent:NO asImageFormat:imageFormat];
}

+ (instancetype)artworkWithStaticIcon:(SDLStaticIconName)staticIcon {
return [[self alloc] initWithStaticIcon:staticIcon];
}

+ (instancetype)persistentArtworkWithImage:(UIImage *)image name:(NSString *)name asImageFormat:(SDLArtworkImageFormat)imageFormat {
return [[self alloc] initWithImage:image name:name persistent:YES asImageFormat:imageFormat];
}
Expand All @@ -60,6 +67,30 @@ - (instancetype)initWithImage:(UIImage *)image persistent:(BOOL)persistent asIma
return [super initWithData:[self.class sdl_dataForUIImage:image imageFormat:imageFormat] name:(imageName != nil ? imageName : @"") fileExtension:[self.class sdl_fileExtensionForImageFormat:imageFormat] persistent:persistent];
}

- (instancetype)initWithStaticIcon:(SDLStaticIconName)staticIcon {
self = [super initWithData:[staticIcon dataUsingEncoding:NSASCIIStringEncoding] name:staticIcon fileExtension:@"" persistent:NO];
self.isStaticIcon = true;

return self;
}

#pragma mark - Setters and Getters

- (void)setImage:(UIImage *)image {
_image = image;
_isTemplate = (image.renderingMode == UIImageRenderingModeAlwaysTemplate);
}

- (SDLImage *)imageRPC {
if (self.isStaticIcon) {
return [[SDLImage alloc] initWithStaticIconName:self.name];
} else {
return [[SDLImage alloc] initWithName:self.name isTemplate:self.isTemplate];
}
}

#pragma mark - Helper Methods

/**
* Returns the JPG or PNG image data for a UIImage.
*
Expand Down
4 changes: 4 additions & 0 deletions SmartDeviceLink/SDLChoiceSet.m
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ - (NSString *)description {
return [NSString stringWithFormat:@"SDLChoiceSet: \"%@\", layout: %@", _title, (_layout == SDLChoiceSetLayoutList ? @"List" : @"Tiles")];
}

- (NSString *)debugDescription {
return [NSString stringWithFormat:@"SDLChoiceSet: Title: \"%@\", layout: %@, timeout: %@, initial prompt: \"%@\", timeout prompt: \"%@\", help prompt: \"%@\", help list: %@, choices: %@", _title, (_layout == SDLChoiceSetLayoutList ? @"List" : @"Tiles"), @(_timeout), _initialPrompt, _timeoutPrompt, _helpPrompt, _helpList, _choices];
}

@end

NS_ASSUME_NONNULL_END
14 changes: 12 additions & 2 deletions SmartDeviceLink/SDLChoiceSetManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
#import "SDLError.h"
#import "SDLFileManager.h"
#import "SDLHMILevel.h"
#import "SDLImage.h"
#import "SDLKeyboardProperties.h"
#import "SDLLogMacros.h"
#import "SDLOnHMIStatus.h"
Expand Down Expand Up @@ -280,7 +279,12 @@ - (void)presentChoiceSet:(SDLChoiceSet *)choiceSet mode:(SDLInteractionMode)mode
}

self.pendingPresentationSet = choiceSet;
[self preloadChoices:self.pendingPresentationSet.choices withCompletionHandler:nil];
[self preloadChoices:self.pendingPresentationSet.choices withCompletionHandler:^(NSError * _Nullable error) {
if (error != nil) {
[choiceSet.delegate choiceSet:choiceSet didReceiveError:error];
return;
}
}];

[self sdl_findIdsOnChoiceSet:self.pendingPresentationSet];

Expand Down Expand Up @@ -404,11 +408,17 @@ - (NSString *)currentState {

- (void)sdl_registerResponse:(SDLRPCResponseNotification *)notification {
SDLRegisterAppInterfaceResponse *response = (SDLRegisterAppInterfaceResponse *)notification.response;

if (!response.success.boolValue) { return; }

self.displayCapabilities = response.displayCapabilities;
}

- (void)sdl_displayLayoutResponse:(SDLRPCResponseNotification *)notification {
SDLSetDisplayLayoutResponse *response = (SDLSetDisplayLayoutResponse *)notification.response;

if (!response.success.boolValue) { return; }

self.displayCapabilities = response.displayCapabilities;
}

Expand Down
1 change: 1 addition & 0 deletions SmartDeviceLink/SDLError.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ extern SDLErrorDomain *const SDLErrorDomainTransport;
+ (NSError *)sdl_fileManager_fileDoesNotExistError;
+ (NSError *)sdl_fileManager_fileUploadCanceled;
+ (NSError *)sdl_fileManager_dataMissingError;
+ (NSError *)sdl_fileManager_staticIconError;

#pragma mark Show Managers

Expand Down
9 changes: 9 additions & 0 deletions SmartDeviceLink/SDLError.m
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,15 @@ + (NSError *)sdl_fileManager_dataMissingError {
return [NSError errorWithDomain:SDLErrorDomainFileManager code:SDLFileManagerErrorFileDataMissing userInfo:userInfo];
}

+ (NSError *)sdl_fileManager_staticIconError {
NSDictionary<NSString *, NSString *> *userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedString(@"The file upload was canceled", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The file is a static icon, which cannot be uploaded", nil),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Stop trying to upload a static icon, set it via the screen manager or create an SDLImage instead", nil)
};
return [NSError errorWithDomain:SDLErrorDomainFileManager code:SDLFileManagerErrorStaticIcon userInfo:userInfo];
}

#pragma mark SDLUploadFileOperation

+ (NSError *)sdl_fileManager_fileDoesNotExistError {
Expand Down
4 changes: 4 additions & 0 deletions SmartDeviceLink/SDLErrorConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ typedef NS_ENUM(NSInteger, SDLFileManagerError) {
* The file data is nil or empty.
*/
SDLFileManagerErrorFileDataMissing = -9,
/*
* The file is a static icon, which cannot be uploaded
*/
SDLFileManagerErrorStaticIcon = -10,
};

/**
Expand Down
9 changes: 9 additions & 0 deletions SmartDeviceLink/SDLFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, readonly) NSInputStream *inputStream;

/**
Describes whether or not this file is represented by static icon data. The head unit will present its representation of the static icon concept when sent this data.
A file that represents a static icon may not be uploaded via the File Manager. It must be set using the Screen Manager.
Support for this feature may vary by system, but it will generally be more supported in soft buttons and menus.
*/
@property (assign, nonatomic, readonly) BOOL isStaticIcon;

- (instancetype)init NS_UNAVAILABLE;

/**
Expand Down
3 changes: 3 additions & 0 deletions SmartDeviceLink/SDLFile.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ @interface SDLFile ()
@property (copy, nonatomic, readwrite) NSString *name;

@property (nonatomic, readwrite) NSInputStream *inputStream;

@property (assign, nonatomic, readwrite) BOOL isStaticIcon;

@end


Expand Down
7 changes: 7 additions & 0 deletions SmartDeviceLink/SDLFileManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,13 @@ - (void)uploadFile:(SDLFile *)file completionHandler:(nullable SDLFileManagerUpl
return;
}

if (file.isStaticIcon) {
if (handler != nil) {
handler(NO, self.bytesAvailable, [NSError sdl_fileManager_staticIconError]);
}
return;
}

// Make sure we are able to send files
if (![self.currentState isEqualToString:SDLFileManagerStateReady]) {
if (handler != nil) {
Expand Down
20 changes: 13 additions & 7 deletions SmartDeviceLink/SDLPreloadChoicesOperation.m
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,13 @@ - (void)sdl_preloadCellArtworksWithCompletionHandler:(void(^)(NSError *_Nullable

NSMutableArray<SDLArtwork *> *artworksToUpload = [NSMutableArray arrayWithCapacity:self.cellsToUpload.count];
for (SDLChoiceCell *cell in self.cellsToUpload) {
if ([self.displayCapabilities hasImageFieldOfName:SDLImageFieldNameChoiceImage]) {
cell.artwork != nil ? [artworksToUpload addObject:cell.artwork] : nil;
if ([self.displayCapabilities hasImageFieldOfName:SDLImageFieldNameChoiceImage]
&& ![self sdl_artworkNeedsUpload:cell.artwork]) {
[artworksToUpload addObject:cell.artwork];
}
if ([self.displayCapabilities hasImageFieldOfName:SDLImageFieldNameChoiceSecondaryImage]) {
cell.secondaryArtwork != nil ? [artworksToUpload addObject:cell.secondaryArtwork] : nil;
if ([self.displayCapabilities hasImageFieldOfName:SDLImageFieldNameChoiceSecondaryImage]
&& ![self sdl_artworkNeedsUpload:cell.artwork]) {
[artworksToUpload addObject:cell.secondaryArtwork];
}
}

Expand All @@ -107,6 +109,10 @@ - (void)sdl_preloadCellArtworksWithCompletionHandler:(void(^)(NSError *_Nullable
}];
}

- (BOOL)sdl_artworkNeedsUpload:(SDLArtwork *)artwork {
return (!artwork || [self.fileManager hasUploadedFile:artwork] || artwork.isStaticIcon);
}

- (void)sdl_preloadCells {
_currentState = SDLPreloadChoicesOperationStatePreloadingChoices;

Expand All @@ -123,7 +129,7 @@ - (void)sdl_preloadCells {
}
} completionHandler:^(BOOL success) {
if (!success) {
SDLLogW(@"Error preloading choice cells: %@", errors);
SDLLogE(@"Error preloading choice cells: %@", errors);
weakSelf.internalError = [NSError sdl_choiceSetManager_choiceUploadFailed:errors];
}

Expand All @@ -147,8 +153,8 @@ - (SDLCreateInteractionChoiceSet *)sdl_choiceFromCell:(SDLChoiceCell *)cell {
NSString *secondaryText = [self.displayCapabilities hasTextFieldOfName:SDLTextFieldNameSecondaryText] ? cell.secondaryText : nil;
NSString *tertiaryText = [self.displayCapabilities hasTextFieldOfName:SDLTextFieldNameTertiaryText] ? cell.tertiaryText : nil;

SDLImage *image = ([self.displayCapabilities hasImageFieldOfName:SDLImageFieldNameChoiceImage] && cell.artwork != nil) ? [[SDLImage alloc] initWithName:cell.artwork.name isTemplate:cell.artwork.isTemplate] : nil;
SDLImage *secondaryImage = ([self.displayCapabilities hasImageFieldOfName:SDLImageFieldNameChoiceSecondaryImage] && cell.secondaryArtwork != nil) ? [[SDLImage alloc] initWithName:cell.secondaryArtwork.name isTemplate:cell.secondaryArtwork.isTemplate] : nil;
SDLImage *image = ([self.displayCapabilities hasImageFieldOfName:SDLImageFieldNameChoiceImage] && cell.artwork != nil) ? cell.artwork.imageRPC : nil;
SDLImage *secondaryImage = ([self.displayCapabilities hasImageFieldOfName:SDLImageFieldNameChoiceSecondaryImage] && cell.secondaryArtwork != nil) ? cell.secondaryArtwork.imageRPC : nil;

SDLChoice *choice = [[SDLChoice alloc] initWithId:cell.choiceId menuName:(NSString *_Nonnull)menuName vrCommands:(NSArray<NSString *> * _Nonnull)vrCommands image:image secondaryText:secondaryText secondaryImage:secondaryImage tertiaryText:tertiaryText];

Expand Down
15 changes: 12 additions & 3 deletions SmartDeviceLink/SDLSoftButtonManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ - (BOOL)sdl_currentStateHasImages {
- (BOOL)sdl_allCurrentStateImagesAreUploaded {
for (SDLSoftButtonObject *button in self.softButtonObjects) {
SDLArtwork *artwork = button.currentState.artwork;
if (artwork != nil && ![self.fileManager hasUploadedFile:artwork]) {
if (artwork != nil && ![self.fileManager hasUploadedFile:artwork] && !artwork.isStaticIcon) {
return NO;
}
}
Expand All @@ -268,7 +268,7 @@ - (void)sdl_uploadInitialStateImages {
NSMutableArray<SDLArtwork *> *initialStatesToBeUploaded = [NSMutableArray array];
// Upload all soft button images, the initial state images first, then the other states. We need to send updates when the initial state is ready.
for (SDLSoftButtonObject *object in self.softButtonObjects) {
if (object.currentState.artwork != nil && ![self.fileManager hasUploadedFile:object.currentState.artwork]) {
if (![self sdl_artworkNeedsUpload:object.currentState.artwork]) {
[initialStatesToBeUploaded addObject:object.currentState.artwork];
}
}
Expand All @@ -293,7 +293,7 @@ - (void)sdl_uploadOtherStateImages {
for (SDLSoftButtonObject *object in self.softButtonObjects) {
for (SDLSoftButtonState *state in object.states) {
if ([state.name isEqualToString:object.currentState.name]) { continue; }
if (state.artwork != nil && ![self.fileManager hasUploadedFile:state.artwork]) {
if (![self sdl_artworkNeedsUpload:object.currentState.artwork]) {
[otherStatesToBeUploaded addObject:state.artwork];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[otherStatesToBeUploaded addObject:state.artwork];
[otherStatesToBeUploaded addObject:object.currentState.artwork];

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise the app crashes when inserting a nil state.artwork into the array.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's incorrect logic that will cause other state artwork to never get uploaded.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All I know is that my app kept crashing at that point when I uploaded a soft button with a static icon image

}
}
Expand All @@ -313,6 +313,10 @@ - (void)sdl_uploadOtherStateImages {
}
}

- (BOOL)sdl_artworkNeedsUpload:(SDLArtwork *)artwork {
return (!artwork || [self.fileManager hasUploadedFile:artwork] || artwork.isStaticIcon);
}

#pragma mark - Creating Soft Buttons

/**
Expand Down Expand Up @@ -355,13 +359,18 @@ - (BOOL)hasQueuedUpdate {

- (void)sdl_registerResponse:(SDLRPCResponseNotification *)notification {
SDLRegisterAppInterfaceResponse *response = (SDLRegisterAppInterfaceResponse *)notification.response;

if (!response.success.boolValue) { return; }

self.softButtonCapabilities = response.softButtonCapabilities ? response.softButtonCapabilities.firstObject : nil;
self.displayCapabilities = response.displayCapabilities;
}

- (void)sdl_displayLayoutResponse:(SDLRPCResponseNotification *)notification {
SDLSetDisplayLayoutResponse *response = (SDLSetDisplayLayoutResponse *)notification.response;

if (!response.success.boolValue) { return; }

self.softButtonCapabilities = response.softButtonCapabilities ? response.softButtonCapabilities.firstObject : nil;
self.displayCapabilities = response.displayCapabilities;

Expand Down
2 changes: 1 addition & 1 deletion SmartDeviceLink/SDLSoftButtonState.m
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ - (SDLSoftButtonType)type {
- (nullable SDLImage *)image {
if (self.artwork == nil) { return nil; }

return [[SDLImage alloc] initWithName:self.artwork.name ofType:SDLImageTypeDynamic isTemplate:self.artwork.isTemplate];
return self.artwork.imageRPC;
}

- (NSString *)description {
Expand Down
Loading