summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Fischer <joeljfischer@gmail.com>2020-09-01 12:56:02 -0400
committerGitHub <noreply@github.com>2020-09-01 12:56:02 -0400
commit36a7790d9075c0a44c0f5902e33ac0a5eeaecc76 (patch)
tree7e44bc2438e65657e96fc12ae54c6fdb12f62115
parent4376899c9d88ad441078143bcaad59001dbc7309 (diff)
parentbc330333b611f841801aa3dadb07e63ace0a6158 (diff)
downloadsdl_ios-36a7790d9075c0a44c0f5902e33ac0a5eeaecc76.tar.gz
Merge pull request #1753 from smartdevicelink/bugfix/issue-1749-refactor-t-g-manager-queues
Refactor Text & Graphic Manager to use queues
-rw-r--r--SmartDeviceLink-iOS.xcodeproj/project.pbxproj36
-rw-r--r--SmartDeviceLink/SDLLogFileModuleMap.m2
-rw-r--r--SmartDeviceLink/SDLTextAndGraphicManager.m492
-rw-r--r--SmartDeviceLink/SDLTextAndGraphicState.h39
-rw-r--r--SmartDeviceLink/SDLTextAndGraphicState.m48
-rw-r--r--SmartDeviceLink/SDLTextAndGraphicUpdateOperation.h42
-rw-r--r--SmartDeviceLink/SDLTextAndGraphicUpdateOperation.m493
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m779
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicUpdateOperationSpec.m929
-rw-r--r--SmartDeviceLinkTests/SDLScreenManagerSpec.m5
10 files changed, 1718 insertions, 1147 deletions
diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
index ae70e6fd9..3ede71e9a 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 */; };
4A899D3624D31FEE007BDD9F /* SDLOnUpdateFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A899D3424D31FEE007BDD9F /* SDLOnUpdateFile.h */; settings = {ATTRIBUTES = (Public, ); }; };
4A899D3724D31FEE007BDD9F /* SDLOnUpdateFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A899D3524D31FEE007BDD9F /* SDLOnUpdateFile.m */; };
4A899D3A24D32273007BDD9F /* SDLOnUpdateSubMenu.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A899D3824D32273007BDD9F /* SDLOnUpdateSubMenu.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -453,6 +457,7 @@
4A99D0132475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A99D0112475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.m */; };
4A9D02BE2497EED400FBE99B /* SDLLifecycleRPCAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A9D02BC2497EED400FBE99B /* SDLLifecycleRPCAdapter.h */; };
4A9D02BF2497EED400FBE99B /* SDLLifecycleRPCAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A9D02BD2497EED400FBE99B /* SDLLifecycleRPCAdapter.m */; };
+ 4AAA5AE324EC686C00837CC7 /* SDLTextAndGraphicUpdateOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAA5AE224EC686C00837CC7 /* SDLTextAndGraphicUpdateOperationSpec.m */; };
4AC68FBA24D33B7A0073FF67 /* SDLOnUpdateFileSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AC68FB924D33B7A0073FF67 /* SDLOnUpdateFileSpec.m */; };
4AC68FBC24D33E5D0073FF67 /* SDLOnUpdateSubMenuSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AC68FBB24D33E5D0073FF67 /* SDLOnUpdateSubMenuSpec.m */; };
4AC68FBE24D340400073FF67 /* SDLDynamicUpdateCapabilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AC68FBD24D340400073FF67 /* SDLDynamicUpdateCapabilitiesSpec.m */; };
@@ -2222,6 +2227,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>"; };
4A899D3424D31FEE007BDD9F /* SDLOnUpdateFile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLOnUpdateFile.h; sourceTree = "<group>"; };
4A899D3524D31FEE007BDD9F /* SDLOnUpdateFile.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLOnUpdateFile.m; sourceTree = "<group>"; };
4A899D3824D32273007BDD9F /* SDLOnUpdateSubMenu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLOnUpdateSubMenu.h; sourceTree = "<group>"; };
@@ -2234,6 +2243,7 @@
4A99D0112475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SDLImageField+ScreenManagerExtensions.m"; sourceTree = "<group>"; };
4A9D02BC2497EED400FBE99B /* SDLLifecycleRPCAdapter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLLifecycleRPCAdapter.h; sourceTree = "<group>"; };
4A9D02BD2497EED400FBE99B /* SDLLifecycleRPCAdapter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLifecycleRPCAdapter.m; sourceTree = "<group>"; };
+ 4AAA5AE224EC686C00837CC7 /* SDLTextAndGraphicUpdateOperationSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLTextAndGraphicUpdateOperationSpec.m; path = DevAPISpecs/SDLTextAndGraphicUpdateOperationSpec.m; sourceTree = "<group>"; };
4AC68FB924D33B7A0073FF67 /* SDLOnUpdateFileSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLOnUpdateFileSpec.m; sourceTree = "<group>"; };
4AC68FBB24D33E5D0073FF67 /* SDLOnUpdateSubMenuSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLOnUpdateSubMenuSpec.m; sourceTree = "<group>"; };
4AC68FBD24D340400073FF67 /* SDLDynamicUpdateCapabilitiesSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLDynamicUpdateCapabilitiesSpec.m; sourceTree = "<group>"; };
@@ -4205,6 +4215,23 @@
name = "Status Manager";
sourceTree = "<group>";
};
+ 4AAA5ADF24EC678A00837CC7 /* Operations */ = {
+ isa = PBXGroup;
+ children = (
+ 4AAA5AE224EC686C00837CC7 /* SDLTextAndGraphicUpdateOperationSpec.m */,
+ );
+ name = Operations;
+ sourceTree = "<group>";
+ };
+ 4AAA5AE424EC690700837CC7 /* Operations */ = {
+ isa = PBXGroup;
+ children = (
+ 4A89AE1624E589D60017EBDC /* SDLTextAndGraphicUpdateOperation.h */,
+ 4A89AE1724E589D60017EBDC /* SDLTextAndGraphicUpdateOperation.m */,
+ );
+ name = Operations;
+ sourceTree = "<group>";
+ };
5D0218EB1A8E795700D1BF62 /* UI */ = {
isa = PBXGroup;
children = (
@@ -4289,8 +4316,11 @@
5D0A737D203F23B30001595D /* Text and Graphic */ = {
isa = PBXGroup;
children = (
+ 4AAA5AE424EC690700837CC7 /* Operations */,
5D0A7372203F0C730001595D /* SDLTextAndGraphicManager.h */,
5D0A7373203F0C730001595D /* SDLTextAndGraphicManager.m */,
+ 4A89AE1A24E595410017EBDC /* SDLTextAndGraphicState.h */,
+ 4A89AE1B24E595410017EBDC /* SDLTextAndGraphicState.m */,
);
name = "Text and Graphic";
sourceTree = "<group>";
@@ -6091,6 +6121,7 @@
5DAD5F8320507DF30025624C /* Text and Graphic */ = {
isa = PBXGroup;
children = (
+ 4AAA5ADF24EC678A00837CC7 /* Operations */,
5DAD5F86205087430025624C /* SDLTextAndGraphicManagerSpec.m */,
);
name = "Text and Graphic";
@@ -6894,9 +6925,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 */,
@@ -7915,6 +7948,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 */,
@@ -8019,6 +8053,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 */,
@@ -8340,6 +8375,7 @@
88B8657924A102AB003491AD /* TestSubscribeButtonObserver.m in Sources */,
5DAE06751BDEC6D600F9B498 /* SDLArtworkSpec.m in Sources */,
5DA23FF01F2FA0FF009C0313 /* SDLControlFramePayloadEndServiceSpec.m in Sources */,
+ 4AAA5AE324EC686C00837CC7 /* SDLTextAndGraphicUpdateOperationSpec.m in Sources */,
162E83591A9BDE8B00906325 /* SDLListFilesResponseSpec.m in Sources */,
162E832A1A9BDE8B00906325 /* SDLDeleteInteractionChoiceSetSpec.m in Sources */,
EE460E0A2066B6E40006EDD3 /* SDLControlFramePayloadRegisterSecondaryTransportNakSpec.m in Sources */,
diff --git a/SmartDeviceLink/SDLLogFileModuleMap.m b/SmartDeviceLink/SDLLogFileModuleMap.m
index 3d25be5d9..403a82258 100644
--- a/SmartDeviceLink/SDLLogFileModuleMap.m
+++ b/SmartDeviceLink/SDLLogFileModuleMap.m
@@ -118,7 +118,7 @@
}
+ (SDLLogFileModule *)sdl_screenManagerTextAndGraphicModule {
- return [SDLLogFileModule moduleWithName:@"Screen/TextAndGraphic" files:[NSSet setWithArray:@[@"SDLTextAndGraphicManager"]]];
+ return [SDLLogFileModule moduleWithName:@"Screen/TextAndGraphic" files:[NSSet setWithArray:@[@"SDLTextAndGraphicManager", @"SDLTextAndGraphicUpdateOperation"]]];
}
+ (SDLLogFileModule *)sdl_screenManagerSoftButtonModule {
diff --git a/SmartDeviceLink/SDLTextAndGraphicManager.m b/SmartDeviceLink/SDLTextAndGraphicManager.m
index 6324e13e3..d7edb7b98 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,18 +121,32 @@ NS_ASSUME_NONNULL_BEGIN
_isDirty = NO;
}
-#pragma mark - Upload / Send
+- (NSOperationQueue *)sdl_newTransactionQueue {
+ NSOperationQueue *queue = [[NSOperationQueue alloc] init];
+ queue.name = @"SDLTextAndGraphicManager Transaction Queue";
+ queue.maxConcurrentOperationCount = 1;
+ queue.qualityOfService = NSQualityOfServiceUserInitiated;
+ queue.suspended = YES;
-- (void)updateWithCompletionHandler:(nullable SDLTextAndGraphicUpdateCompletionHandler)handler {
- if (self.isBatchingUpdates) { return; }
+ return queue;
+}
- // Don't send if we're in HMI NONE
- if (self.currentLevel == nil || [self.currentLevel isEqualToString:SDLHMILevelNone]) {
- self.waitingOnHMILevelUpdateToUpdate = YES;
- return;
+/// Suspend the queue if the window capabilities are nil (we assume that text and graphics are not supported yet)
+/// 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 {
- self.waitingOnHMILevelUpdateToUpdate = NO;
+ SDLLogD(@"Starting the transaction queue");
+ self.transactionQueue.suspended = NO;
}
+}
+
+#pragma mark - Upload / Send
+
+- (void)updateWithCompletionHandler:(nullable SDLTextAndGraphicUpdateCompletionHandler)handler {
+ if (self.isBatchingUpdates) { return; }
if (self.isDirty) {
self.isDirty = NO;
@@ -151,402 +156,43 @@ 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;
- }
-
- return;
+ if (self.transactionQueue.operationCount > 0) {
+ SDLLogV(@"Transactions already exist, cancelling them");
+ [self.transactionQueue cancelAllOperations];
}
- 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;
- }
+ __weak typeof(self) weakSelf = self;
+ SDLTextAndGraphicUpdateOperation *updateOperation = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager currentCapabilities:self.windowCapability currentScreenData:self.currentScreenData newState:[self currentState] currentScreenDataUpdatedHandler:^(SDLShow * _Nonnull newScreenData) {
+ weakSelf.currentScreenData = newScreenData;
+ [weakSelf sdl_updatePendingOperationsWithNewScreenData:newScreenData];
+ } updateCompletionHandler:handler];
- if (strongSelf.hasQueuedUpdate) {
- SDLLogV(@"Queued update exists, sending another update");
- [strongSelf sdl_updateWithCompletionHandler:[strongSelf.queuedUpdateHandler copy]];
- strongSelf.queuedUpdateHandler = nil;
- strongSelf.hasQueuedUpdate = NO;
+ __weak typeof(updateOperation) weakOp = updateOperation;
+ updateOperation.completionBlock = ^{
+ if (weakOp.error != nil) {
+ SDLLogE(@"Update operation failed with error: %@", weakOp.error);
}
- }];
+ };
+ [self.transactionQueue addOperation:updateOperation];
}
-- (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];
- }
+- (void)sdl_updatePendingOperationsWithNewScreenData:(SDLShow *)newScreenData {
+ for (NSOperation *operation in self.transactionQueue.operations) {
+ if (![operation isMemberOfClass:SDLTextAndGraphicUpdateOperation.class] || operation.isExecuting) { continue; }
+ SDLTextAndGraphicUpdateOperation *updateOp = (SDLTextAndGraphicUpdateOperation *)operation;
- if (artworksToUpload.count == 0
- && (self.primaryGraphic.isStaticIcon || self.secondaryGraphic.isStaticIcon)) {
- SDLLogD(@"Upload attempted on static icons, sending them without upload instead");
- handler(nil);
+ updateOp.currentScreenData = newScreenData;
}
-
- [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;
- }
+#pragma mark - Convert to State
- 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;
-}
-
-- (SDLShow *)sdl_assembleFourLineShowText:(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] : @[];
- }
-
- 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;
-}
-
-#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;
- newShow.secondaryGraphic = show.secondaryGraphic;
-
- return newShow;
-}
-
-- (nullable SDLShow *)sdl_createImageOnlyShowWithPrimaryArtwork:(nullable SDLArtwork *)primaryArtwork secondaryArtwork:(nullable SDLArtwork *)secondaryArtwork {
- SDLShow *newShow = [[SDLShow alloc] init];
- newShow.graphic = ![self sdl_artworkNeedsUpload:primaryArtwork] ? primaryArtwork.imageRPC : nil;
- newShow.secondaryGraphic = ![self sdl_artworkNeedsUpload:secondaryArtwork] ? secondaryArtwork.imageRPC : nil;
-
- if (newShow.graphic == nil && newShow.secondaryGraphic == nil) {
- SDLLogV(@"No graphics to upload");
- return nil;
- }
-
- 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;
+- (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 - 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;
@@ -557,16 +203,6 @@ NS_ASSUME_NONNULL_BEGIN
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);
@@ -574,24 +210,8 @@ NS_ASSUME_NONNULL_BEGIN
return hasTextFields || hasImageFields;
}
-#pragma mark - Equality
-
-- (BOOL)sdl_showImages:(SDLShow *)show isEqualToShowImages:(SDLShow *)show2 {
- BOOL same = NO;
- same = ((show.graphic.value == nil && show.graphic.value == nil)
- || [show.graphic.value isEqualToString:show2.graphic.value]);
- if (!same) { return NO; }
-
- same = ((show.secondaryGraphic.value == nil && show.secondaryGraphic.value == nil)
- || [show.secondaryGraphic.value isEqualToString:show2.secondaryGraphic.value]);
-
- return same;
-}
-
#pragma mark - Getters / Setters
-#pragma mark - Setters
-
- (void)setTextField1:(nullable NSString *)textField1 {
_textField1 = textField1;
_isDirty = YES;
@@ -697,10 +317,6 @@ NS_ASSUME_NONNULL_BEGIN
}
}
-- (BOOL)hasQueuedUpdate {
- return (_hasQueuedUpdate || _queuedUpdateHandler != nil);
-}
-
- (nullable SDLArtwork *)blankArtwork {
if (_blankArtwork != nil) {
return _blankArtwork;
@@ -720,10 +336,11 @@ NS_ASSUME_NONNULL_BEGIN
- (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability {
// we won't use the object in the parameter but the convenience method of the system capability manager
self.windowCapability = self.systemCapabilityManager.defaultMainWindowCapability;
+ [self sdl_updateTransactionQueueSuspended];
// Auto-send an updated show
if ([self sdl_hasData]) {
- [self updateWithCompletionHandler:nil];
+ [self sdl_updateWithCompletionHandler:nil];
}
}
@@ -734,13 +351,8 @@ NS_ASSUME_NONNULL_BEGIN
return;
}
- SDLHMILevel oldLevel = self.currentLevel;
self.currentLevel = hmiStatus.hmiLevel;
-
- // Auto-send an updated show if we were in NONE and now we are not
- if ([oldLevel isEqualToString:SDLHMILevelNone] && ![self.currentLevel isEqualToString:SDLHMILevelNone] && self.waitingOnHMILevelUpdateToUpdate) {
- [self sdl_updateWithCompletionHandler:nil];
- }
+ [self sdl_updateTransactionQueueSuspended];
}
@end
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..a44b63810
--- /dev/null
+++ b/SmartDeviceLink/SDLTextAndGraphicState.m
@@ -0,0 +1,48 @@
+//
+// SDLTextAndGraphicState.m
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 8/13/20.
+// Copyright © 2020 smartdevicelink. All rights reserved.
+//
+
+#import "SDLTextAndGraphicState.h"
+
+#import "SDLArtwork.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@implementation SDLTextAndGraphicState
+
+- (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 {
+ 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;
+}
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"Text Field 1: %@, 2: %@, 3: %@, 4: %@, media track: %@, title: %@, alignment: %@, text 1 type: %@, 2: %@, 3: %@, 4: %@, primary graphic: %@, secondary graphic: %@", _textField1, _textField2, _textField3, _textField4, _mediaTrackTextField, _title, _alignment, _textField1Type, _textField2Type, _textField3Type, _textField4Type, _primaryGraphic, _secondaryGraphic];
+}
+
+- (id)copyWithZone:(nullable NSZone *)zone {
+ return [[SDLTextAndGraphicState allocWithZone:zone] initWithTextField1:[_textField1 copy] textField2:[_textField2 copy] textField3:[_textField3 copy] textField4:[_textField4 copy] mediaText:[_mediaTrackTextField copy] title:[_title copy] primaryGraphic:[_primaryGraphic copy] secondaryGraphic:[_secondaryGraphic copy] alignment:[_alignment copy] textField1Type:[_textField1Type copy] textField2Type:[_textField2Type copy] textField3Type:[_textField3Type copy] textField4Type:[_textField4Type copy]];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLTextAndGraphicUpdateOperation.h b/SmartDeviceLink/SDLTextAndGraphicUpdateOperation.h
new file mode 100644
index 000000000..cb563760d
--- /dev/null
+++ b/SmartDeviceLink/SDLTextAndGraphicUpdateOperation.h
@@ -0,0 +1,42 @@
+//
+// 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);
+typedef void(^CurrentDataUpdatedHandler)(SDLShow *newScreenData);
+
+@interface SDLTextAndGraphicUpdateOperation : SDLAsynchronousOperation
+
+/// The current state of the screen in Show form. This is passed as a dependency in the init but it may need to be updated if a previous operation updated the state of the screen. This will be updated with new screen data when this operation sends successful shows.
+@property (strong, nonatomic) SDLShow *currentScreenData;
+
+/// 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 currentScreenDataUpdatedHandler:(nullable CurrentDataUpdatedHandler)currentDataUpdatedHandler 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..dd70bda0f
--- /dev/null
+++ b/SmartDeviceLink/SDLTextAndGraphicUpdateOperation.m
@@ -0,0 +1,493 @@
+//
+// 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 "SDLError.h"
+#import "SDLFileManager.h"
+#import "SDLGlobals.h"
+#import "SDLImage.h"
+#import "SDLLogMacros.h"
+#import "SDLMetadataTags.h"
+#import "SDLShow.h"
+#import "SDLTextAndGraphicState.h"
+#import "SDLVersion.h"
+#import "SDLWindowCapability.h"
+#import "SDLWindowCapability+ScreenManagerExtensions.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SDLTextAndGraphicUpdateOperation ()
+
+@property (weak, nonatomic) id<SDLConnectionManagerType> connectionManager;
+@property (weak, nonatomic) SDLFileManager *fileManager;
+@property (strong, nonatomic) SDLWindowCapability *currentCapabilities;
+@property (strong, nonatomic) SDLTextAndGraphicState *updatedState;
+
+@property (copy, nonatomic, nullable) CurrentDataUpdatedHandler currentDataUpdatedHandler;
+@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 currentScreenDataUpdatedHandler:(nullable CurrentDataUpdatedHandler)currentDataUpdatedHandler updateCompletionHandler:(nullable SDLTextAndGraphicUpdateCompletionHandler)updateCompletionHandler {
+ self = [self init];
+ if (!self) { return nil; }
+
+ _connectionManager = connectionManager;
+ _fileManager = fileManager;
+ _currentCapabilities = currentCapabilities;
+ _currentScreenData = currentData;
+ _updatedState = newState;
+ _currentDataUpdatedHandler = currentDataUpdatedHandler;
+ _updateCompletionHandler = updateCompletionHandler;
+
+ return self;
+}
+
+- (void)start {
+ [super start];
+ if (self.cancelled) {
+ // Make sure the update handler is called
+ self.internalError = [NSError sdl_textAndGraphicManager_pendingUpdateSuperseded];
+ [self finishOperation];
+ 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 = [self sdl_assembleShowText:fullShow];
+ fullShow = [self sdl_assembleShowImages:fullShow];
+
+ __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 sdl_sendShow:[self sdl_extractTextFromShow:fullShow] withHandler:^(NSError * _Nullable error) {
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ if (error != nil) {
+ strongSelf.internalError = error;
+ }
+ [strongSelf finishOperation];
+ }];
+ } 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
+ [self sdl_sendShow:fullShow withHandler:^(NSError * _Nullable error) {
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ if (error != nil) {
+ strongSelf.internalError = error;
+ }
+ [strongSelf finishOperation];
+ }];
+ } 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 sdl_sendShow:[self sdl_extractTextFromShow:fullShow] withHandler:^(NSError * _Nullable error) {
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ if (self.cancelled) {
+ [strongSelf finishOperation];
+ }
+
+ [strongSelf sdl_uploadImagesAndSendWhenDone:^(NSError * _Nullable error) {
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ if (error != nil) {
+ strongSelf.internalError = error;
+ }
+ [strongSelf finishOperation];
+ }];
+ }];
+ }
+}
+
+#pragma mark - Send Show
+
+- (void)sdl_sendShow:(SDLShow *)show withHandler:(void (^)(NSError *_Nullable error))handler {
+ __weak typeof(self)weakSelf = self;
+ [self.connectionManager sendConnectionRequest:show withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ SDLLogD(@"Text and Graphic update completed");
+
+ if (response.success) {
+ [strongSelf sdl_updateCurrentScreenDataFromShow:(SDLShow *)request];
+ }
+
+ handler(error);
+ }];
+}
+
+#pragma mark - Uploading Images
+
+- (void)sdl_uploadImagesAndSendWhenDone:(void (^)(NSError *_Nullable error))handler {
+ __weak typeof(self)weakSelf = self;
+ [self sdl_uploadImagesWithCompletionHandler:^(NSError *_Nullable error) {
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+
+ SDLShow *showWithGraphics = [self sdl_createImageOnlyShowWithPrimaryArtwork:self.updatedState.primaryGraphic secondaryArtwork:self.updatedState.secondaryGraphic];
+ if (showWithGraphics != nil) {
+ SDLLogD(@"Sending update with the successfully uploaded images");
+ [strongSelf sdl_sendShow:showWithGraphics withHandler:^(NSError * _Nullable error) {
+ return handler(error);
+ }];
+ } else {
+ SDLLogW(@"All images failed to upload. No graphics to show, skipping update.");
+ return handler(error);
+ }
+ }];
+}
+
+- (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);
+ }
+
+ __weak typeof(self) weakSelf = self;
+ [self.fileManager uploadArtworks:artworksToUpload progressHandler:^BOOL(NSString * _Nonnull artworkName, float uploadPercentage, NSError * _Nullable error) {
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ if (strongSelf.isCancelled) {
+ [strongSelf finishOperation];
+ return NO;
+ }
+
+ return YES;
+ } 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;
+}
+
+- (nullable SDLShow *)sdl_createImageOnlyShowWithPrimaryArtwork:(nullable SDLArtwork *)primaryArtwork secondaryArtwork:(nullable SDLArtwork *)secondaryArtwork {
+ SDLShow *newShow = [[SDLShow alloc] init];
+ newShow.graphic = ![self sdl_artworkNeedsUpload:primaryArtwork] ? primaryArtwork.imageRPC : nil;
+ newShow.secondaryGraphic = ![self sdl_artworkNeedsUpload:secondaryArtwork] ? secondaryArtwork.imageRPC : nil;
+
+ if (newShow.graphic == nil && newShow.secondaryGraphic == nil) {
+ SDLLogV(@"No graphics to upload");
+ return nil;
+ }
+
+ return newShow;
+}
+
+#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;
+
+ 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;
+ show.metadataTags.mainField1 = [metadataArray copy];
+
+ 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 = @"";
+ show.metadataTags = [[SDLMetadataTags alloc] init];
+
+ 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;
+ newShow.alignment = show.alignment;
+
+ 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;
+
+ if (self.currentDataUpdatedHandler != nil) {
+ self.currentDataUpdatedHandler(self.currentScreenData);
+ }
+}
+
+#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 below v5.0, so we'll just try to detect if the primary image is allowed and allow the secondary image if it is.
+ if ([[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithMajor:5 minor:0 patch:0]]) {
+ return (templateSupportsSecondaryArtwork && !graphicMatchesExisting && graphicExists);
+ } else {
+ return ([self.currentCapabilities hasImageFieldOfName:SDLImageFieldNameGraphic] && !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];
+}
+
+#pragma mark - Operation Overrides
+
+- (void)finishOperation {
+ SDLLogV(@"Finishing text and graphic update operation");
+ if (self.updateCompletionHandler != nil) {
+ self.updateCompletionHandler(self.error);
+ }
+ [super finishOperation];
+}
+
+- (nullable NSString *)name {
+ return @"com.sdl.textandgraphic.update";
+}
+
+- (NSOperationQueuePriority)queuePriority {
+ return NSOperationQueuePriorityNormal;
+}
+
+- (nullable NSError *)error {
+ return self.internalError;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m
index 99aa9dbb5..b8b34e046 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m
@@ -11,6 +11,7 @@
#import "SDLPutFileResponse.h"
#import "SDLShow.h"
#import "SDLTextAndGraphicManager.h"
+#import "SDLTextAndGraphicUpdateOperation.h"
#import "SDLTextField.h"
#import "SDLSystemCapabilityManager.h"
#import "SDLWindowCapability.h"
@@ -21,15 +22,11 @@
// Dependencies
@property (weak, nonatomic) id<SDLConnectionManagerType> connectionManager;
@property (weak, nonatomic) SDLFileManager *fileManager;
+@property (weak, nonatomic) SDLSystemCapabilityManager *systemCapabilityManager;
@property (strong, nonatomic) SDLShow *currentScreenData;
-@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;
@@ -53,7 +50,6 @@ describe(@"text and graphic manager", ^{
__block NSString *testString = @"some string";
__block NSString *testArtworkName = @"some artwork name";
__block SDLArtwork *testArtwork = [[SDLArtwork alloc] initWithData:[@"Test data" dataUsingEncoding:NSUTF8StringEncoding] name:testArtworkName fileExtension:@"png" persistent:NO];
- __block SDLArtwork *testStaticIcon = [SDLArtwork artworkWithStaticIcon:SDLStaticIconNameDate];
beforeEach(^{
mockFileManager = OCMClassMock([SDLFileManager class]);
@@ -62,6 +58,7 @@ describe(@"text and graphic manager", ^{
[testManager start];
});
+ // should instantiate correctly
it(@"should instantiate correctly", ^{
expect(testManager.connectionManager).to(equal(mockConnectionManager));
expect(testManager.fileManager).to(equal(mockFileManager));
@@ -81,48 +78,66 @@ describe(@"text and graphic manager", ^{
expect(testManager.textField4Type).to(beNil());
expect(testManager.currentScreenData).to(equal([[SDLShow alloc] init]));
- expect(testManager.inProgressUpdate).to(beNil());
- expect(testManager.queuedImageUpdate).to(beNil());
- expect(testManager.hasQueuedUpdate).to(beFalse());
+ expect(testManager.transactionQueue).toNot(beNil());
expect(testManager.windowCapability).to(beNil());
expect(testManager.currentLevel).to(equal(SDLHMILevelNone));
expect(testManager.blankArtwork).toNot(beNil());
expect(testManager.isDirty).to(beFalse());
});
+ // setting setters
describe(@"setting setters", ^{
beforeEach(^{
testManager.currentLevel = SDLHMILevelFull;
});
+ // when in HMI NONE
context(@"when in HMI NONE", ^{
beforeEach(^{
testManager.currentLevel = SDLHMILevelNone;
});
- it(@"should not set text field 1", ^{
+ it(@"should set text field 1 but be suspended", ^{
testManager.textField1 = testString;
expect(testManager.textField1).to(equal(testString));
- expect(testManager.inProgressUpdate).to(beNil());
- expect(testManager.isDirty).to(beTrue());
+ expect(testManager.transactionQueue.isSuspended).to(beTrue());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
});
});
+ // when no HMI level has been received
context(@"when no HMI level has been received", ^{
beforeEach(^{
testManager.currentLevel = nil;
});
- it(@"should not set text field 1", ^{
+ it(@"should set text field 1 but be suspended", ^{
testManager.textField1 = testString;
expect(testManager.textField1).to(equal(testString));
- expect(testManager.inProgressUpdate).to(beNil());
- expect(testManager.isDirty).to(beTrue());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
+ expect(testManager.transactionQueue.isSuspended).to(beTrue());
+ });
+ });
+
+ // when previous updates have bene cancelled
+ context(@"when previous updates have bene cancelled", ^{
+ beforeEach(^{
+ testManager.textField1 = @"Hello";
+
+ // This should cancel the first operation
+ testManager.textField2 = @"Goodbye";
+ });
+
+ it(@"should properly queue the new update", ^{
+ expect(testManager.transactionQueue.isSuspended).to(beTrue());
+ expect(testManager.transactionQueue.operationCount).to(equal(2));
+ expect(testManager.transactionQueue.operations[0].cancelled).to(beTrue());
});
});
+ // while batching
context(@"while batching", ^{
beforeEach(^{
testManager.batchUpdates = YES;
@@ -132,7 +147,7 @@ describe(@"text and graphic manager", ^{
testManager.textField1 = testString;
expect(testManager.textField1).to(equal(testString));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
@@ -140,7 +155,7 @@ describe(@"text and graphic manager", ^{
testManager.textField2 = testString;
expect(testManager.textField2).to(equal(testString));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
@@ -148,7 +163,7 @@ describe(@"text and graphic manager", ^{
testManager.textField3 = testString;
expect(testManager.textField3).to(equal(testString));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
@@ -156,7 +171,7 @@ describe(@"text and graphic manager", ^{
testManager.textField4 = testString;
expect(testManager.textField4).to(equal(testString));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
@@ -164,7 +179,7 @@ describe(@"text and graphic manager", ^{
testManager.mediaTrackTextField = testString;
expect(testManager.mediaTrackTextField).to(equal(testString));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
@@ -172,7 +187,7 @@ describe(@"text and graphic manager", ^{
testManager.title = testString;
expect(testManager.title).to(equal(testString));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
@@ -180,7 +195,7 @@ describe(@"text and graphic manager", ^{
testManager.primaryGraphic = testArtwork;
expect(testManager.primaryGraphic.name).to(equal(testArtworkName));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
@@ -188,7 +203,7 @@ describe(@"text and graphic manager", ^{
testManager.secondaryGraphic = testArtwork;
expect(testManager.secondaryGraphic.name).to(equal(testArtworkName));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
@@ -196,7 +211,7 @@ describe(@"text and graphic manager", ^{
testManager.alignment = SDLTextAlignmentLeft;
expect(testManager.alignment).to(equal(SDLTextAlignmentLeft));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
@@ -204,7 +219,7 @@ describe(@"text and graphic manager", ^{
testManager.textField1Type = SDLMetadataTypeMediaAlbum;
expect(testManager.textField1Type).to(equal(SDLMetadataTypeMediaAlbum));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
@@ -212,7 +227,7 @@ describe(@"text and graphic manager", ^{
testManager.textField2Type = SDLMetadataTypeMediaAlbum;
expect(testManager.textField2Type).to(equal(SDLMetadataTypeMediaAlbum));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
@@ -220,7 +235,7 @@ describe(@"text and graphic manager", ^{
testManager.textField3Type = SDLMetadataTypeMediaAlbum;
expect(testManager.textField3Type).to(equal(SDLMetadataTypeMediaAlbum));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
@@ -228,11 +243,12 @@ describe(@"text and graphic manager", ^{
testManager.textField4Type = SDLMetadataTypeMediaAlbum;
expect(testManager.textField4Type).to(equal(SDLMetadataTypeMediaAlbum));
- expect(testManager.inProgressUpdate).to(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
expect(testManager.isDirty).to(beTrue());
});
});
+ // while not batching
context(@"while not batching", ^{
beforeEach(^{
testManager.batchUpdates = NO;
@@ -242,7 +258,7 @@ describe(@"text and graphic manager", ^{
testManager.textField1 = testString;
expect(testManager.textField1).to(equal(testString));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
@@ -250,7 +266,7 @@ describe(@"text and graphic manager", ^{
testManager.textField2 = testString;
expect(testManager.textField2).to(equal(testString));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
@@ -258,7 +274,7 @@ describe(@"text and graphic manager", ^{
testManager.textField3 = testString;
expect(testManager.textField3).to(equal(testString));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
@@ -266,7 +282,7 @@ describe(@"text and graphic manager", ^{
testManager.textField4 = testString;
expect(testManager.textField4).to(equal(testString));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
@@ -274,7 +290,7 @@ describe(@"text and graphic manager", ^{
testManager.mediaTrackTextField = testString;
expect(testManager.mediaTrackTextField).to(equal(testString));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
@@ -282,7 +298,7 @@ describe(@"text and graphic manager", ^{
testManager.title = testString;
expect(testManager.title).to(equal(testString));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
@@ -290,7 +306,7 @@ describe(@"text and graphic manager", ^{
testManager.primaryGraphic = testArtwork;
expect(testManager.primaryGraphic.name).to(equal(testArtworkName));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
@@ -298,7 +314,7 @@ describe(@"text and graphic manager", ^{
testManager.secondaryGraphic = testArtwork;
expect(testManager.secondaryGraphic.name).to(equal(testArtworkName));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
@@ -306,7 +322,7 @@ describe(@"text and graphic manager", ^{
testManager.alignment = SDLTextAlignmentLeft;
expect(testManager.alignment).to(equal(SDLTextAlignmentLeft));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
@@ -314,7 +330,7 @@ describe(@"text and graphic manager", ^{
testManager.textField1Type = SDLMetadataTypeMediaAlbum;
expect(testManager.textField1Type).to(equal(SDLMetadataTypeMediaAlbum));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
@@ -322,7 +338,7 @@ describe(@"text and graphic manager", ^{
testManager.textField2Type = SDLMetadataTypeMediaAlbum;
expect(testManager.textField2Type).to(equal(SDLMetadataTypeMediaAlbum));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
@@ -330,7 +346,7 @@ describe(@"text and graphic manager", ^{
testManager.textField3Type = SDLMetadataTypeMediaAlbum;
expect(testManager.textField3Type).to(equal(SDLMetadataTypeMediaAlbum));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
@@ -338,12 +354,13 @@ describe(@"text and graphic manager", ^{
testManager.textField4Type = SDLMetadataTypeMediaAlbum;
expect(testManager.textField4Type).to(equal(SDLMetadataTypeMediaAlbum));
- expect(testManager.inProgressUpdate).toNot(beNil());
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
expect(testManager.isDirty).to(beFalse());
});
});
});
+ // batching an update
describe(@"batching an update", ^{
NSString *textLine1 = @"line1";
NSString *textLine2 = @"line2";
@@ -361,670 +378,29 @@ describe(@"text and graphic manager", ^{
testManager.currentLevel = SDLHMILevelFull;
testManager.batchUpdates = YES;
- testManager.textField1 = nil;
- testManager.textField2 = nil;
- testManager.textField3 = nil;
- testManager.textField4 = nil;
- testManager.mediaTrackTextField = nil;
- testManager.title = nil;
- testManager.textField1Type = nil;
- testManager.textField2Type = nil;
- testManager.textField3Type = nil;
- testManager.textField4Type = nil;
- });
-
- context(@"when textFields are nil", ^{
- beforeEach(^{
- testManager.windowCapability = [[SDLWindowCapability alloc] init];
- });
-
- it(@"should send nothing", ^{
- testManager.mediaTrackTextField = textMediaTrack;
- testManager.title = textTitle;
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField3 = textLine3;
- testManager.textField4 = textLine4;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mediaTrack).toNot(equal(textMediaTrack));
- expect(testManager.inProgressUpdate.templateTitle).toNot(equal(textTitle));
- expect(testManager.inProgressUpdate.mainField1).toNot(equal(textLine1));
- expect(testManager.inProgressUpdate.mainField2).toNot(equal(textLine2));
- expect(testManager.inProgressUpdate.mainField3).toNot(equal(textLine3));
- expect(testManager.inProgressUpdate.mainField4).toNot(equal(textLine4));
- });
- });
-
- context(@"with one line available", ^{
- beforeEach(^{
- testManager.windowCapability = [[SDLWindowCapability alloc] init];
- SDLTextField *lineOneField = [[SDLTextField alloc] init];
- lineOneField.name = SDLTextFieldNameMainField1;
- testManager.windowCapability.textFields = @[lineOneField];
- });
-
- it(@"should not set mediatrack", ^{
- testManager.mediaTrackTextField = textMediaTrack;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mediaTrack).toNot(equal(textMediaTrack));
- expect(testManager.inProgressUpdate.mainField1).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(beNil());
- });
-
- it(@"should not set title", ^{
- testManager.title = textTitle;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.templateTitle).toNot(equal(textTitle));
- expect(testManager.inProgressUpdate.mainField1).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(beNil());
- });
-
- it(@"should format a one line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField1Type = line1Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal(textLine1));
- expect(testManager.inProgressUpdate.metadataTags.mainField1.firstObject).to(equal(line1Type));
- expect(testManager.inProgressUpdate.mainField2).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(beNil());
- });
-
- it(@"should format a two line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField1Type = line1Type;
- testManager.textField2Type = line2Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal([NSString stringWithFormat:@"%@ - %@", textLine1, textLine2]));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[0]).to(equal(line1Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[1]).to(equal(line2Type));
- expect(testManager.inProgressUpdate.mainField2).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(beNil());
- });
-
- it(@"should format a three line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField3 = textLine3;
- testManager.textField1Type = line1Type;
- testManager.textField2Type = line2Type;
- testManager.textField3Type = line3Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal([NSString stringWithFormat:@"%@ - %@ - %@", textLine1, textLine2, textLine3]));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[0]).to(equal(line1Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[1]).to(equal(line2Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[2]).to(equal(line3Type));
- expect(testManager.inProgressUpdate.mainField2).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(beNil());
- });
-
- it(@"should format a four line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField3 = textLine3;
- testManager.textField4 = textLine4;
- testManager.textField1Type = line1Type;
- testManager.textField2Type = line2Type;
- testManager.textField3Type = line3Type;
- testManager.textField4Type = line4Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal([NSString stringWithFormat:@"%@ - %@ - %@ - %@", textLine1, textLine2, textLine3, textLine4]));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[0]).to(equal(line1Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[1]).to(equal(line2Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[2]).to(equal(line3Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[3]).to(equal(line4Type));
- expect(testManager.inProgressUpdate.mainField2).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(beNil());
- });
-
- context(@"when media track and title are available", ^{
- beforeEach(^{
- NSMutableArray<SDLTextField *> *existingFieldsMutable = [testManager.windowCapability.textFields mutableCopy];
- SDLTextField *mediaTrack = [[SDLTextField alloc] init];
- mediaTrack.name = SDLTextFieldNameMediaTrack;
- [existingFieldsMutable addObject:mediaTrack];
-
- SDLTextField *title = [[SDLTextField alloc] init];
- title.name = SDLTextFieldNameTemplateTitle;
- [existingFieldsMutable addObject:title];
- testManager.windowCapability.textFields = [existingFieldsMutable copy];
- });
-
- it(@"should set media track and title properly", ^{
- testManager.mediaTrackTextField = textMediaTrack;
- testManager.title = textTitle;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mediaTrack).to(equal(textMediaTrack));
- expect(testManager.inProgressUpdate.templateTitle).to(equal(textTitle));
- });
- });
- });
-
- context(@"with two lines available", ^{
- beforeEach(^{
- testManager.windowCapability = [[SDLWindowCapability alloc] init];
- SDLTextField *lineTwoField = [[SDLTextField alloc] init];
- lineTwoField.name = SDLTextFieldNameMainField2;
- testManager.windowCapability.textFields = @[lineTwoField];
- });
-
- it(@"should not set mediatrack", ^{
- testManager.mediaTrackTextField = textMediaTrack;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mediaTrack).toNot(equal(textMediaTrack));
- expect(testManager.inProgressUpdate.mainField1).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(beNil());
- });
-
- it(@"should not set title", ^{
- testManager.title = textTitle;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.templateTitle).toNot(equal(textTitle));
- expect(testManager.inProgressUpdate.mainField1).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(beNil());
- });
-
- it(@"should format a one line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField1Type = line1Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal(textLine1));
- expect(testManager.inProgressUpdate.metadataTags.mainField1.firstObject).to(equal(line1Type));
- expect(testManager.inProgressUpdate.mainField2).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(beNil());
- });
-
- it(@"should format a two line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField1Type = line1Type;
- testManager.textField2Type = line2Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal(textLine1));
- expect(testManager.inProgressUpdate.metadataTags.mainField1.firstObject).to(equal(line1Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(haveCount(1));
- expect(testManager.inProgressUpdate.mainField2).to(equal(textLine2));
- expect(testManager.inProgressUpdate.metadataTags.mainField2[0]).to(equal(line2Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(haveCount(1));
- expect(testManager.inProgressUpdate.mainField3).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField3).to(beNil());
- });
-
- it(@"should format a three line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField3 = textLine3;
- testManager.textField1Type = line1Type;
- testManager.textField2Type = line2Type;
- testManager.textField3Type = line3Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal([NSString stringWithFormat:@"%@ - %@", textLine1, textLine2]));
- expect(testManager.inProgressUpdate.mainField2).to(equal(textLine3));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[0]).to(equal(line1Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[1]).to(equal(line2Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(haveCount(2));
- expect(testManager.inProgressUpdate.metadataTags.mainField2[0]).to(equal(line3Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(haveCount(1));
- expect(testManager.inProgressUpdate.mainField3).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField3).to(beNil());
- });
-
- it(@"should format a four line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField3 = textLine3;
- testManager.textField4 = textLine4;
- testManager.textField1Type = line1Type;
- testManager.textField2Type = line2Type;
- testManager.textField3Type = line3Type;
- testManager.textField4Type = line4Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal([NSString stringWithFormat:@"%@ - %@", textLine1, textLine2]));
- expect(testManager.inProgressUpdate.mainField2).to(equal([NSString stringWithFormat:@"%@ - %@", textLine3, textLine4]));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[0]).to(equal(line1Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[1]).to(equal(line2Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(haveCount(2));
- expect(testManager.inProgressUpdate.metadataTags.mainField2[0]).to(equal(line3Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField2[1]).to(equal(line4Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(haveCount(2));
- expect(testManager.inProgressUpdate.mainField3).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField3).to(beNil());
- });
- });
-
- context(@"with three lines available", ^{
- beforeEach(^{
- testManager.windowCapability = [[SDLWindowCapability alloc] init];
- SDLTextField *lineThreeField = [[SDLTextField alloc] init];
- lineThreeField.name = SDLTextFieldNameMainField3;
- testManager.windowCapability.textFields = @[lineThreeField];
- });
-
- it(@"should not set mediatrack", ^{
- testManager.mediaTrackTextField = textMediaTrack;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mediaTrack).toNot(equal(textMediaTrack));
- expect(testManager.inProgressUpdate.mainField1).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(beNil());
- });
-
- it(@"should not set title", ^{
- testManager.title = textTitle;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.templateTitle).toNot(equal(textTitle));
- expect(testManager.inProgressUpdate.mainField1).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(beNil());
- });
-
- it(@"should format a one line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField1Type = line1Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal(textLine1));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[0]).to(equal(line1Type));
- expect(testManager.inProgressUpdate.mainField2).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(beNil());
- });
-
- it(@"should format a two line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField1Type = line1Type;
- testManager.textField2Type = line2Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal(textLine1));
- expect(testManager.inProgressUpdate.metadataTags.mainField1.firstObject).to(equal(line1Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(haveCount(1));
- expect(testManager.inProgressUpdate.mainField2).to(equal(textLine2));
- expect(testManager.inProgressUpdate.metadataTags.mainField2[0]).to(equal(line2Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(haveCount(1));
- expect(testManager.inProgressUpdate.mainField3).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField3).to(beNil());
- });
-
- it(@"should format a three line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField3 = textLine3;
- testManager.textField1Type = line1Type;
- testManager.textField2Type = line2Type;
- testManager.textField3Type = line3Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal(textLine1));
- expect(testManager.inProgressUpdate.mainField2).to(equal(textLine2));
- expect(testManager.inProgressUpdate.mainField3).to(equal(textLine3));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[0]).to(equal(line1Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(haveCount(1));
- expect(testManager.inProgressUpdate.metadataTags.mainField2[0]).to(equal(line2Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(haveCount(1));
- expect(testManager.inProgressUpdate.metadataTags.mainField3[0]).to(equal(line3Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField3).to(haveCount(1));
- expect(testManager.inProgressUpdate.mainField4).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField4).to(beNil());
- });
-
- it(@"should format a four line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField3 = textLine3;
- testManager.textField4 = textLine4;
- testManager.textField1Type = line1Type;
- testManager.textField2Type = line2Type;
- testManager.textField3Type = line3Type;
- testManager.textField4Type = line4Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal(textLine1));
- expect(testManager.inProgressUpdate.mainField2).to(equal(textLine2));
- expect(testManager.inProgressUpdate.mainField3).to(equal([NSString stringWithFormat:@"%@ - %@", textLine3, textLine4]));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[0]).to(equal(line1Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(haveCount(1));
- expect(testManager.inProgressUpdate.metadataTags.mainField2[0]).to(equal(line2Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(haveCount(1));
- expect(testManager.inProgressUpdate.metadataTags.mainField3[0]).to(equal(line3Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField3[1]).to(equal(line4Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField3).to(haveCount(2));
- expect(testManager.inProgressUpdate.mainField4).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField4).to(beNil());
- });
- });
-
- context(@"with four lines available", ^{
- beforeEach(^{
- testManager.windowCapability = [[SDLWindowCapability alloc] init];
- SDLTextField *lineFourField = [[SDLTextField alloc] init];
- lineFourField.name = SDLTextFieldNameMainField4;
- testManager.windowCapability.textFields = @[lineFourField];
- });
-
- it(@"should not set mediatrack", ^{
- testManager.mediaTrackTextField = textMediaTrack;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mediaTrack).toNot(equal(textMediaTrack));
- expect(testManager.inProgressUpdate.mainField1).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(beNil());
- });
-
- it(@"should not set title", ^{
- testManager.title = textTitle;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.templateTitle).toNot(equal(textTitle));
- expect(testManager.inProgressUpdate.mainField1).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(beNil());
- });
-
- it(@"should format a one line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField1Type = line1Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal(textLine1));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[0]).to(equal(line1Type));
- expect(testManager.inProgressUpdate.mainField2).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(beNil());
- });
-
- it(@"should format a two line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField1Type = line1Type;
- testManager.textField2Type = line2Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal(textLine1));
- expect(testManager.inProgressUpdate.metadataTags.mainField1.firstObject).to(equal(line1Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(haveCount(1));
- expect(testManager.inProgressUpdate.mainField2).to(equal(textLine2));
- expect(testManager.inProgressUpdate.metadataTags.mainField2[0]).to(equal(line2Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(haveCount(1));
- expect(testManager.inProgressUpdate.mainField3).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField3).to(beNil());
- });
-
- it(@"should format a three line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField3 = textLine3;
- testManager.textField1Type = line1Type;
- testManager.textField2Type = line2Type;
- testManager.textField3Type = line3Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal(textLine1));
- expect(testManager.inProgressUpdate.mainField2).to(equal(textLine2));
- expect(testManager.inProgressUpdate.mainField3).to(equal(textLine3));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[0]).to(equal(line1Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(haveCount(1));
- expect(testManager.inProgressUpdate.metadataTags.mainField2[0]).to(equal(line2Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(haveCount(1));
- expect(testManager.inProgressUpdate.metadataTags.mainField3[0]).to(equal(line3Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField3).to(haveCount(1));
- expect(testManager.inProgressUpdate.mainField4).to(beEmpty());
- expect(testManager.inProgressUpdate.metadataTags.mainField4).to(beNil());
- });
-
- it(@"should format a four line text and metadata update properly", ^{
- testManager.textField1 = textLine1;
- testManager.textField2 = textLine2;
- testManager.textField3 = textLine3;
- testManager.textField4 = textLine4;
- testManager.textField1Type = line1Type;
- testManager.textField2Type = line2Type;
- testManager.textField3Type = line3Type;
- testManager.textField4Type = line4Type;
-
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.mainField1).to(equal(textLine1));
- expect(testManager.inProgressUpdate.mainField2).to(equal(textLine2));
- expect(testManager.inProgressUpdate.mainField3).to(equal(textLine3));
- expect(testManager.inProgressUpdate.mainField4).to(equal(textLine4));
- expect(testManager.inProgressUpdate.metadataTags.mainField1[0]).to(equal(line1Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField1).to(haveCount(1));
- expect(testManager.inProgressUpdate.metadataTags.mainField2[0]).to(equal(line2Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField2).to(haveCount(1));
- expect(testManager.inProgressUpdate.metadataTags.mainField3[0]).to(equal(line3Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField3).to(haveCount(1));
- expect(testManager.inProgressUpdate.metadataTags.mainField4[0]).to(equal(line4Type));
- expect(testManager.inProgressUpdate.metadataTags.mainField4).to(haveCount(1));
- });
+ testManager.textField1 = textLine1;
+ testManager.textField2 = textLine2;
+ testManager.textField3 = textLine3;
+ testManager.textField4 = textLine4;
+ testManager.mediaTrackTextField = textMediaTrack;
+ testManager.title = textTitle;
+ testManager.textField1Type = line1Type;
+ testManager.textField2Type = line2Type;
+ testManager.textField3Type = line3Type;
+ testManager.textField4Type = line4Type;
});
- context(@"updating images", ^{
- __block NSString *testTextFieldText = @"mainFieldText";
-
- beforeEach(^{
- testManager.windowCapability = [[SDLWindowCapability alloc] init];
- SDLImageField *primaryImageField = [[SDLImageField alloc] init];
- primaryImageField.name = SDLImageFieldNameGraphic;
- SDLImageField *secondaryImageField = [[SDLImageField alloc] init];
- secondaryImageField.name = SDLImageFieldNameSecondaryGraphic;
- testManager.windowCapability.imageFields = @[primaryImageField, secondaryImageField];
-
- SDLTextField *lineOneField = [[SDLTextField alloc] init];
- lineOneField.name = SDLTextFieldNameMainField1;
- testManager.windowCapability.textFields = @[lineOneField];
-
- testManager.batchUpdates = YES;
- testManager.textField1 = testTextFieldText;
- });
-
- context(@"when imageFields are nil", ^{
- beforeEach(^{
- testManager.windowCapability.imageFields = nil;
- });
-
- it(@"should send nothing", ^{
- testManager.primaryGraphic = testArtwork;
- testManager.secondaryGraphic = testArtwork;
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.graphic).to(beNil());
- expect(testManager.inProgressUpdate.secondaryGraphic).to(beNil());
- expect(testManager.inProgressUpdate.mainField1).to(equal(testTextFieldText));
- });
- });
-
- context(@"when the image is already on the head unit", ^{
- beforeEach(^{
- OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES);
- });
-
- it(@"should immediately attempt to update", ^{
- testManager.primaryGraphic = testArtwork;
- testManager.secondaryGraphic = testArtwork;
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.inProgressUpdate.graphic.value).to(equal(testArtworkName));
- expect(testManager.inProgressUpdate.secondaryGraphic.value).to(equal(testArtworkName));
- expect(testManager.inProgressUpdate.mainField1).to(equal(testTextFieldText));
- });
- });
-
- context(@"when the image is a static icon", ^{
- beforeEach(^{
- testManager.primaryGraphic = testStaticIcon;
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
- });
-
- it(@"should immediately update without uploading the images", ^{
- OCMReject([mockFileManager uploadArtwork:[OCMArg any] completionHandler:[OCMArg any]]);
- expect(testManager.inProgressUpdate.mainField1).to(equal(testTextFieldText));
- expect(testManager.inProgressUpdate.graphic.value).toNot(beNil());
- });
- });
+ it(@"should wait until batching ends to create an update operation", ^{
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
- context(@"when the image is not on the head unit", ^{
- beforeEach(^{
- OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(NO);
-
- testManager.primaryGraphic = testArtwork;
- testManager.secondaryGraphic = testArtwork;
- testManager.batchUpdates = NO;
- [testManager updateWithCompletionHandler:nil];
- });
-
- it(@"should immediately attempt to update without the images", ^{
- expect(testManager.inProgressUpdate.mainField1).to(equal(testTextFieldText));
- expect(testManager.inProgressUpdate.graphic.value).to(beNil());
- expect(testManager.inProgressUpdate.secondaryGraphic.value).to(beNil());
- expect(testManager.queuedImageUpdate.graphic.value).to(equal(testArtworkName));
- expect(testManager.queuedImageUpdate.secondaryGraphic.value).to(equal(testArtworkName));
- });
- });
-
- describe(@"When an image fails to upload to the remote", ^{
- __block SDLArtwork *testArtwork1 = nil;
- __block SDLArtwork *testArtwork2 = nil;
-
- beforeEach(^{
- testArtwork1 = [[SDLArtwork alloc] initWithData:[@"Test data 1" dataUsingEncoding:NSUTF8StringEncoding] name:@"Test data 1" fileExtension:@"png" persistent:NO];
- testArtwork2 = [[SDLArtwork alloc] initWithData:[@"Test data 2" dataUsingEncoding:NSUTF8StringEncoding] name:@"Test data 2" fileExtension:@"png" persistent:NO];
- });
-
- context(@"If the images for the primary and secondary graphics fail the upload process", ^{
- it(@"Should skip sending an update", ^{
- testManager.primaryGraphic = testArtwork1;
- testManager.secondaryGraphic = testArtwork2;
- testManager.batchUpdates = NO;
-
- OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(NO);
- NSArray<NSString *> *testSuccessfulArtworks = @[];
- NSError *testError = [NSError errorWithDomain:@"errorDomain"
- code:9
- userInfo:@{testArtwork1.name:@"error 1", testArtwork2.name:@"error 2"}
- ];
- OCMStub([mockFileManager uploadArtworks:[OCMArg isNotNil] completionHandler:([OCMArg invokeBlockWithArgs:testSuccessfulArtworks, testError, nil])]);
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.textField1).to(equal(testTextFieldText));
- expect(testManager.inProgressUpdate).to(beNil());
- expect(testManager.queuedImageUpdate.graphic.value).to(equal(testArtwork1.name));
- expect(testManager.queuedImageUpdate.secondaryGraphic.value).to(equal(testArtwork2.name));
- });
- });
-
- context(@"If only one of images for the primary and secondary graphics fails to upload", ^{
- it(@"Should show the primary graphic even if the secondary graphic upload fails", ^{
- testManager.primaryGraphic = testArtwork1;
- testManager.secondaryGraphic = testArtwork2;
- testManager.batchUpdates = NO;
-
- OCMStub([mockFileManager hasUploadedFile:testArtwork1]).andReturn(YES);
- OCMStub([mockFileManager hasUploadedFile:testArtwork2]).andReturn(NO);
- NSArray<NSString *> *testSuccessfulArtworks = @[testArtwork1.name];
- NSError *testError = [NSError errorWithDomain:@"errorDomain" code:9 userInfo:@{testArtwork2.name:@"error 2"}];
- OCMStub([mockFileManager uploadArtworks:[OCMArg isNotNil] completionHandler:([OCMArg invokeBlockWithArgs:testSuccessfulArtworks, testError, nil])]);
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.textField1).to(equal(testTextFieldText));
- expect(testManager.inProgressUpdate.graphic.value).to(equal(testArtwork1.name));
- expect(testManager.inProgressUpdate.secondaryGraphic).to(beNil());
- expect(testManager.inProgressUpdate.mainField1).to(beNil());
- expect(testManager.queuedImageUpdate.graphic.value).to(equal(testArtwork1.name));
- expect(testManager.queuedImageUpdate.secondaryGraphic.value).to(equal(testArtwork2.name));
- });
-
- it(@"Should show the secondary graphic even if the primary graphic upload fails", ^{
- testManager.primaryGraphic = testArtwork1;
- testManager.secondaryGraphic = testArtwork2;
- testManager.batchUpdates = NO;
-
- OCMStub([mockFileManager hasUploadedFile:testArtwork1]).andReturn(NO);
- OCMStub([mockFileManager hasUploadedFile:testArtwork2]).andReturn(YES);
- NSArray<NSString *> *testSuccessfulArtworks = @[testArtwork2.name];
- NSError *testError = [NSError errorWithDomain:@"errorDomain" code:9 userInfo:@{testArtwork1.name:@"error 2"}];
- OCMStub([mockFileManager uploadArtworks:[OCMArg isNotNil] completionHandler:([OCMArg invokeBlockWithArgs:testSuccessfulArtworks, testError, nil])]);
- [testManager updateWithCompletionHandler:nil];
-
- expect(testManager.textField1).to(equal(testTextFieldText));
- expect(testManager.inProgressUpdate.graphic).to(beNil());
- expect(testManager.inProgressUpdate.secondaryGraphic.value).to(equal(testArtwork2.name));
- expect(testManager.inProgressUpdate.mainField1).to(beNil());
- expect(testManager.queuedImageUpdate.graphic.value).to(equal(testArtwork1.name));
- expect(testManager.queuedImageUpdate.secondaryGraphic.value).to(equal(testArtwork2.name));
- });
- });
- });
+ testManager.batchUpdates = NO;
+ [testManager updateWithCompletionHandler:nil];
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
});
});
- context(@"On disconnects", ^{
+ // on disconnect
+ context(@"on disconnect", ^{
beforeEach(^{
[testManager stop];
});
@@ -1047,9 +423,6 @@ describe(@"text and graphic manager", ^{
expect(testManager.textField4Type).to(beNil());
expect(testManager.currentScreenData).to(equal([[SDLShow alloc] init]));
- expect(testManager.inProgressUpdate).to(beNil());
- expect(testManager.queuedImageUpdate).to(beNil());
- expect(testManager.hasQueuedUpdate).to(beFalse());
expect(testManager.windowCapability).to(beNil());
expect(testManager.currentLevel).to(equal(SDLHMILevelNone));
expect(testManager.blankArtwork).toNot(beNil());
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicUpdateOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicUpdateOperationSpec.m
new file mode 100644
index 000000000..cf308aecf
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicUpdateOperationSpec.m
@@ -0,0 +1,929 @@
+//
+// SDLTextAndGraphicUpdateOperationSpec.m
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 8/18/20.
+// Copyright © 2020 smartdevicelink. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+#import <Quick/Quick.h>
+#import <Nimble/Nimble.h>
+#import <OCMock/OCMock.h>
+
+#import "SDLFileManager.h"
+#import "SDLImage.h"
+#import "SDLImageField.h"
+#import "SDLMetadataTags.h"
+#import "SDLResult.h"
+#import "SDLShow.h"
+#import "SDLShowResponse.h"
+#import "SDLTextAndGraphicState.h"
+#import "SDLTextAndGraphicUpdateOperation.h"
+#import "SDLTextField.h"
+#import "SDLTextFieldName.h"
+#import "SDLWindowCapability.h"
+#import "TestConnectionManager.h"
+
+QuickSpecBegin(SDLTextAndGraphicUpdateOperationSpec)
+
+SDLTextField *fieldLine1 = [[SDLTextField alloc] initWithName:SDLTextFieldNameMainField1 characterSet:SDLCharacterSetUtf8 width:20 rows:20];
+SDLTextField *fieldLine2 = [[SDLTextField alloc] initWithName:SDLTextFieldNameMainField2 characterSet:SDLCharacterSetUtf8 width:20 rows:20];
+SDLTextField *fieldLine3 = [[SDLTextField alloc] initWithName:SDLTextFieldNameMainField3 characterSet:SDLCharacterSetUtf8 width:20 rows:20];
+SDLTextField *fieldLine4 = [[SDLTextField alloc] initWithName:SDLTextFieldNameMainField4 characterSet:SDLCharacterSetUtf8 width:20 rows:20];
+SDLImageField *fieldGraphic = [[SDLImageField alloc] initWithName:SDLImageFieldNameGraphic imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil];
+SDLImageField *fieldSecondaryGraphic = [[SDLImageField alloc] initWithName:SDLImageFieldNameSecondaryGraphic imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil];
+
+NSString *field1String = @"Text Field 1";
+NSString *field2String = @"Text Field 2";
+NSString *field3String = @"Text Field 3";
+NSString *field4String = @"Text Field 4";
+
+NSString *testArtworkName = @"some artwork name";
+SDLArtwork *testArtwork = [[SDLArtwork alloc] initWithData:[@"Test data" dataUsingEncoding:NSUTF8StringEncoding] name:testArtworkName fileExtension:@"png" persistent:NO];
+NSString *testArtworkName2 = @"some other artwork name";
+SDLArtwork *testArtwork2 = [[SDLArtwork alloc] initWithData:[@"Test data 2" dataUsingEncoding:NSUTF8StringEncoding] name:testArtworkName2 fileExtension:@"png" persistent:NO];
+SDLArtwork *testStaticIcon = [SDLArtwork artworkWithStaticIcon:SDLStaticIconNameDate];
+
+describe(@"the text and graphic operation", ^{
+ __block SDLTextAndGraphicUpdateOperation *testOp = nil;
+
+ __block TestConnectionManager *testConnectionManager = nil;
+ __block SDLFileManager *mockFileManager = nil;
+ __block SDLWindowCapability *windowCapability = nil;
+ __block SDLTextAndGraphicState *updatedState = nil;
+
+ __block SDLShowResponse *successShowResponse = [[SDLShowResponse alloc] init];
+ __block SDLShow *emptyCurrentDataShow = nil;
+
+ beforeEach(^{
+ testConnectionManager = [[TestConnectionManager alloc] init];
+ mockFileManager = OCMClassMock([SDLFileManager class]);
+ testOp = nil;
+ updatedState = nil;
+
+ successShowResponse.success = @YES;
+ successShowResponse.resultCode = SDLResultSuccess;
+ emptyCurrentDataShow = [[SDLShow alloc] init];
+ });
+
+ // updating text fields
+ describe(@"updating text fields", ^{
+ // with textfields available == nil
+ context(@"with textfields available == nil", ^{
+ beforeEach(^{
+ windowCapability = [[SDLWindowCapability alloc] init];
+ });
+
+ context(@"when sending four lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+ updatedState.textField3 = field3String;
+ updatedState.textField4 = field4String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should not send any text", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(beEmpty());
+ expect(testOp.currentScreenData.mainField2).to(beEmpty());
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+ });
+
+ // with one line available
+ context(@"with one line available", ^{
+ beforeEach(^{
+ windowCapability = [[SDLWindowCapability alloc] init];
+ windowCapability.textFields = @[fieldLine1];
+ });
+
+ context(@"when sending one line of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should only send one line of text", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@", field1String]));
+ expect(testOp.currentScreenData.mainField2).to(beEmpty());
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+
+ context(@"when sending two lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should concatenate the strings into one line", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@ - %@", field1String, field2String]));
+ expect(testOp.currentScreenData.mainField2).to(beEmpty());
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+
+ context(@"when sending three lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+ updatedState.textField3 = field3String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should concatenate the strings into one line", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@ - %@ - %@", field1String, field2String, field3String]));
+ expect(testOp.currentScreenData.mainField2).to(beEmpty());
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+
+ context(@"when sending four lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+ updatedState.textField3 = field3String;
+ updatedState.textField4 = field4String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should concatenate the strings into one line", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@ - %@ - %@ - %@", field1String, field2String, field3String, field4String]));
+ expect(testOp.currentScreenData.mainField2).to(beEmpty());
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+ });
+
+ // with two lines available
+ context(@"with two lines available", ^{
+ beforeEach(^{
+ windowCapability = [[SDLWindowCapability alloc] init];
+ windowCapability.textFields = @[fieldLine1, fieldLine2];
+ });
+
+ context(@"when sending one line of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should only send one line of text", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@", field1String]));
+ expect(testOp.currentScreenData.mainField2).to(beEmpty());
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+
+ context(@"when sending two lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should send two lines of text", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@", field1String]));
+ expect(testOp.currentScreenData.mainField2).to(equal([NSString stringWithFormat:@"%@", field2String]));
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+
+ context(@"when sending three lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+ updatedState.textField3 = field3String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should concatenate the strings into two lines", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@ - %@", field1String, field2String]));
+ expect(testOp.currentScreenData.mainField2).to(equal([NSString stringWithFormat:@"%@", field3String]));
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+
+ context(@"when sending four lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+ updatedState.textField3 = field3String;
+ updatedState.textField4 = field4String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should concatenate the strings into two lines", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@ - %@", field1String, field2String]));
+ expect(testOp.currentScreenData.mainField2).to(equal([NSString stringWithFormat:@"%@ - %@", field3String, field4String]));
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+ });
+
+ // with three lines available
+ context(@"with three lines available", ^{
+ beforeEach(^{
+ windowCapability = [[SDLWindowCapability alloc] init];
+ windowCapability.textFields = @[fieldLine1, fieldLine2, fieldLine3];
+ });
+
+ context(@"when sending one line of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should only send one line of text", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@", field1String]));
+ expect(testOp.currentScreenData.mainField2).to(beEmpty());
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+
+ context(@"when sending two lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should send two lines of text", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@", field1String]));
+ expect(testOp.currentScreenData.mainField2).to(equal([NSString stringWithFormat:@"%@", field2String]));
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+
+ context(@"when sending three lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+ updatedState.textField3 = field3String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should send three lines of text", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@", field1String]));
+ expect(testOp.currentScreenData.mainField2).to(equal([NSString stringWithFormat:@"%@", field2String]));
+ expect(testOp.currentScreenData.mainField3).to(equal([NSString stringWithFormat:@"%@", field3String]));
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+
+ context(@"when sending four lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+ updatedState.textField3 = field3String;
+ updatedState.textField4 = field4String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should concatenate the strings into three lines", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@", field1String]));
+ expect(testOp.currentScreenData.mainField2).to(equal([NSString stringWithFormat:@"%@", field2String]));
+ expect(testOp.currentScreenData.mainField3).to(equal([NSString stringWithFormat:@"%@ - %@", field3String, field4String]));
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+ });
+
+ // with four lines available
+ context(@"with four lines available", ^{
+ beforeEach(^{
+ windowCapability = [[SDLWindowCapability alloc] init];
+ windowCapability.textFields = @[fieldLine1, fieldLine2, fieldLine3, fieldLine4];
+ });
+
+ context(@"when sending one line of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should only send one line of text", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@", field1String]));
+ expect(testOp.currentScreenData.mainField2).to(beEmpty());
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+
+ context(@"when sending two lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should send two lines of text", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@", field1String]));
+ expect(testOp.currentScreenData.mainField2).to(equal([NSString stringWithFormat:@"%@", field2String]));
+ expect(testOp.currentScreenData.mainField3).to(beEmpty());
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+
+ context(@"when sending three lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+ updatedState.textField3 = field3String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should send three lines text", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@", field1String]));
+ expect(testOp.currentScreenData.mainField2).to(equal([NSString stringWithFormat:@"%@", field2String]));
+ expect(testOp.currentScreenData.mainField3).to(equal([NSString stringWithFormat:@"%@", field3String]));
+ expect(testOp.currentScreenData.mainField4).to(beEmpty());
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).to(beNil());
+ });
+ });
+
+ context(@"when sending four lines of text", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+ updatedState.textField3 = field3String;
+ updatedState.textField4 = field4String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should send four lines of text", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(testOp.currentScreenData.mainField1).to(equal([NSString stringWithFormat:@"%@", field1String]));
+ expect(testOp.currentScreenData.mainField2).to(equal([NSString stringWithFormat:@"%@", field2String]));
+ expect(testOp.currentScreenData.mainField3).to(equal([NSString stringWithFormat:@"%@", field3String]));
+ expect(testOp.currentScreenData.mainField4).to(equal([NSString stringWithFormat:@"%@", field4String]));
+ expect(testOp.currentScreenData.templateTitle).to(beEmpty());
+ expect(testOp.currentScreenData.alignment).to(beNil());
+ expect(testOp.currentScreenData.mediaTrack).to(beEmpty());
+ expect(testOp.currentScreenData.graphic).to(beNil());
+ expect(testOp.currentScreenData.secondaryGraphic).to(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField1).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField2).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField3).toNot(beNil());
+ expect(testOp.currentScreenData.metadataTags.mainField4).toNot(beNil());
+ });
+ });
+ });
+
+ // should call the update handler when done
+ context(@"should call the update handler when done", ^{
+ __block BOOL didCallHandler = NO;
+ beforeEach(^{
+ windowCapability = [[SDLWindowCapability alloc] init];
+ windowCapability.textFields = @[fieldLine1, fieldLine2, fieldLine3, fieldLine4];
+
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+ updatedState.textField3 = field3String;
+ updatedState.textField4 = field4String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:^(NSError * _Nullable error) {
+ didCallHandler = YES;
+ }];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should send the text and then call the update handler", ^{
+ expect(didCallHandler).to(equal(YES));
+ });
+ });
+
+ context(@"should call the currentScreenDataUpdatedHandler when the screen data is updated", ^{
+ __block SDLShow *updatedData = nil;
+ beforeEach(^{
+ windowCapability = [[SDLWindowCapability alloc] init];
+ windowCapability.textFields = @[fieldLine1, fieldLine2, fieldLine3, fieldLine4];
+
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.textField2 = field2String;
+ updatedState.textField3 = field3String;
+ updatedState.textField4 = field4String;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:^(SDLShow * _Nonnull newScreenData) {
+ updatedData = newScreenData;
+ } updateCompletionHandler:nil];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ });
+
+ it(@"should send the text and then call the update handler", ^{
+ expect(updatedData.mainField1).to(equal(field1String));
+ expect(updatedData.mainField2).to(equal(field2String));
+ expect(updatedData.mainField3).to(equal(field3String));
+ expect(updatedData.mainField4).to(equal(field4String));
+ });
+ });
+ });
+
+ // updating image fields
+ describe(@"updating image fields", ^{
+ beforeEach(^{
+ windowCapability = [[SDLWindowCapability alloc] init];
+ windowCapability.textFields = @[fieldLine1, fieldLine2, fieldLine3, fieldLine4];
+ windowCapability.imageFields = @[fieldGraphic, fieldSecondaryGraphic];
+ });
+
+ // when the images are already available on the head unit
+ context(@"when the images are already available on the head unit", ^{
+ beforeEach(^{
+ OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES);
+ });
+
+ // when only the primary graphic is supported
+ context(@"when only the primary graphic is supported", ^{
+ beforeEach(^{
+ windowCapability.imageFields = @[fieldGraphic];
+
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.primaryGraphic = testArtwork;
+ updatedState.secondaryGraphic = testArtwork2;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+ });
+
+ it(@"should send a show and not upload any artworks", ^{
+ expect(testConnectionManager.receivedRequests).to(haveCount(1));
+ SDLShow *firstSentRequest = testConnectionManager.receivedRequests[0];
+ expect(firstSentRequest.mainField1).to(equal(field1String));
+ expect(firstSentRequest.mainField2).to(beEmpty());
+ expect(firstSentRequest.graphic).toNot(beNil());
+ expect(firstSentRequest.secondaryGraphic).to(beNil());
+ });
+ });
+
+ // when both image fields are supported
+ context(@"when both image fields are supported", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.primaryGraphic = testArtwork;
+ updatedState.secondaryGraphic = testArtwork2;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+ });
+
+ it(@"should send a show and not upload any artworks", ^{
+ expect(testConnectionManager.receivedRequests).to(haveCount(1));
+ SDLShow *firstSentRequest = testConnectionManager.receivedRequests[0];
+ expect(firstSentRequest.mainField1).to(equal(field1String));
+ expect(firstSentRequest.mainField2).to(beEmpty());
+ expect(firstSentRequest.graphic).toNot(beNil());
+ expect(firstSentRequest.secondaryGraphic).toNot(beNil());
+ });
+ });
+ });
+
+ // when images are not on the head unit
+ context(@"when images are not on the head unit", ^{
+ beforeEach(^{
+ OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(NO);
+ OCMStub([mockFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], [NSNull null], nil])]);
+ });
+
+ // when there is text to update as well
+ context(@"when there is text to update as well", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.primaryGraphic = testArtwork;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+ });
+
+ it(@"should send the text, then upload the images, then send the full show", ^{
+ // First the text only show should be sent
+ expect(testConnectionManager.receivedRequests).to(haveCount(1));
+ SDLShow *firstSentRequest = testConnectionManager.receivedRequests[0];
+ expect(firstSentRequest.mainField1).to(equal(field1String));
+ expect(firstSentRequest.graphic).to(beNil());
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+
+ // Then the images should be uploaded
+ OCMExpect([mockFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]);
+
+ // Then the full show should be sent, this is currently not testable because the `mockFileManager hasUploadedFile` should change mid-call of `uploadArtworks` after the artwork is uploaded but before the final Show is sent in sdl_createImageOnlyShowWithPrimaryArtwork.
+// expect(testConnectionManager.receivedRequests).to(haveCount(2));
+// SDLShow *secondSentRequest = testConnectionManager.receivedRequests[1];
+// expect(secondSentRequest.mainField1).to(beNil());
+// expect(secondSentRequest.graphic).toNot(beNil());
+ });
+ });
+
+ // when there is no text to update
+ context(@"when there is no text to update", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.primaryGraphic = testArtwork;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+ });
+
+ it(@"should just upload the images, then send the full show", ^{
+ // First the text only show should be sent
+ expect(testConnectionManager.receivedRequests).to(haveCount(1));
+ SDLShow *firstSentRequest = testConnectionManager.receivedRequests[0];
+ expect(firstSentRequest.mainField1).to(beEmpty());
+ expect(firstSentRequest.graphic).to(beNil());
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+
+ // Then the images should be uploaded
+ OCMExpect([mockFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]);
+
+ // Then the full show should be sent, this is currently not testable because the `mockFileManager hasUploadedFile` should change mid-call of `uploadArtworks` after the artwork is uploaded but before the final Show is sent in sdl_createImageOnlyShowWithPrimaryArtwork.
+// expect(testConnectionManager.receivedRequests).to(haveCount(2));
+// SDLShow *secondSentRequest = testConnectionManager.receivedRequests[1];
+// expect(secondSentRequest.mainField1).to(beEmpty());
+// expect(secondSentRequest.graphic).toNot(beNil());
+ });
+ });
+
+ // when the image is a static icon
+ context(@"when the image is a static icon", ^{
+ beforeEach(^{
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.primaryGraphic = testStaticIcon;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+ });
+
+ it(@"should not upload the artwork", ^{
+ // The full show should be sent immediately
+ expect(testConnectionManager.receivedRequests).to(haveCount(1));
+ SDLShow *firstSentRequest = testConnectionManager.receivedRequests[0];
+ expect(firstSentRequest.mainField1).to(beEmpty());
+ expect(firstSentRequest.graphic).toNot(beNil());
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+
+ // Then the images should be uploaded
+ OCMReject([mockFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]);
+ });
+ });
+ });
+
+ // when an image fails to upload to the remote
+ describe(@"when an image fails to upload to the remote", ^{
+ context(@"if the images for the primary and secondary graphics fail the upload process", ^{
+ beforeEach(^{
+ OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(NO);
+ NSArray<NSString *> *testSuccessfulArtworks = @[];
+ NSError *testError = [NSError errorWithDomain:@"errorDomain"
+ code:9
+ userInfo:@{testArtwork.name:@"error 1", testArtwork2.name:@"error 2"}
+ ];
+ OCMStub([mockFileManager uploadArtworks:[OCMArg isNotNil] completionHandler:([OCMArg invokeBlockWithArgs:testSuccessfulArtworks, testError, nil])]);
+
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.primaryGraphic = testArtwork;
+ updatedState.secondaryGraphic = testArtwork2;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+ });
+
+ it(@"should skip sending an update", ^{
+ // Just the empty text show
+ expect(testConnectionManager.receivedRequests).to(haveCount(1));
+
+ SDLShow *sentShow = testConnectionManager.receivedRequests[0];
+ expect(sentShow.graphic).to(beNil());
+ expect(sentShow.secondaryGraphic).to(beNil());
+ });
+ });
+
+ context(@"if only one of images for the primary and secondary graphics fails to upload", ^{
+ it(@"should show the primary graphic even if the secondary graphic upload fails", ^{
+ OCMStub([mockFileManager hasUploadedFile:testArtwork]).andReturn(YES);
+ OCMStub([mockFileManager hasUploadedFile:testArtwork2]).andReturn(NO);
+ NSArray<NSString *> *testSuccessfulArtworks = @[testArtwork.name];
+ NSError *testError = [NSError errorWithDomain:@"errorDomain" code:9 userInfo:@{testArtwork2.name:@"error 2"}];
+ OCMStub([mockFileManager uploadArtworks:[OCMArg isNotNil] progressHandler:[OCMArg isNotNil] completionHandler:([OCMArg invokeBlockWithArgs:testSuccessfulArtworks, testError, nil])]);
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.primaryGraphic = testArtwork;
+ updatedState.secondaryGraphic = testArtwork2;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ expect(testConnectionManager.receivedRequests).to(haveCount(1));
+ SDLShow *receivedShow = testConnectionManager.receivedRequests[0];
+ expect(receivedShow.mainField1).to(equal(field1String));
+ expect(receivedShow.graphic.value).to(beNil());
+ expect(receivedShow.secondaryGraphic).to(beNil());
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ expect(testConnectionManager.receivedRequests).to(haveCount(2));
+ SDLShow *secondReceivedShow = testConnectionManager.receivedRequests[1];
+ expect(secondReceivedShow.mainField1).to(beNil());
+ expect(secondReceivedShow.graphic.value).to(equal(testArtwork.name));
+ expect(secondReceivedShow.secondaryGraphic).to(beNil());
+ });
+
+ it(@"Should show the secondary graphic even if the primary graphic upload fails", ^{
+ OCMStub([mockFileManager hasUploadedFile:testArtwork]).andReturn(NO);
+ OCMStub([mockFileManager hasUploadedFile:testArtwork2]).andReturn(YES);
+ NSArray<NSString *> *testSuccessfulArtworks = @[testArtwork2.name];
+ NSError *testError = [NSError errorWithDomain:@"errorDomain" code:9 userInfo:@{testArtwork.name:@"error 2"}];
+ OCMStub([mockFileManager uploadArtworks:[OCMArg isNotNil] progressHandler:[OCMArg isNotNil] completionHandler:([OCMArg invokeBlockWithArgs:testSuccessfulArtworks, testError, nil])]);
+ updatedState = [[SDLTextAndGraphicState alloc] init];
+ updatedState.textField1 = field1String;
+ updatedState.primaryGraphic = testArtwork;
+ updatedState.secondaryGraphic = testArtwork2;
+
+ testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentDataShow newState:updatedState currentScreenDataUpdatedHandler:nil updateCompletionHandler:nil];
+ [testOp start];
+
+ expect(testConnectionManager.receivedRequests).to(haveCount(1));
+ SDLShow *receivedShow = testConnectionManager.receivedRequests[0];
+ expect(receivedShow.mainField1).to(equal(field1String));
+ expect(receivedShow.graphic).to(beNil());
+ expect(receivedShow.secondaryGraphic).to(beNil());
+
+ [testConnectionManager respondToLastRequestWithResponse:successShowResponse];
+ expect(testConnectionManager.receivedRequests).to(haveCount(2));
+ SDLShow *secondReceivedShow = testConnectionManager.receivedRequests[1];
+ expect(secondReceivedShow.mainField1).to(beNil());
+ expect(secondReceivedShow.graphic).to(beNil());
+ expect(secondReceivedShow.secondaryGraphic.value).to(equal(testArtwork2.name));
+ });
+ });
+ });
+ });
+});
+
+QuickSpecEnd
diff --git a/SmartDeviceLinkTests/SDLScreenManagerSpec.m b/SmartDeviceLinkTests/SDLScreenManagerSpec.m
index b3f613256..da4f9f725 100644
--- a/SmartDeviceLinkTests/SDLScreenManagerSpec.m
+++ b/SmartDeviceLinkTests/SDLScreenManagerSpec.m
@@ -30,8 +30,7 @@
@property (weak, nonatomic) id<SDLConnectionManagerType> connectionManager;
@property (weak, nonatomic) SDLFileManager *fileManager;
-
-@property (strong, nonatomic, nullable) SDLShow *inProgressUpdate;
+@property (strong, nonatomic) NSOperationQueue *transactionQueue;
@property (copy, nonatomic, nullable) SDLHMILevel currentLevel;
@end
@@ -106,7 +105,7 @@ describe(@"screen manager", ^{
});
it(@"should have in progress updates", ^{
- expect(testScreenManager.textAndGraphicManager.inProgressUpdate).toNot(beNil());
+ expect(testScreenManager.textAndGraphicManager.transactionQueue.operationCount).toNot(equal(0));
expect(testScreenManager.softButtonManager.transactionQueue.operationCount).to(equal(1));
expect(testScreenManager.textAndGraphicManager.batchUpdates).to(beFalse());