summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Fischer <joeljfischer@gmail.com>2020-08-13 16:02:57 -0400
committerJoel Fischer <joeljfischer@gmail.com>2020-08-13 16:02:57 -0400
commitb8c36591299ff43b7792650ee14f73ac6979a701 (patch)
treec6b05480c0ef1f6819557dd75fd29e6200d7e202
parent062957f2c2963b30f4719909a1f795dcb7ca5961 (diff)
downloadsdl_ios-b8c36591299ff43b7792650ee14f73ac6979a701.tar.gz
*VERY* In-progress updates
-rw-r--r--SmartDeviceLink-iOS.xcodeproj/project.pbxproj16
-rw-r--r--SmartDeviceLink/SDLTextAndGraphicManager.m420
-rw-r--r--SmartDeviceLink/SDLTextAndGraphicState.h39
-rw-r--r--SmartDeviceLink/SDLTextAndGraphicState.m34
-rw-r--r--SmartDeviceLink/SDLTextAndGraphicUpdateOperation.h40
-rw-r--r--SmartDeviceLink/SDLTextAndGraphicUpdateOperation.m427
6 files changed, 594 insertions, 382 deletions
diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
index 1bae109fe..c86df5d47 100644
--- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
+++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
@@ -441,6 +441,10 @@
4A457DD924A5137100386CBA /* SDLLifecycleProtocolHandlerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A457DD824A5137100386CBA /* SDLLifecycleProtocolHandlerSpec.m */; };
4A4AD8A424894260008FC414 /* TestOldConfigurationUpdateManagerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A4AD8A324894260008FC414 /* TestOldConfigurationUpdateManagerDelegate.m */; };
4A4AD8A724894270008FC414 /* TestNewConfigurationUpdateManagerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A4AD8A624894270008FC414 /* TestNewConfigurationUpdateManagerDelegate.m */; };
+ 4A89AE1824E589D60017EBDC /* SDLTextAndGraphicUpdateOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A89AE1624E589D60017EBDC /* SDLTextAndGraphicUpdateOperation.h */; };
+ 4A89AE1924E589D60017EBDC /* SDLTextAndGraphicUpdateOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A89AE1724E589D60017EBDC /* SDLTextAndGraphicUpdateOperation.m */; };
+ 4A89AE1C24E595410017EBDC /* SDLTextAndGraphicState.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A89AE1A24E595410017EBDC /* SDLTextAndGraphicState.h */; };
+ 4A89AE1D24E595410017EBDC /* SDLTextAndGraphicState.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A89AE1B24E595410017EBDC /* SDLTextAndGraphicState.m */; };
4A99D00E247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A99D00C247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h */; };
4A99D00F247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A99D00D247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m */; };
4A99D0122475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A99D0102475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h */; };
@@ -2192,6 +2196,10 @@
4A4AD8A324894260008FC414 /* TestOldConfigurationUpdateManagerDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TestOldConfigurationUpdateManagerDelegate.m; path = DevAPISpecs/TestOldConfigurationUpdateManagerDelegate.m; sourceTree = "<group>"; };
4A4AD8A524894270008FC414 /* TestNewConfigurationUpdateManagerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestNewConfigurationUpdateManagerDelegate.h; sourceTree = "<group>"; };
4A4AD8A624894270008FC414 /* TestNewConfigurationUpdateManagerDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestNewConfigurationUpdateManagerDelegate.m; sourceTree = "<group>"; };
+ 4A89AE1624E589D60017EBDC /* SDLTextAndGraphicUpdateOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLTextAndGraphicUpdateOperation.h; sourceTree = "<group>"; };
+ 4A89AE1724E589D60017EBDC /* SDLTextAndGraphicUpdateOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLTextAndGraphicUpdateOperation.m; sourceTree = "<group>"; };
+ 4A89AE1A24E595410017EBDC /* SDLTextAndGraphicState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLTextAndGraphicState.h; sourceTree = "<group>"; };
+ 4A89AE1B24E595410017EBDC /* SDLTextAndGraphicState.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLTextAndGraphicState.m; sourceTree = "<group>"; };
4A99D00C247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDLTextField+ScreenManagerExtensions.h"; sourceTree = "<group>"; };
4A99D00D247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SDLTextField+ScreenManagerExtensions.m"; sourceTree = "<group>"; };
4A99D0102475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDLImageField+ScreenManagerExtensions.h"; sourceTree = "<group>"; };
@@ -4221,6 +4229,10 @@
children = (
5D0A7372203F0C730001595D /* SDLTextAndGraphicManager.h */,
5D0A7373203F0C730001595D /* SDLTextAndGraphicManager.m */,
+ 4A89AE1A24E595410017EBDC /* SDLTextAndGraphicState.h */,
+ 4A89AE1B24E595410017EBDC /* SDLTextAndGraphicState.m */,
+ 4A89AE1624E589D60017EBDC /* SDLTextAndGraphicUpdateOperation.h */,
+ 4A89AE1724E589D60017EBDC /* SDLTextAndGraphicUpdateOperation.m */,
);
name = "Text and Graphic";
sourceTree = "<group>";
@@ -6803,9 +6815,11 @@
884E702321FBA952008D53BA /* SDLAppServiceType.h in Headers */,
DAC572571D1067270004288B /* SDLTouchManager.h in Headers */,
5D61FE0D1A84238C00846EE7 /* SDLVrCapabilities.h in Headers */,
+ 4A89AE1824E589D60017EBDC /* SDLTextAndGraphicUpdateOperation.h in Headers */,
EEB1932E205028B700A8940C /* SDLControlFramePayloadTransportEventUpdate.h in Headers */,
EE798CA420561210008EDE8E /* SDLSecondaryTransportManager.h in Headers */,
5DBF06271E64A91D00A5CF03 /* SDLLogFileModule.h in Headers */,
+ 4A89AE1C24E595410017EBDC /* SDLTextAndGraphicState.h in Headers */,
5D61FC531A84238C00846EE7 /* SDLButtonEventMode.h in Headers */,
88E6F1AD220E19DF006156F9 /* SDLMediaServiceData.h in Headers */,
1FF7DAB61F75B27300B46C30 /* SDLFocusableItemLocatorType.h in Headers */,
@@ -7809,6 +7823,7 @@
9FE2471222D77AA400F8D2FC /* SDLCreateWindowResponse.m in Sources */,
5D61FDBC1A84238C00846EE7 /* SDLSystemAction.m in Sources */,
5D61FC381A84238C00846EE7 /* SDLAlert.m in Sources */,
+ 4A89AE1D24E595410017EBDC /* SDLTextAndGraphicState.m in Sources */,
88AAD4BD2211B76800F1E6D7 /* SDLMediaServiceManifest.m in Sources */,
884E701C21FB8D0F008D53BA /* SDLPublishAppService.m in Sources */,
8831FA49220235B000B8FFB7 /* SDLAppServicesCapabilities.m in Sources */,
@@ -7913,6 +7928,7 @@
88EED83F1F33C5A400E6C42E /* SDLSendHapticData.m in Sources */,
5D16545B1D3E7A1600554D93 /* SDLLifecycleManager.m in Sources */,
E9C32B971AB20BA200F283AF /* SDLTimer.m in Sources */,
+ 4A89AE1924E589D60017EBDC /* SDLTextAndGraphicUpdateOperation.m in Sources */,
10893C172417D78300BA347E /* SDLIconArchiveFile.m in Sources */,
5D61FCB61A84238C00846EE7 /* SDLGetVehicleData.m in Sources */,
5DAB5F572098E5D100A020C8 /* SDLProtocolConstants.m in Sources */,
diff --git a/SmartDeviceLink/SDLTextAndGraphicManager.m b/SmartDeviceLink/SDLTextAndGraphicManager.m
index 6324e13e3..624068602 100644
--- a/SmartDeviceLink/SDLTextAndGraphicManager.m
+++ b/SmartDeviceLink/SDLTextAndGraphicManager.m
@@ -26,6 +26,8 @@
#import "SDLSystemCapability.h"
#import "SDLSystemCapabilityManager.h"
#import "SDLTextField.h"
+#import "SDLTextAndGraphicUpdateOperation.h"
+#import "SDLTextAndGraphicState.h"
#import "SDLWindowCapability.h"
#import "SDLWindowCapability+ScreenManagerExtensions.h"
@@ -43,15 +45,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (strong, nonatomic) SDLShow *currentScreenData;
-/**
- This is the "full" update, including both text and image names, whether or not that will succeed at the moment (e.g. if images are in the process of uploading)
- */
-@property (strong, nonatomic, nullable) SDLShow *inProgressUpdate;
-@property (copy, nonatomic, nullable) SDLTextAndGraphicUpdateCompletionHandler inProgressHandler;
-
-@property (strong, nonatomic, nullable) SDLShow *queuedImageUpdate;
-@property (assign, nonatomic) BOOL hasQueuedUpdate;
-@property (copy, nonatomic, nullable) SDLTextAndGraphicUpdateCompletionHandler queuedUpdateHandler;
+@property (strong, nonatomic) NSOperationQueue *transactionQueue;
@property (strong, nonatomic, nullable) SDLWindowCapability *windowCapability;
@property (strong, nonatomic, nullable) SDLHMILevel currentLevel;
@@ -72,6 +66,7 @@ NS_ASSUME_NONNULL_BEGIN
_connectionManager = connectionManager;
_fileManager = fileManager;
_systemCapabilityManager = systemCapabilityManager;
+ _transactionQueue = [self sdl_newTransactionQueue];
_alignment = SDLTextAlignmentCenter;
@@ -118,11 +113,7 @@ NS_ASSUME_NONNULL_BEGIN
_textField4Type = nil;
_currentScreenData = [[SDLShow alloc] init];
- _inProgressUpdate = nil;
- _inProgressHandler = nil;
- _queuedImageUpdate = nil;
- _hasQueuedUpdate = NO;
- _queuedUpdateHandler = nil;
+ _transactionQueue = [self sdl_newTransactionQueue];
_windowCapability = nil;
_currentLevel = SDLHMILevelNone;
_blankArtwork = nil;
@@ -130,6 +121,28 @@ NS_ASSUME_NONNULL_BEGIN
_isDirty = NO;
}
+- (NSOperationQueue *)sdl_newTransactionQueue {
+ NSOperationQueue *queue = [[NSOperationQueue alloc] init];
+ queue.name = @"SDLTextAndGraphicManager Transaction Queue";
+ queue.maxConcurrentOperationCount = 1;
+ queue.qualityOfService = NSQualityOfServiceUserInitiated;
+ queue.suspended = YES;
+
+ return queue;
+}
+
+/// Suspend the queue if the soft button capabilities are nil (we assume that soft buttons are not supported)
+/// OR if the HMI level is NONE since we want to delay sending RPCs until we're in non-NONE
+- (void)sdl_updateTransactionQueueSuspended {
+ if (self.windowCapability == nil || [self.currentLevel isEqualToEnum:SDLHMILevelNone]) {
+ SDLLogD(@"Suspending the transaction queue. Current HMI level is NONE: %@, window capabilities are nil: %@", ([self.currentLevel isEqualToEnum:SDLHMILevelNone] ? @"YES" : @"NO"), (self.windowCapability == nil ? @"YES" : @"NO"));
+ self.transactionQueue.suspended = YES;
+ } else {
+ SDLLogD(@"Starting the transaction queue");
+ self.transactionQueue.suspended = NO;
+ }
+}
+
#pragma mark - Upload / Send
- (void)updateWithCompletionHandler:(nullable SDLTextAndGraphicUpdateCompletionHandler)handler {
@@ -151,336 +164,28 @@ NS_ASSUME_NONNULL_BEGIN
- (void)sdl_updateWithCompletionHandler:(nullable SDLTextAndGraphicUpdateCompletionHandler)handler {
SDLLogD(@"Updating text and graphics");
- if (self.inProgressUpdate != nil) {
- SDLLogV(@"In progress update exists, queueing update");
- // If we already have a pending update, we're going to tell the old handler that it was superseded by a new update and then return
- if (self.queuedUpdateHandler != nil) {
- SDLLogV(@"Queued update already exists, superseding previous queued update");
- self.queuedUpdateHandler([NSError sdl_textAndGraphicManager_pendingUpdateSuperseded]);
- self.queuedUpdateHandler = nil;
- }
-
- if (handler != nil) {
- self.queuedUpdateHandler = handler;
- } else {
- self.hasQueuedUpdate = YES;
- }
+ if (self.transactionQueue.operationCount > 0) {
+ SDLLogV(@"Transactions already exist, cancelling them");
+ [self.transactionQueue cancelAllOperations];
return;
}
- SDLShow *fullShow = [[SDLShow alloc] init];
- fullShow.alignment = self.alignment;
- fullShow.metadataTags = [[SDLMetadataTags alloc] init];
- fullShow = [self sdl_assembleShowText:fullShow];
- fullShow = [self sdl_assembleShowImages:fullShow];
-
- self.inProgressHandler = handler;
-
- __weak typeof(self)weakSelf = self;
- if (!([self sdl_shouldUpdatePrimaryImage] || [self sdl_shouldUpdateSecondaryImage])) {
- SDLLogV(@"No images to send, sending text");
- // If there are no images to update, just send the text
- self.inProgressUpdate = [self sdl_extractTextFromShow:fullShow];
- } else if (![self sdl_artworkNeedsUpload:self.primaryGraphic] && ![self sdl_artworkNeedsUpload:self.secondaryGraphic]) {
- SDLLogV(@"Images already uploaded, sending full update");
- // The files to be updated are already uploaded, send the full show immediately
- self.inProgressUpdate = fullShow;
- } else {
- SDLLogV(@"Images need to be uploaded, sending text and uploading images");
-
- // We need to upload or queue the upload of the images
- // Send the text immediately
- self.inProgressUpdate = [self sdl_extractTextFromShow:fullShow];
-
- // Start uploading the images
- __block SDLShow *thisUpdate = fullShow;
- [self sdl_uploadImagesWithCompletionHandler:^(NSError *_Nullable error) {
- __strong typeof(weakSelf) strongSelf = weakSelf;
-
- if (error != nil) {
- SDLShow *showWithGraphics = [self sdl_createImageOnlyShowWithPrimaryArtwork:self.primaryGraphic secondaryArtwork:self.secondaryGraphic];
- if (showWithGraphics != nil) {
- SDLLogW(@"Some images failed to upload. Sending update with the successfully uploaded images");
- self.inProgressUpdate = showWithGraphics;
- } else {
- SDLLogE(@"All images failed to upload. No graphics to show, skipping update.");
- self.inProgressUpdate = nil;
- }
- return;
- }
-
- // Check if queued image update still matches our images (there could have been a new Show in the meantime) and send a new update if it does. Since the images will already be on the head unit, the whole show will be sent
- // TODO: Send delete if it doesn't?
- if ([strongSelf sdl_showImages:thisUpdate isEqualToShowImages:strongSelf.queuedImageUpdate]) {
- SDLLogV(@"Queued image update matches the images we need, sending update");
- return [strongSelf sdl_updateWithCompletionHandler:strongSelf.inProgressHandler];
- } else {
- SDLLogV(@"Queued image update does not match the images we need, skipping update");
- }
- }];
- // When the images are done uploading, send another show with the images
- self.queuedImageUpdate = fullShow;
- }
-
- if (self.inProgressUpdate == nil) { return; }
-
- [self.connectionManager sendConnectionRequest:self.inProgressUpdate withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
- __strong typeof(weakSelf) strongSelf = weakSelf;
- SDLLogD(@"Text and Graphic update completed");
-
- // TODO: Monitor and delete old images when space is low?
- if (response.success) {
- [strongSelf sdl_updateCurrentScreenDataFromShow:(SDLShow *)request];
- }
-
- strongSelf.inProgressUpdate = nil;
- if (strongSelf.inProgressHandler != nil) {
- strongSelf.inProgressHandler(error);
- strongSelf.inProgressHandler = nil;
- }
-
- if (strongSelf.hasQueuedUpdate) {
- SDLLogV(@"Queued update exists, sending another update");
- [strongSelf sdl_updateWithCompletionHandler:[strongSelf.queuedUpdateHandler copy]];
- strongSelf.queuedUpdateHandler = nil;
- strongSelf.hasQueuedUpdate = NO;
- }
- }];
-}
-
-- (void)sdl_uploadImagesWithCompletionHandler:(void (^)(NSError *_Nullable error))handler {
- NSMutableArray<SDLArtwork *> *artworksToUpload = [NSMutableArray array];
- if ([self sdl_shouldUpdatePrimaryImage] && !self.primaryGraphic.isStaticIcon) {
- [artworksToUpload addObject:self.primaryGraphic];
- }
- if ([self sdl_shouldUpdateSecondaryImage] && !self.secondaryGraphic.isStaticIcon) {
- [artworksToUpload addObject:self.secondaryGraphic];
- }
-
- if (artworksToUpload.count == 0
- && (self.primaryGraphic.isStaticIcon || self.secondaryGraphic.isStaticIcon)) {
- SDLLogD(@"Upload attempted on static icons, sending them without upload instead");
- handler(nil);
- }
-
- [self.fileManager uploadArtworks:artworksToUpload completionHandler:^(NSArray<NSString *> * _Nonnull artworkNames, NSError * _Nullable error) {
- if (error != nil) {
- SDLLogW(@"Text and graphic manager artwork failed to upload with error: %@", error.localizedDescription);
- }
-
- handler(error);
- }];
-}
-
-#pragma mark - Assembly of Shows
-
-#pragma mark Images
-
-- (SDLShow *)sdl_assembleShowImages:(SDLShow *)show {
- if (![self sdl_shouldUpdatePrimaryImage] && ![self sdl_shouldUpdateSecondaryImage]) {
- return show;
- }
-
- if ([self sdl_shouldUpdatePrimaryImage]) {
- show.graphic = self.primaryGraphic.imageRPC;
- }
- if ([self sdl_shouldUpdateSecondaryImage]) {
- show.secondaryGraphic = self.secondaryGraphic.imageRPC;
- }
-
- return show;
-}
-
-#pragma mark Text
-
-- (SDLShow *)sdl_assembleShowText:(SDLShow *)show {
- [self sdl_setBlankTextFieldsWithShow:show];
-
- if (self.mediaTrackTextField != nil && [self sdl_shouldUpdateMediaTextField]) {
- show.mediaTrack = self.mediaTrackTextField;
- } else {
- show.mediaTrack = @"";
- }
-
- if (self.title != nil && [self sdl_shouldUpdateTitleField]) {
- show.templateTitle = self.title;
- } else {
- show.templateTitle = @"";
- }
-
- NSArray *nonNilFields = [self sdl_findNonNilTextFields];
- if (nonNilFields.count == 0) { return show; }
-
- NSUInteger numberOfLines = self.windowCapability.maxNumberOfMainFieldLines;
- if (numberOfLines == 1) {
- show = [self sdl_assembleOneLineShowText:show withShowFields:nonNilFields];
- } else if (numberOfLines == 2) {
- show = [self sdl_assembleTwoLineShowText:show];
- } else if (numberOfLines == 3) {
- show = [self sdl_assembleThreeLineShowText:show];
- } else if (numberOfLines == 4) {
- show = [self sdl_assembleFourLineShowText:show];
- }
-
- return show;
-}
-
-- (SDLShow *)sdl_assembleOneLineShowText:(SDLShow *)show withShowFields:(NSArray<NSString *> *)fields {
- NSMutableString *showString1 = [NSMutableString stringWithString:fields[0]];
- for (NSUInteger i = 1; i < fields.count; i++) {
- [showString1 appendFormat:@" - %@", fields[i]];
- }
- show.mainField1 = showString1.copy;
-
- SDLMetadataTags *tags = [[SDLMetadataTags alloc] init];
- NSMutableArray<SDLMetadataType> *metadataArray = [NSMutableArray array];
- self.textField1Type ? [metadataArray addObject:self.textField1Type] : nil;
- self.textField2Type ? [metadataArray addObject:self.textField2Type] : nil;
- self.textField3Type ? [metadataArray addObject:self.textField3Type] : nil;
- self.textField4Type ? [metadataArray addObject:self.textField4Type] : nil;
- tags.mainField1 = [metadataArray copy];
- show.metadataTags = tags;
-
- return show;
-}
-
-- (SDLShow *)sdl_assembleTwoLineShowText:(SDLShow *)show {
- NSMutableString *tempString = [NSMutableString string];
- if (self.textField1.length > 0) {
- // If text 1 exists, put it in slot 1
- [tempString appendString:self.textField1];
- show.metadataTags.mainField1 = self.textField1Type.length > 0 ? @[self.textField1Type] : @[];
- }
-
- if (self.textField2.length > 0) {
- if (!(self.textField3.length > 0 || self.textField4.length > 0)) {
- // If text 3 & 4 do not exist, put it in slot 2
- show.mainField2 = self.textField2;
- show.metadataTags.mainField2 = self.textField2Type.length > 0 ? @[self.textField2Type] : @[];
- } else if (self.textField1.length > 0) {
- // If text 1 exists, put it in slot 1 formatted
- [tempString appendFormat:@" - %@", self.textField2];
- show.metadataTags.mainField1 = self.textField2Type.length > 0 ? [show.metadataTags.mainField1 arrayByAddingObjectsFromArray:@[self.textField2Type]] : show.metadataTags.mainField1;
- } else {
- // If text 1 does not exist, put it in slot 1 unformatted
- [tempString appendString:self.textField2];
- show.metadataTags.mainField1 = self.textField2Type.length > 0 ? @[self.textField2Type] : @[];
- }
- }
-
- show.mainField1 = [tempString copy];
-
- tempString = [NSMutableString string];
- if (self.textField3.length > 0) {
- // If text 3 exists, put it in slot 2
- [tempString appendString:self.textField3];
- show.metadataTags.mainField2 = self.textField3Type.length > 0 ? @[self.textField3Type] : @[];
- }
-
- if (self.textField4.length > 0) {
- if (self.textField3.length > 0) {
- // If text 3 exists, put it in slot 2 formatted
- [tempString appendFormat:@" - %@", self.textField4];
- show.metadataTags.mainField2 = self.textField4Type.length > 0 ? [show.metadataTags.mainField2 arrayByAddingObjectsFromArray:@[self.textField4Type]] : show.metadataTags.mainField2;
- } else {
- // If text 3 does not exist, put it in slot 3 unformatted
- [tempString appendString:self.textField4];
- show.metadataTags.mainField2 = self.textField4Type.length > 0 ? @[self.textField4Type] : @[];
- }
- }
-
- if (tempString.length > 0) {
- show.mainField2 = [tempString copy];
- }
-
- return show;
-}
-
-- (SDLShow *)sdl_assembleThreeLineShowText:(SDLShow *)show {
- if (self.textField1.length > 0) {
- show.mainField1 = self.textField1;
- show.metadataTags.mainField1 = self.textField1Type.length > 0 ? @[self.textField1Type] : @[];
- }
-
- if (self.textField2.length > 0) {
- show.mainField2 = self.textField2;
- show.metadataTags.mainField2 = self.textField2Type.length > 0 ? @[self.textField2Type] : @[];
- }
-
- NSMutableString *tempString = [NSMutableString string];
- if (self.textField3.length > 0) {
- [tempString appendString:self.textField3];
- show.metadataTags.mainField3 = self.textField3Type.length > 0 ? @[self.textField3Type] : @[];
- }
-
- if (self.textField4.length > 0) {
- if (self.textField3.length > 0) {
- // If text 3 exists, put it in slot 3 formatted
- [tempString appendFormat:@" - %@", self.textField4];
- show.metadataTags.mainField3 = self.textField4Type.length > 0 ? [show.metadataTags.mainField3 arrayByAddingObjectsFromArray:@[self.textField4Type]] : show.metadataTags.mainField3;
- } else {
- // If text 3 does not exist, put it in slot 3 formatted
- [tempString appendString:self.textField4];
- show.metadataTags.mainField3 = self.textField4Type.length > 0 ? @[self.textField4Type] : @[];
- }
- }
-
- show.mainField3 = [tempString copy];
-
- return show;
+ SDLTextAndGraphicUpdateOperation *updateOperation = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager currentCapabilities:self.windowCapability currentScreenData:self.currentScreenData newState:[self currentState] updateCompletionHandler:handler];
+ updateOperation.completionBlock = ^{
+ // Check for error and `sentShow` and update our own current state, then update other pending transactions
+ };
+ [self.transactionQueue addOperation:updateOperation];
}
-- (SDLShow *)sdl_assembleFourLineShowText:(SDLShow *)show {
- if (self.textField1.length > 0) {
- show.mainField1 = self.textField1;
- show.metadataTags.mainField1 = self.textField1Type.length > 0 ? @[self.textField1Type] : @[];
- }
+#pragma mark - Convert to State
- if (self.textField2.length > 0) {
- show.mainField2 = self.textField2;
- show.metadataTags.mainField2 = self.textField2Type.length > 0 ? @[self.textField2Type] : @[];
- }
-
- if (self.textField3.length > 0) {
- show.mainField3 = self.textField3;
- show.metadataTags.mainField3 = self.textField3Type.length > 0 ? @[self.textField3Type] : @[];
- }
-
- if (self.textField4.length > 0) {
- show.mainField4 = self.textField4;
- show.metadataTags.mainField4 = self.textField4Type.length > 0 ? @[self.textField4Type] : @[];
- }
-
- return show;
-}
-
-- (SDLShow *)sdl_setBlankTextFieldsWithShow:(SDLShow *)show {
- show.mainField1 = @"";
- show.mainField2 = @"";
- show.mainField3 = @"";
- show.mainField4 = @"";
- show.mediaTrack = @"";
- show.templateTitle = @"";
-
- return show;
+- (SDLTextAndGraphicState *)currentState {
+ return [[SDLTextAndGraphicState alloc] initWithTextField1:_textField1 textField2:_textField2 textField3:_textField3 textField4:_textField4 mediaText:_mediaTrackTextField title:_title primaryGraphic:_primaryGraphic secondaryGraphic:_secondaryGraphic alignment:_alignment textField1Type:_textField1Type textField2Type:_textField2Type textField3Type:_textField3Type textField4Type:_textField4Type];
}
#pragma mark - Extraction
-- (SDLShow *)sdl_extractTextFromShow:(SDLShow *)show {
- SDLShow *newShow = [[SDLShow alloc] init];
- newShow.mainField1 = show.mainField1;
- newShow.mainField2 = show.mainField2;
- newShow.mainField3 = show.mainField3;
- newShow.mainField4 = show.mainField4;
- newShow.mediaTrack = show.mediaTrack;
- newShow.templateTitle = show.templateTitle;
- newShow.metadataTags = show.metadataTags;
-
- return newShow;
-}
-
- (SDLShow *)sdl_extractImageFromShow:(SDLShow *)show {
SDLShow *newShow = [[SDLShow alloc] init];
newShow.graphic = show.graphic;
@@ -518,55 +223,6 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - Helpers
-- (BOOL)sdl_artworkNeedsUpload:(SDLArtwork *)artwork {
- return (artwork != nil && ![self.fileManager hasUploadedFile:artwork] && !artwork.isStaticIcon);
-}
-
-- (BOOL)sdl_shouldUpdatePrimaryImage {
- BOOL templateSupportsPrimaryArtwork = [self.windowCapability hasImageFieldOfName:SDLImageFieldNameGraphic];
- BOOL graphicMatchesExisting = [self.currentScreenData.graphic.value isEqualToString:self.primaryGraphic.name];
- BOOL graphicExists = (self.primaryGraphic != nil);
-
- return (templateSupportsPrimaryArtwork && !graphicMatchesExisting && graphicExists);
-}
-
-- (BOOL)sdl_shouldUpdateSecondaryImage {
- BOOL templateSupportsSecondaryArtwork = [self.windowCapability hasImageFieldOfName:SDLImageFieldNameSecondaryGraphic];
- BOOL graphicMatchesExisting = [self.currentScreenData.secondaryGraphic.value isEqualToString:self.secondaryGraphic.name];
- BOOL graphicExists = (self.secondaryGraphic != nil);
-
- // Cannot detect if there is a secondary image, so we'll just try to detect if there's a primary image and allow it if there is.
- return (templateSupportsSecondaryArtwork && !graphicMatchesExisting && graphicExists);
-}
-
-- (BOOL)sdl_shouldUpdateMediaTextField {
- return [self.windowCapability hasTextFieldOfName:SDLTextFieldNameMediaTrack];
-}
-
-- (BOOL)sdl_shouldUpdateTitleField {
- return [self.windowCapability hasTextFieldOfName:SDLTextFieldNameTemplateTitle];
-}
-
-- (NSArray<NSString *> *)sdl_findNonNilTextFields {
- NSMutableArray *array = [NSMutableArray array];
- (self.textField1.length > 0) ? [array addObject:self.textField1] : nil;
- (self.textField2.length > 0) ? [array addObject:self.textField2] : nil;
- (self.textField3.length > 0) ? [array addObject:self.textField3] : nil;
- (self.textField4.length > 0) ? [array addObject:self.textField4] : nil;
-
- return [array copy];
-}
-
-- (NSArray<SDLMetadataType> *)sdl_findNonNilMetadataFields {
- NSMutableArray *array = [NSMutableArray array];
- (self.textField1Type.length) > 0 ? [array addObject:self.textField1Type] : nil;
- (self.textField2Type.length) > 0 ? [array addObject:self.textField2Type] : nil;
- (self.textField3Type.length) > 0 ? [array addObject:self.textField3Type] : nil;
- (self.textField4Type.length) > 0 ? [array addObject:self.textField4Type] : nil;
-
- return [array copy];
-}
-
- (BOOL)sdl_hasData {
BOOL hasTextFields = ([self sdl_findNonNilTextFields].count > 0) || (self.title.length > 0) || (self.mediaTrackTextField.length > 0);
BOOL hasImageFields = (self.primaryGraphic != nil) || (self.secondaryGraphic != nil);
diff --git a/SmartDeviceLink/SDLTextAndGraphicState.h b/SmartDeviceLink/SDLTextAndGraphicState.h
new file mode 100644
index 000000000..3fd2b3f93
--- /dev/null
+++ b/SmartDeviceLink/SDLTextAndGraphicState.h
@@ -0,0 +1,39 @@
+//
+// SDLTextAndGraphicState.h
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 8/13/20.
+// Copyright © 2020 smartdevicelink. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+#import "SDLMetadataType.h"
+#import "SDLTextAlignment.h"
+
+@class SDLArtwork;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SDLTextAndGraphicState : NSObject <NSCopying>
+
+@property (copy, nonatomic, nullable) NSString *textField1;
+@property (copy, nonatomic, nullable) NSString *textField2;
+@property (copy, nonatomic, nullable) NSString *textField3;
+@property (copy, nonatomic, nullable) NSString *textField4;
+@property (copy, nonatomic, nullable) NSString *mediaTrackTextField;
+@property (copy, nonatomic, nullable) NSString *title;
+@property (strong, nonatomic, nullable) SDLArtwork *primaryGraphic;
+@property (strong, nonatomic, nullable) SDLArtwork *secondaryGraphic;
+
+@property (copy, nonatomic, nullable) SDLTextAlignment alignment;
+@property (copy, nonatomic, nullable) SDLMetadataType textField1Type;
+@property (copy, nonatomic, nullable) SDLMetadataType textField2Type;
+@property (copy, nonatomic, nullable) SDLMetadataType textField3Type;
+@property (copy, nonatomic, nullable) SDLMetadataType textField4Type;
+
+- (instancetype)initWithTextField1:(nullable NSString *)textField1 textField2:(nullable NSString *)textField2 textField3:(nullable NSString *)textField3 textField4:(nullable NSString *)textField4 mediaText:(nullable NSString *)mediaTrackTextField title:(nullable NSString *)title primaryGraphic:(nullable SDLArtwork *)primaryGraphic secondaryGraphic:(nullable SDLArtwork *)secondaryGraphic alignment:(nullable SDLTextAlignment)alignment textField1Type:(nullable SDLMetadataType)textField1Type textField2Type:(nullable SDLMetadataType)textField2Type textField3Type:(nullable SDLMetadataType)textField3Type textField4Type:(nullable SDLMetadataType)textField4Type;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLTextAndGraphicState.m b/SmartDeviceLink/SDLTextAndGraphicState.m
new file mode 100644
index 000000000..24c94089e
--- /dev/null
+++ b/SmartDeviceLink/SDLTextAndGraphicState.m
@@ -0,0 +1,34 @@
+//
+// SDLTextAndGraphicState.m
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 8/13/20.
+// Copyright © 2020 smartdevicelink. All rights reserved.
+//
+
+#import "SDLTextAndGraphicState.h"
+
+@implementation SDLTextAndGraphicState
+
+- (instancetype)initWithTextField1:(NSString *)textField1 textField2:(NSString *)textField2 textField3:(NSString *)textField3 textField4:(NSString *)textField4 mediaText:(NSString *)mediaTrackTextField title:(NSString *)title primaryGraphic:(SDLArtwork *)primaryGraphic secondaryGraphic:(SDLArtwork *)secondaryGraphic alignment:(SDLTextAlignment)alignment textField1Type:(SDLMetadataType)textField1Type textField2Type:(SDLMetadataType)textField2Type textField3Type:(SDLMetadataType)textField3Type textField4Type:(SDLMetadataType)textField4Type {
+ self = [self init];
+ if (!self) { return nil; }
+
+ _textField1 = textField1;
+ _textField2 = textField2;
+ _textField3 = textField3;
+ _textField4 = textField4;
+ _mediaTrackTextField = mediaTrackTextField;
+ _title = title;
+ _primaryGraphic = primaryGraphic;
+ _secondaryGraphic = secondaryGraphic;
+ _alignment = alignment;
+ _textField1Type = textField1Type;
+ _textField2Type = textField2Type;
+ _textField3Type = textField3Type;
+ _textField4Type = textField4Type;
+
+ return self;
+}
+
+@end
diff --git a/SmartDeviceLink/SDLTextAndGraphicUpdateOperation.h b/SmartDeviceLink/SDLTextAndGraphicUpdateOperation.h
new file mode 100644
index 000000000..99f59de31
--- /dev/null
+++ b/SmartDeviceLink/SDLTextAndGraphicUpdateOperation.h
@@ -0,0 +1,40 @@
+//
+// SDLTextAndGraphicUpdateOperation.h
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 8/13/20.
+// Copyright © 2020 smartdevicelink. All rights reserved.
+//
+
+#import "SDLAsynchronousOperation.h"
+
+@class SDLArtwork;
+@class SDLFileManager;
+@class SDLImageField;
+@class SDLTextField;
+@class SDLShow;
+@class SDLTextAndGraphicState;
+@class SDLWindowCapability;
+
+@protocol SDLConnectionManagerType;
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef void(^SDLTextAndGraphicUpdateCompletionHandler)(NSError *__nullable error);
+
+@interface SDLTextAndGraphicUpdateOperation : SDLAsynchronousOperation
+
+@property (strong, nonatomic, nullable, readonly) SDLShow *sentShow;
+
+/// Initialize the operation with its dependencies
+/// @param connectionManager The connection manager to send RPCs
+/// @param fileManager The file manager to upload artwork
+/// @param currentCapabilities The current window capability describing whether or not image fields and text fields are supported
+/// @param currentData The current show data to determine which text and image fields need to be sent
+/// @param newState The new text and graphic manager state to be compared with currentData and sent in a Show update if needed.
+/// @param updateCompletionHandler The handler potentially passed by the developer to be called when the update finishes
+- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager fileManager:(SDLFileManager *)fileManager currentCapabilities:(SDLWindowCapability *)currentCapabilities currentScreenData:(SDLShow *)currentData newState:(SDLTextAndGraphicState *)newState updateCompletionHandler:(nullable SDLTextAndGraphicUpdateCompletionHandler)updateCompletionHandler;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLTextAndGraphicUpdateOperation.m b/SmartDeviceLink/SDLTextAndGraphicUpdateOperation.m
new file mode 100644
index 000000000..179cf2603
--- /dev/null
+++ b/SmartDeviceLink/SDLTextAndGraphicUpdateOperation.m
@@ -0,0 +1,427 @@
+//
+// SDLTextAndGraphicUpdateOperation.m
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 8/13/20.
+// Copyright © 2020 smartdevicelink. All rights reserved.
+//
+
+#import "SDLTextAndGraphicUpdateOperation.h"
+
+#import "SDLArtwork.h"
+#import "SDLConnectionManagerType.h"
+#import "SDLFileManager.h"
+#import "SDLImage.h"
+#import "SDLLogMacros.h"
+#import "SDLMetadataTags.h"
+#import "SDLShow.h"
+#import "SDLTextAndGraphicState.h"
+#import "SDLWindowCapability.h"
+#import "SDLWindowCapability+ScreenManagerExtensions.h"
+
+@interface SDLTextAndGraphicUpdateOperation ()
+
+@property (weak, nonatomic) id<SDLConnectionManagerType> connectionManager;
+@property (weak, nonatomic) SDLFileManager *fileManager;
+@property (strong, nonatomic) SDLWindowCapability *currentCapabilities;
+@property (strong, nonatomic) SDLShow *currentScreenData;
+@property (strong, nonatomic) SDLTextAndGraphicState *updatedState;
+
+@property (copy, nonatomic, nullable) SDLTextAndGraphicUpdateCompletionHandler updateCompletionHandler;
+
+@property (copy, nonatomic, nullable) NSError *internalError;
+
+@end
+
+@implementation SDLTextAndGraphicUpdateOperation
+
+- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager fileManager:(SDLFileManager *)fileManager currentCapabilities:(SDLWindowCapability *)currentCapabilities currentScreenData:(SDLShow *)currentData newState:(nonnull SDLTextAndGraphicState *)newState {
+ self = [self init];
+ if (!self) { return nil; }
+
+ _connectionManager = connectionManager;
+ _fileManager = fileManager;
+ _currentCapabilities = currentCapabilities;
+ _currentScreenData = currentData;
+ _updatedState = newState;
+
+ return self;
+}
+
+- (void)start {
+ [super start];
+ if (self.cancelled) { return; }
+
+ // Build a show with everything from `self.newState`, we'll pull things out later if we can.
+ SDLShow *fullShow = [[SDLShow alloc] init];
+ fullShow.alignment = self.updatedState.alignment;
+ fullShow.metadataTags = [[SDLMetadataTags alloc] init];
+ fullShow = [self sdl_assembleShowText:fullShow];
+ fullShow = [self sdl_assembleShowImages:fullShow];
+
+ SDLShow *showToSend = nil;
+
+ if (!([self sdl_shouldUpdatePrimaryImage] || [self sdl_shouldUpdateSecondaryImage])) {
+ SDLLogV(@"No images to send, sending text");
+ // If there are no images to update, just send the text
+ showToSend = [self sdl_extractTextFromShow:fullShow];
+ } else if (![self sdl_artworkNeedsUpload:self.updatedState.primaryGraphic] && ![self sdl_artworkNeedsUpload:self.updatedState.secondaryGraphic]) {
+ SDLLogV(@"Images already uploaded, sending full update");
+ // The files to be updated are already uploaded, send the full show immediately
+ showToSend = fullShow;
+ } else {
+ SDLLogV(@"Images need to be uploaded, sending text and uploading images");
+
+ // We need to upload or queue the upload of the images
+ // Send the text immediately
+ showToSend = [self sdl_extractTextFromShow:fullShow];
+ }
+
+ // Send the initial, and potentially only, Show request
+ __weak typeof(self)weakSelf = self;
+ [self.connectionManager sendConnectionRequest:showToSend withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ SDLLogD(@"Text and Graphic update completed");
+
+ // TODO: Monitor and delete old images when space is low?
+ if (response.success) {
+ [strongSelf sdl_updateCurrentScreenDataFromShow:(SDLShow *)request];
+ [self sdl_uploadImagesAndSendWhenDone];
+ } else {
+ // It failed, store the error and pass it along
+ self.internalError = error;
+ }
+ }];
+}
+
+- (void)sdl_uploadImagesAndSendWhenDone {
+ // Start uploading the images
+ __weak typeof(self)weakSelf = self;
+ [self sdl_uploadImagesWithCompletionHandler:^(NSError *_Nullable error) {
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+
+ if (error != nil) {
+ SDLShow *showWithGraphics = [self sdl_createImageOnlyShowWithPrimaryArtwork:self.newState.primaryGraphic secondaryArtwork:self.newState.secondaryGraphic];
+ if (showWithGraphics != nil) {
+ SDLLogW(@"Some images failed to upload. Sending update with the successfully uploaded images");
+ self.inProgressUpdate = showWithGraphics;
+ } else {
+ SDLLogE(@"All images failed to upload. No graphics to show, skipping update.");
+ self.inProgressUpdate = nil;
+ }
+ return;
+ }
+
+ // Check if queued image update still matches our images (there could have been a new Show in the meantime) and send a new update if it does. Since the images will already be on the head unit, the whole show will be sent
+ // TODO: Send delete if it doesn't?
+ if ([strongSelf sdl_showImages:thisUpdate isEqualToShowImages:strongSelf.queuedImageUpdate]) {
+ SDLLogV(@"Queued image update matches the images we need, sending update");
+ return [strongSelf sdl_updateWithCompletionHandler:strongSelf.inProgressHandler];
+ } else {
+ SDLLogV(@"Queued image update does not match the images we need, skipping update");
+ }
+ }];
+ // When the images are done uploading, send another show with the images
+ self.queuedImageUpdate = fullShow;
+}
+
+#pragma mark - Uploading Images
+
+- (void)sdl_uploadImagesWithCompletionHandler:(void (^)(NSError *_Nullable error))handler {
+ NSMutableArray<SDLArtwork *> *artworksToUpload = [NSMutableArray array];
+ if ([self sdl_shouldUpdatePrimaryImage] && !self.updatedState.primaryGraphic.isStaticIcon) {
+ [artworksToUpload addObject:self.updatedState.primaryGraphic];
+ }
+ if ([self sdl_shouldUpdateSecondaryImage] && !self.updatedState.secondaryGraphic.isStaticIcon) {
+ [artworksToUpload addObject:self.updatedState.secondaryGraphic];
+ }
+
+ if (artworksToUpload.count == 0) {
+ SDLLogD(@"No artworks need an upload, sending them without upload instead");
+ return handler(nil);
+ }
+
+ [self.fileManager uploadArtworks:artworksToUpload completionHandler:^(NSArray<NSString *> * _Nonnull artworkNames, NSError * _Nullable error) {
+ if (error != nil) {
+ SDLLogW(@"Text and graphic manager artwork failed to upload with error: %@", error.localizedDescription);
+ }
+
+ handler(error);
+ }];
+}
+
+
+#pragma mark - Assembly of Shows
+
+#pragma mark Images
+
+- (SDLShow *)sdl_assembleShowImages:(SDLShow *)show {
+ if (![self sdl_shouldUpdatePrimaryImage] && ![self sdl_shouldUpdateSecondaryImage]) {
+ return show;
+ }
+
+ if ([self sdl_shouldUpdatePrimaryImage]) {
+ show.graphic = self.updatedState.primaryGraphic.imageRPC;
+ }
+ if ([self sdl_shouldUpdateSecondaryImage]) {
+ show.secondaryGraphic = self.updatedState.secondaryGraphic.imageRPC;
+ }
+
+ return show;
+}
+
+#pragma mark Text
+
+- (SDLShow *)sdl_assembleShowText:(SDLShow *)show {
+ [self sdl_setBlankTextFieldsWithShow:show];
+
+ if (self.updatedState.mediaTrackTextField != nil && [self sdl_shouldUpdateMediaTextField]) {
+ show.mediaTrack = self.updatedState.mediaTrackTextField;
+ } else {
+ show.mediaTrack = @"";
+ }
+
+ if (self.updatedState.title != nil && [self sdl_shouldUpdateTitleField]) {
+ show.templateTitle = self.updatedState.title;
+ } else {
+ show.templateTitle = @"";
+ }
+
+ NSArray *nonNilFields = [self sdl_findNonNilTextFields];
+ if (nonNilFields.count == 0) { return show; }
+
+ NSUInteger numberOfLines = self.currentCapabilities.maxNumberOfMainFieldLines;
+ if (numberOfLines == 1) {
+ show = [self sdl_assembleOneLineShowText:show withShowFields:nonNilFields];
+ } else if (numberOfLines == 2) {
+ show = [self sdl_assembleTwoLineShowText:show];
+ } else if (numberOfLines == 3) {
+ show = [self sdl_assembleThreeLineShowText:show];
+ } else if (numberOfLines == 4) {
+ show = [self sdl_assembleFourLineShowText:show];
+ }
+
+ return show;
+}
+
+- (SDLShow *)sdl_assembleOneLineShowText:(SDLShow *)show withShowFields:(NSArray<NSString *> *)fields {
+ NSMutableString *showString1 = [NSMutableString stringWithString:fields[0]];
+ for (NSUInteger i = 1; i < fields.count; i++) {
+ [showString1 appendFormat:@" - %@", fields[i]];
+ }
+ show.mainField1 = showString1.copy;
+
+ SDLMetadataTags *tags = [[SDLMetadataTags alloc] init];
+ NSMutableArray<SDLMetadataType> *metadataArray = [NSMutableArray array];
+ self.updatedState.textField1Type ? [metadataArray addObject:self.updatedState.textField1Type] : nil;
+ self.updatedState.textField2Type ? [metadataArray addObject:self.updatedState.textField2Type] : nil;
+ self.updatedState.textField3Type ? [metadataArray addObject:self.updatedState.textField3Type] : nil;
+ self.updatedState.textField4Type ? [metadataArray addObject:self.updatedState.textField4Type] : nil;
+ tags.mainField1 = [metadataArray copy];
+ show.metadataTags = tags;
+
+ return show;
+}
+
+- (SDLShow *)sdl_assembleTwoLineShowText:(SDLShow *)show {
+ NSMutableString *tempString = [NSMutableString string];
+ if (self.updatedState.textField1.length > 0) {
+ // If text 1 exists, put it in slot 1
+ [tempString appendString:self.updatedState.textField1];
+ show.metadataTags.mainField1 = self.updatedState.textField1Type.length > 0 ? @[self.updatedState.textField1Type] : @[];
+ }
+
+ if (self.updatedState.textField2.length > 0) {
+ if (!(self.updatedState.textField3.length > 0 || self.updatedState.textField4.length > 0)) {
+ // If text 3 & 4 do not exist, put it in slot 2
+ show.mainField2 = self.updatedState.textField2;
+ show.metadataTags.mainField2 = self.updatedState.textField2Type.length > 0 ? @[self.updatedState.textField2Type] : @[];
+ } else if (self.updatedState.textField1.length > 0) {
+ // If text 1 exists, put it in slot 1 formatted
+ [tempString appendFormat:@" - %@", self.updatedState.textField2];
+ show.metadataTags.mainField1 = self.updatedState.textField2Type.length > 0 ? [show.metadataTags.mainField1 arrayByAddingObjectsFromArray:@[self.updatedState.textField2Type]] : show.metadataTags.mainField1;
+ } else {
+ // If text 1 does not exist, put it in slot 1 unformatted
+ [tempString appendString:self.updatedState.textField2];
+ show.metadataTags.mainField1 = self.updatedState.textField2Type.length > 0 ? @[self.updatedState.textField2Type] : @[];
+ }
+ }
+
+ show.mainField1 = [tempString copy];
+
+ tempString = [NSMutableString string];
+ if (self.updatedState.textField3.length > 0) {
+ // If text 3 exists, put it in slot 2
+ [tempString appendString:self.updatedState.textField3];
+ show.metadataTags.mainField2 = self.updatedState.textField3Type.length > 0 ? @[self.updatedState.textField3Type] : @[];
+ }
+
+ if (self.updatedState.textField4.length > 0) {
+ if (self.updatedState.textField3.length > 0) {
+ // If text 3 exists, put it in slot 2 formatted
+ [tempString appendFormat:@" - %@", self.updatedState.textField4];
+ show.metadataTags.mainField2 = self.updatedState.textField4Type.length > 0 ? [show.metadataTags.mainField2 arrayByAddingObjectsFromArray:@[self.updatedState.textField4Type]] : show.metadataTags.mainField2;
+ } else {
+ // If text 3 does not exist, put it in slot 3 unformatted
+ [tempString appendString:self.updatedState.textField4];
+ show.metadataTags.mainField2 = self.updatedState.textField4Type.length > 0 ? @[self.updatedState.textField4Type] : @[];
+ }
+ }
+
+ if (tempString.length > 0) {
+ show.mainField2 = [tempString copy];
+ }
+
+ return show;
+}
+
+- (SDLShow *)sdl_assembleThreeLineShowText:(SDLShow *)show {
+ if (self.updatedState.textField1.length > 0) {
+ show.mainField1 = self.updatedState.textField1;
+ show.metadataTags.mainField1 = self.updatedState.textField1Type.length > 0 ? @[self.updatedState.textField1Type] : @[];
+ }
+
+ if (self.updatedState.textField2.length > 0) {
+ show.mainField2 = self.updatedState.textField2;
+ show.metadataTags.mainField2 = self.updatedState.textField2Type.length > 0 ? @[self.updatedState.textField2Type] : @[];
+ }
+
+ NSMutableString *tempString = [NSMutableString string];
+ if (self.updatedState.textField3.length > 0) {
+ [tempString appendString:self.updatedState.textField3];
+ show.metadataTags.mainField3 = self.updatedState.textField3Type.length > 0 ? @[self.updatedState.textField3Type] : @[];
+ }
+
+ if (self.updatedState.textField4.length > 0) {
+ if (self.updatedState.textField3.length > 0) {
+ // If text 3 exists, put it in slot 3 formatted
+ [tempString appendFormat:@" - %@", self.updatedState.textField4];
+ show.metadataTags.mainField3 = self.updatedState.textField4Type.length > 0 ? [show.metadataTags.mainField3 arrayByAddingObjectsFromArray:@[self.updatedState.textField4Type]] : show.metadataTags.mainField3;
+ } else {
+ // If text 3 does not exist, put it in slot 3 formatted
+ [tempString appendString:self.updatedState.textField4];
+ show.metadataTags.mainField3 = self.updatedState.textField4Type.length > 0 ? @[self.updatedState.textField4Type] : @[];
+ }
+ }
+
+ show.mainField3 = [tempString copy];
+
+ return show;
+}
+
+- (SDLShow *)sdl_assembleFourLineShowText:(SDLShow *)show {
+ if (self.updatedState.textField1.length > 0) {
+ show.mainField1 = self.updatedState.textField1;
+ show.metadataTags.mainField1 = self.updatedState.textField1Type.length > 0 ? @[self.updatedState.textField1Type] : @[];
+ }
+
+ if (self.updatedState.textField2.length > 0) {
+ show.mainField2 = self.updatedState.textField2;
+ show.metadataTags.mainField2 = self.updatedState.textField2Type.length > 0 ? @[self.updatedState.textField2Type] : @[];
+ }
+
+ if (self.updatedState.textField3.length > 0) {
+ show.mainField3 = self.updatedState.textField3;
+ show.metadataTags.mainField3 = self.updatedState.textField3Type.length > 0 ? @[self.updatedState.textField3Type] : @[];
+ }
+
+ if (self.updatedState.textField4.length > 0) {
+ show.mainField4 = self.updatedState.textField4;
+ show.metadataTags.mainField4 = self.updatedState.textField4Type.length > 0 ? @[self.updatedState.textField4Type] : @[];
+ }
+
+ return show;
+}
+
+- (SDLShow *)sdl_setBlankTextFieldsWithShow:(SDLShow *)show {
+ show.mainField1 = @"";
+ show.mainField2 = @"";
+ show.mainField3 = @"";
+ show.mainField4 = @"";
+ show.mediaTrack = @"";
+ show.templateTitle = @"";
+
+ return show;
+}
+
+#pragma mark - Extraction
+
+- (SDLShow *)sdl_extractTextFromShow:(SDLShow *)show {
+ SDLShow *newShow = [[SDLShow alloc] init];
+ newShow.mainField1 = show.mainField1;
+ newShow.mainField2 = show.mainField2;
+ newShow.mainField3 = show.mainField3;
+ newShow.mainField4 = show.mainField4;
+ newShow.mediaTrack = show.mediaTrack;
+ newShow.templateTitle = show.templateTitle;
+ newShow.metadataTags = show.metadataTags;
+
+ return newShow;
+}
+
+- (void)sdl_updateCurrentScreenDataFromShow:(SDLShow *)show {
+ // If the items are nil, they were not updated, so we can't just set it directly
+ self.currentScreenData.mainField1 = show.mainField1 ?: self.currentScreenData.mainField1;
+ self.currentScreenData.mainField2 = show.mainField2 ?: self.currentScreenData.mainField2;
+ self.currentScreenData.mainField3 = show.mainField3 ?: self.currentScreenData.mainField3;
+ self.currentScreenData.mainField4 = show.mainField4 ?: self.currentScreenData.mainField4;
+ self.currentScreenData.mediaTrack = show.mediaTrack ?: self.currentScreenData.mediaTrack;
+ self.currentScreenData.templateTitle = show.templateTitle ?: self.currentScreenData.templateTitle;
+ self.currentScreenData.metadataTags = show.metadataTags ?: self.currentScreenData.metadataTags;
+ self.currentScreenData.alignment = show.alignment ?: self.currentScreenData.alignment;
+ self.currentScreenData.graphic = show.graphic ?: self.currentScreenData.graphic;
+ self.currentScreenData.secondaryGraphic = show.secondaryGraphic ?: self.currentScreenData.secondaryGraphic;
+}
+
+#pragma mark - Should Update
+
+- (BOOL)sdl_artworkNeedsUpload:(SDLArtwork *)artwork {
+ return (artwork != nil && ![self.fileManager hasUploadedFile:artwork] && !artwork.isStaticIcon);
+}
+
+- (BOOL)sdl_shouldUpdatePrimaryImage {
+ BOOL templateSupportsPrimaryArtwork = [self.currentCapabilities hasImageFieldOfName:SDLImageFieldNameGraphic];
+ BOOL graphicMatchesExisting = [self.currentScreenData.graphic.value isEqualToString:self.updatedState.primaryGraphic.name];
+ BOOL graphicExists = (self.updatedState.primaryGraphic != nil);
+
+ return (templateSupportsPrimaryArtwork && !graphicMatchesExisting && graphicExists);
+}
+
+- (BOOL)sdl_shouldUpdateSecondaryImage {
+ BOOL templateSupportsSecondaryArtwork = [self.currentCapabilities hasImageFieldOfName:SDLImageFieldNameSecondaryGraphic];
+ BOOL graphicMatchesExisting = [self.currentScreenData.secondaryGraphic.value isEqualToString:self.updatedState.secondaryGraphic.name];
+ BOOL graphicExists = (self.updatedState.secondaryGraphic != nil);
+
+ // Cannot detect if there is a secondary image, so we'll just try to detect if there's a primary image and allow it if there is.
+ return (templateSupportsSecondaryArtwork && !graphicMatchesExisting && graphicExists);
+}
+
+- (BOOL)sdl_shouldUpdateMediaTextField {
+ return [self.currentCapabilities hasTextFieldOfName:SDLTextFieldNameMediaTrack];
+}
+
+- (BOOL)sdl_shouldUpdateTitleField {
+ return [self.currentCapabilities hasTextFieldOfName:SDLTextFieldNameTemplateTitle];
+}
+
+- (NSArray<NSString *> *)sdl_findNonNilTextFields {
+ NSMutableArray *array = [NSMutableArray array];
+ (self.updatedState.textField1.length > 0) ? [array addObject:self.updatedState.textField1] : nil;
+ (self.updatedState.textField2.length > 0) ? [array addObject:self.updatedState.textField2] : nil;
+ (self.updatedState.textField3.length > 0) ? [array addObject:self.updatedState.textField3] : nil;
+ (self.updatedState.textField4.length > 0) ? [array addObject:self.updatedState.textField4] : nil;
+
+ return [array copy];
+}
+
+- (NSArray<SDLMetadataType> *)sdl_findNonNilMetadataFields {
+ NSMutableArray *array = [NSMutableArray array];
+ (self.updatedState.textField1Type.length) > 0 ? [array addObject:self.updatedState.textField1Type] : nil;
+ (self.updatedState.textField2Type.length) > 0 ? [array addObject:self.updatedState.textField2Type] : nil;
+ (self.updatedState.textField3Type.length) > 0 ? [array addObject:self.updatedState.textField3Type] : nil;
+ (self.updatedState.textField4Type.length) > 0 ? [array addObject:self.updatedState.textField4Type] : nil;
+
+ return [array copy];
+}
+
+@end