diff options
author | Joel Fischer <joeljfischer@gmail.com> | 2019-04-25 16:50:02 -0400 |
---|---|---|
committer | Joel Fischer <joeljfischer@gmail.com> | 2019-04-25 16:50:02 -0400 |
commit | 9e4090122a81e91c3cb445aaa271c23041638453 (patch) | |
tree | 45048f032543059e10cf38a4acb16bd3264ad3d0 | |
parent | f2f33e8a9c65914fdf5ee683918ab0f3590dc29d (diff) | |
download | sdl_ios-9e4090122a81e91c3cb445aaa271c23041638453.tar.gz |
Initial work on re-doing the soft button manager
-rw-r--r-- | Example Apps/Example ObjC/ButtonManager.m | 3 | ||||
-rw-r--r-- | SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 42 | ||||
-rw-r--r-- | SmartDeviceLink/SDLChoiceSetManager.m | 2 | ||||
-rw-r--r-- | SmartDeviceLink/SDLSoftButtonManager.h | 7 | ||||
-rw-r--r-- | SmartDeviceLink/SDLSoftButtonManager.m | 295 | ||||
-rw-r--r-- | SmartDeviceLink/SDLSoftButtonReplaceOperation.h | 27 | ||||
-rw-r--r-- | SmartDeviceLink/SDLSoftButtonReplaceOperation.m | 293 | ||||
-rw-r--r-- | SmartDeviceLink/SDLSoftButtonTransitionOperation.h | 28 | ||||
-rw-r--r-- | SmartDeviceLink/SDLSoftButtonTransitionOperation.m | 78 |
9 files changed, 525 insertions, 250 deletions
diff --git a/Example Apps/Example ObjC/ButtonManager.m b/Example Apps/Example ObjC/ButtonManager.m index 786d5027b..4befdf726 100644 --- a/Example Apps/Example ObjC/ButtonManager.m +++ b/Example Apps/Example ObjC/ButtonManager.m @@ -135,6 +135,9 @@ NS_ASSUME_NONNULL_BEGIN SDLSoftButtonObject *object = [weakself.sdlManager.screenManager softButtonObjectNamed:ImagesVisibleSoftButton]; [object transitionToNextState]; + SDLSoftButtonObject *textButton = [weakself.sdlManager.screenManager softButtonObjectNamed:TextVisibleSoftButton]; + [textButton transitionToNextState]; + SDLLogD(@"Image visibility soft button press fired %d", weakself.imagesEnabled); }]; diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 795fb035c..252651f01 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -1088,6 +1088,10 @@ 5DA026901AD44EE700019F86 /* SDLDialNumberResponseSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DA0268F1AD44EE700019F86 /* SDLDialNumberResponseSpec.m */; }; 5DA102A41D4122C700C15826 /* NSMutableDictionary+SafeRemove.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DA102A21D4122C700C15826 /* NSMutableDictionary+SafeRemove.h */; }; 5DA102A51D4122C700C15826 /* NSMutableDictionary+SafeRemove.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DA102A31D4122C700C15826 /* NSMutableDictionary+SafeRemove.m */; }; + 5DA150C72271FDC20032928D /* SDLSoftButtonTransitionOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DA150C52271FDC20032928D /* SDLSoftButtonTransitionOperation.h */; }; + 5DA150C82271FDC20032928D /* SDLSoftButtonTransitionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DA150C62271FDC20032928D /* SDLSoftButtonTransitionOperation.m */; }; + 5DA150CD2271FE180032928D /* SDLSoftButtonReplaceOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DA150CB2271FE180032928D /* SDLSoftButtonReplaceOperation.h */; }; + 5DA150CE2271FE180032928D /* SDLSoftButtonReplaceOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DA150CC2271FE180032928D /* SDLSoftButtonReplaceOperation.m */; }; 5DA22CB71D075CF200245F5F /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DA22CB31D075CF200245F5F /* Nimble.framework */; }; 5DA22CB81D075CF200245F5F /* OCMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DA22CB41D075CF200245F5F /* OCMock.framework */; }; 5DA22CBA1D075CF200245F5F /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DA22CB61D075CF200245F5F /* Quick.framework */; }; @@ -2717,6 +2721,10 @@ 5DA0268F1AD44EE700019F86 /* SDLDialNumberResponseSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLDialNumberResponseSpec.m; sourceTree = "<group>"; }; 5DA102A21D4122C700C15826 /* NSMutableDictionary+SafeRemove.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableDictionary+SafeRemove.h"; sourceTree = "<group>"; }; 5DA102A31D4122C700C15826 /* NSMutableDictionary+SafeRemove.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableDictionary+SafeRemove.m"; sourceTree = "<group>"; }; + 5DA150C52271FDC20032928D /* SDLSoftButtonTransitionOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLSoftButtonTransitionOperation.h; sourceTree = "<group>"; }; + 5DA150C62271FDC20032928D /* SDLSoftButtonTransitionOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLSoftButtonTransitionOperation.m; sourceTree = "<group>"; }; + 5DA150CB2271FE180032928D /* SDLSoftButtonReplaceOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLSoftButtonReplaceOperation.h; sourceTree = "<group>"; }; + 5DA150CC2271FE180032928D /* SDLSoftButtonReplaceOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLSoftButtonReplaceOperation.m; sourceTree = "<group>"; }; 5DA22CB31D075CF200245F5F /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = sdl_ios/Carthage/Build/iOS/Nimble.framework; sourceTree = "<group>"; }; 5DA22CB41D075CF200245F5F /* OCMock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OCMock.framework; path = sdl_ios/Carthage/Build/iOS/OCMock.framework; sourceTree = "<group>"; }; 5DA22CB61D075CF200245F5F /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = sdl_ios/Carthage/Build/iOS/Quick.framework; sourceTree = "<group>"; }; @@ -3726,10 +3734,8 @@ children = ( 5D0A7380203F23F30001595D /* SDLSoftButtonManager.h */, 5D0A7381203F23F30001595D /* SDLSoftButtonManager.m */, - 5D0A7384203F24060001595D /* SDLSoftButtonState.h */, - 5D0A7385203F24060001595D /* SDLSoftButtonState.m */, - 5D0A7388203F24320001595D /* SDLSoftButtonObject.h */, - 5D0A7389203F24320001595D /* SDLSoftButtonObject.m */, + 5DA150CA2271FE030032928D /* Buttons */, + 5DA150C92271FDF90032928D /* Operations */, ); name = "Soft Button"; sourceTree = "<group>"; @@ -5214,6 +5220,28 @@ name = Permissions; sourceTree = "<group>"; }; + 5DA150C92271FDF90032928D /* Operations */ = { + isa = PBXGroup; + children = ( + 5DA150C52271FDC20032928D /* SDLSoftButtonTransitionOperation.h */, + 5DA150C62271FDC20032928D /* SDLSoftButtonTransitionOperation.m */, + 5DA150CB2271FE180032928D /* SDLSoftButtonReplaceOperation.h */, + 5DA150CC2271FE180032928D /* SDLSoftButtonReplaceOperation.m */, + ); + name = Operations; + sourceTree = "<group>"; + }; + 5DA150CA2271FE030032928D /* Buttons */ = { + isa = PBXGroup; + children = ( + 5D0A7384203F24060001595D /* SDLSoftButtonState.h */, + 5D0A7385203F24060001595D /* SDLSoftButtonState.m */, + 5D0A7388203F24320001595D /* SDLSoftButtonObject.h */, + 5D0A7389203F24320001595D /* SDLSoftButtonObject.m */, + ); + name = Buttons; + sourceTree = "<group>"; + }; 5DA23FF11F2FA32A009C0313 /* Audio Service */ = { isa = PBXGroup; children = ( @@ -6212,6 +6240,7 @@ 5D61FC451A84238C00846EE7 /* SDLAudioPassThruCapabilities.h in Headers */, 5D61FDC71A84238C00846EE7 /* SDLTextAlignment.h in Headers */, 5D61FD051A84238C00846EE7 /* SDLOnButtonPress.h in Headers */, + 5DA150C72271FDC20032928D /* SDLSoftButtonTransitionOperation.h in Headers */, 5D61FCC51A84238C00846EE7 /* SDLHMIZoneCapabilities.h in Headers */, 884AF94F220B488900E22928 /* SDLOnSystemCapabilityUpdated.h in Headers */, 880D267D220DE5DF00B3F496 /* SDLWeatherServiceManifest.h in Headers */, @@ -6400,6 +6429,7 @@ 5D61FDE31A84238C00846EE7 /* SDLUnregisterAppInterface.h in Headers */, 5D61FD331A84238C00846EE7 /* SDLPowerModeQualificationStatus.h in Headers */, 5D92937020B5E0E500FCC775 /* SDLDeleteChoicesOperation.h in Headers */, + 5DA150CD2271FE180032928D /* SDLSoftButtonReplaceOperation.h in Headers */, 5D61FE011A84238C00846EE7 /* SDLVehicleDataNotificationStatus.h in Headers */, 5D61FDC91A84238C00846EE7 /* SDLTextField.h in Headers */, 5D6F7A351BC5B9B60070BF37 /* SDLLockScreenViewController.h in Headers */, @@ -6683,7 +6713,7 @@ }; 5D61FA1B1A84237100846EE7 = { CreatedOnToolsVersion = 6.1.1; - LastSwiftMigration = 0940; + LastSwiftMigration = 1020; }; 5D61FA251A84237100846EE7 = { CreatedOnToolsVersion = 6.1.1; @@ -7035,6 +7065,7 @@ 5D61FD461A84238C00846EE7 /* SDLProtocolHeader.m in Sources */, 5DD8406320FCD6C10082CE04 /* SDLElectronicParkBrakeStatus.m in Sources */, 8BBEA6071F324165003EEA26 /* SDLMetadataType.m in Sources */, + 5DA150C82271FDC20032928D /* SDLSoftButtonTransitionOperation.m in Sources */, 5D61FDBC1A84238C00846EE7 /* SDLSystemAction.m in Sources */, 5D61FC381A84238C00846EE7 /* SDLAlert.m in Sources */, 88AAD4BD2211B76800F1E6D7 /* SDLMediaServiceManifest.m in Sources */, @@ -7097,6 +7128,7 @@ 1E5AD0651F207DD50029B8AF /* SDLTemperature.m in Sources */, DA9F7E901DCC04C000ACAE48 /* SDLUnsubscribeWayPointsResponse.m in Sources */, 88F65137220C74FD00CAF321 /* SDLWeatherData.m in Sources */, + 5DA150CE2271FE180032928D /* SDLSoftButtonReplaceOperation.m in Sources */, 5DE372A21ACB2ED300849FAA /* SDLHMICapabilities.m in Sources */, 5D61FDD41A84238C00846EE7 /* SDLTouchEvent.m in Sources */, 5D61FD881A84238C00846EE7 /* SDLSetGlobalProperties.m in Sources */, diff --git a/SmartDeviceLink/SDLChoiceSetManager.m b/SmartDeviceLink/SDLChoiceSetManager.m index 21dff1cc5..36914d553 100644 --- a/SmartDeviceLink/SDLChoiceSetManager.m +++ b/SmartDeviceLink/SDLChoiceSetManager.m @@ -372,7 +372,7 @@ UInt16 const ChoiceCellIdMin = 1; } } -#pragma mark - Setters +#pragma mark - Keyboard Configuration - (void)setKeyboardConfiguration:(nullable SDLKeyboardProperties *)keyboardConfiguration { if (keyboardConfiguration == nil) { diff --git a/SmartDeviceLink/SDLSoftButtonManager.h b/SmartDeviceLink/SDLSoftButtonManager.h index b163fe599..8a20b50f6 100644 --- a/SmartDeviceLink/SDLSoftButtonManager.h +++ b/SmartDeviceLink/SDLSoftButtonManager.h @@ -54,13 +54,6 @@ typedef void(^SDLSoftButtonUpdateCompletionHandler)(NSError *__nullable error); - (void)stop; /** - Cause all transitions in between `beginUpdates` and this method call to occur in one RPC update. - - @param handler The handler called once the update is completed. - */ -- (void)updateWithCompletionHandler:(nullable SDLSoftButtonUpdateCompletionHandler)handler; - -/** Returns a soft button object associated with the manager that is named the specified name or nil if nothing corresponds. @param name The name to find a soft button for diff --git a/SmartDeviceLink/SDLSoftButtonManager.m b/SmartDeviceLink/SDLSoftButtonManager.m index 3f19ff1e1..826c363dd 100644 --- a/SmartDeviceLink/SDLSoftButtonManager.m +++ b/SmartDeviceLink/SDLSoftButtonManager.m @@ -22,7 +22,9 @@ #import "SDLSoftButton.h" #import "SDLSoftButtonCapabilities.h" #import "SDLSoftButtonObject.h" +#import "SDLSoftButtonReplaceOperation.h" #import "SDLSoftButtonState.h" +#import "SDLSoftButtonTransitionOperation.h" NS_ASSUME_NONNULL_BEGIN @@ -35,24 +37,15 @@ NS_ASSUME_NONNULL_BEGIN @interface SDLSoftButtonManager() -@property (strong, nonatomic) NSArray<SDLSoftButton *> *currentSoftButtons; - @property (weak, nonatomic) id<SDLConnectionManagerType> connectionManager; @property (weak, nonatomic) SDLFileManager *fileManager; -@property (strong, nonatomic, nullable) SDLShow *inProgressUpdate; -@property (copy, nonatomic, nullable) SDLSoftButtonUpdateCompletionHandler inProgressHandler; - -@property (assign, nonatomic) BOOL hasQueuedUpdate; -@property (copy, nonatomic, nullable) SDLSoftButtonUpdateCompletionHandler queuedUpdateHandler; +@property (strong, nonatomic) NSOperationQueue *transactionQueue; @property (copy, nonatomic, nullable) SDLHMILevel currentLevel; @property (strong, nonatomic, nullable) SDLDisplayCapabilities *displayCapabilities; @property (strong, nonatomic, nullable) SDLSoftButtonCapabilities *softButtonCapabilities; -@property (assign, nonatomic) BOOL waitingOnHMILevelUpdateToUpdate; -@property (assign, nonatomic) BOOL isDirty; - @end @implementation SDLSoftButtonManager @@ -66,8 +59,7 @@ NS_ASSUME_NONNULL_BEGIN _softButtonObjects = @[]; _currentLevel = nil; - _waitingOnHMILevelUpdateToUpdate = NO; - _isDirty = NO; + _transactionQueue = [self sdl_newTransactionQueue]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_registerResponse:) name:SDLDidReceiveRegisterAppInterfaceResponse object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_displayLayoutResponse:) name:SDLDidReceiveSetDisplayLayoutResponse object:nil]; @@ -79,35 +71,31 @@ NS_ASSUME_NONNULL_BEGIN - (void)stop { _softButtonObjects = @[]; _currentMainField1 = nil; - - _inProgressUpdate = nil; - _inProgressHandler = nil; - _hasQueuedUpdate = NO; - _queuedUpdateHandler = nil; _currentLevel = nil; _displayCapabilities = nil; _softButtonCapabilities = nil; - _waitingOnHMILevelUpdateToUpdate = NO; - _isDirty = NO; + + [_transactionQueue cancelAllOperations]; + self.transactionQueue = [self sdl_newTransactionQueue]; } +- (NSOperationQueue *)sdl_newTransactionQueue { + NSOperationQueue *queue = [[NSOperationQueue alloc] init]; + queue.name = @"SDLSoftButtonManager Transaction Queue"; + queue.maxConcurrentOperationCount = 1; + queue.qualityOfService = NSQualityOfServiceUserInitiated; + queue.suspended = YES; + + return queue; +} + + +#pragma mark - Sending Soft Buttons + - (void)setSoftButtonObjects:(NSArray<SDLSoftButtonObject *> *)softButtonObjects { // Only update if something changed. This prevents, for example, an empty array being reset if (_softButtonObjects == softButtonObjects) { return; - } else { - self.isDirty = YES; - } - - self.inProgressUpdate = nil; - if (self.inProgressHandler != nil) { - self.inProgressHandler([NSError sdl_softButtonManager_pendingUpdateSuperseded]); - self.inProgressHandler = nil; - } - self.hasQueuedUpdate = NO; - if (self.queuedUpdateHandler != nil) { - self.queuedUpdateHandler([NSError sdl_softButtonManager_pendingUpdateSuperseded]); - self.queuedUpdateHandler = nil; } // Set the soft button ids. Check to make sure no two soft buttons have the same name, there aren't many soft buttons, so n^2 isn't going to be bad @@ -129,231 +117,60 @@ NS_ASSUME_NONNULL_BEGIN _softButtonObjects = softButtonObjects; - [self updateWithCompletionHandler:nil]; -} - -- (nullable SDLSoftButtonObject *)softButtonObjectNamed:(NSString *)name { - for (SDLSoftButtonObject *object in self.softButtonObjects) { - if ([object.name isEqualToString:name]) { - return object; - } - } - - return nil; + SDLSoftButtonReplaceOperation *op = [[SDLSoftButtonReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager capabilities:self.softButtonCapabilities softButtonObjects:_softButtonObjects mainField1:self.currentMainField1]; + [self.transactionQueue cancelAllOperations]; + [self.transactionQueue addOperation:op]; } - (void)sdl_transitionSoftButton:(SDLSoftButtonObject *)softButton { - self.isDirty = YES; - [self updateWithCompletionHandler:nil]; -} - -- (void)updateWithCompletionHandler:(nullable SDLSoftButtonUpdateCompletionHandler)handler { - // Don't send if we're batching - if (self.isBatchingUpdates || !self.isDirty) { return; } - - // Don't send if we're in HMI NONE - if (self.currentLevel == nil || [self.currentLevel isEqualToString:SDLHMILevelNone]) { - self.waitingOnHMILevelUpdateToUpdate = YES; - return; - } else { - self.waitingOnHMILevelUpdateToUpdate = NO; - } - - [self sdl_updateWithCompletionHandler:handler]; + SDLSoftButtonTransitionOperation *op = [[SDLSoftButtonTransitionOperation alloc] initWithConnectionManager:self.connectionManager capabilities:self.softButtonCapabilities softButtons:[self sdl_softButtonsForCurrentState] mainField1:self.currentMainField1]; + [self.transactionQueue addOperation:op]; } -- (void)sdl_updateWithCompletionHandler:(nullable SDLSoftButtonUpdateCompletionHandler)handler { - SDLLogD(@"Updating soft buttons"); - self.isDirty = NO; - - 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_softButtonManager_pendingUpdateSuperseded]); - self.queuedUpdateHandler = nil; - } - - if (handler != nil) { - self.queuedUpdateHandler = handler; - } else { - self.hasQueuedUpdate = YES; - } - - return; - } - - self.inProgressHandler = [handler copy]; - self.inProgressUpdate = [[SDLShow alloc] init]; - self.inProgressUpdate.mainField1 = self.currentMainField1 ?: @""; - - if ([self sdl_supportsSoftButtonImages]) { - [self sdl_uploadInitialStateImages]; - [self sdl_uploadOtherStateImages]; - } - - if (self.softButtonObjects == nil) { - SDLLogV(@"Soft button objects are nil, sending an empty array"); - self.inProgressUpdate.softButtons = @[]; - } else if (([self sdl_currentStateHasImages] && ![self sdl_allCurrentStateImagesAreUploaded]) - || ![self sdl_supportsSoftButtonImages]) { - // The images don't yet exist on the head unit, or we cannot use images, send a text update, if possible. Otherwise, don't send anything yet. - NSArray<SDLSoftButton *> *textOnlyButtons = [self sdl_textButtonsForCurrentState]; - if (textOnlyButtons != nil) { - SDLLogV(@"Soft button images unavailable, sending text buttons"); - self.inProgressUpdate.softButtons = textOnlyButtons; - } else { - SDLLogV(@"Soft button images unavailable, text buttons unavailable"); - self.inProgressUpdate = nil; - return; - } - } else { - SDLLogV(@"Sending soft buttons with images"); - self.inProgressUpdate.softButtons = [self sdl_softButtonsForCurrentState]; - } - - __weak typeof(self) weakSelf = self; - [self.connectionManager sendConnectionRequest:self.inProgressUpdate withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { - __strong typeof(weakSelf) strongSelf = weakSelf; - - SDLLogD(@"Soft button update completed"); - - strongSelf.inProgressUpdate = nil; - if (strongSelf.inProgressHandler != nil) { - strongSelf.inProgressHandler(error); - strongSelf.inProgressHandler = nil; - } - - if (strongSelf.hasQueuedUpdate) { - SDLLogV(@"Queued update exists, sending another update"); - [strongSelf updateWithCompletionHandler:[strongSelf.queuedUpdateHandler copy]]; - strongSelf.queuedUpdateHandler = nil; - strongSelf.hasQueuedUpdate = NO; - } - }]; -} -#pragma mark - Images +#pragma mark - Getting Soft Buttons -- (BOOL)sdl_currentStateHasImages { +- (nullable SDLSoftButtonObject *)softButtonObjectNamed:(NSString *)name { for (SDLSoftButtonObject *object in self.softButtonObjects) { - if (object.currentState.artwork != nil) { - return YES; + if ([object.name isEqualToString:name]) { + return object; } } - return NO; + return nil; } -- (BOOL)sdl_allCurrentStateImagesAreUploaded { +- (NSArray<SDLSoftButton *> *)sdl_softButtonsForCurrentState { + NSMutableArray<SDLSoftButton *> *softButtons = [NSMutableArray arrayWithCapacity:self.softButtonObjects.count]; for (SDLSoftButtonObject *button in self.softButtonObjects) { - SDLArtwork *artwork = button.currentState.artwork; - if (artwork != nil && ![self.fileManager hasUploadedFile:artwork] && !artwork.isStaticIcon) { - return NO; - } + [softButtons addObject:button.currentStateSoftButton]; } - return YES; + return [softButtons copy]; } -- (BOOL)sdl_supportsSoftButtonImages { - BOOL supportsGraphics = self.displayCapabilities ? self.displayCapabilities.graphicSupported.boolValue : YES; - BOOL supportsSoftButtonImages = self.softButtonCapabilities ? self.softButtonCapabilities.imageSupported.boolValue : NO; +#pragma mark - Getters / Setters - return (supportsGraphics && supportsSoftButtonImages); -} +- (void)setBatchUpdates:(BOOL)batchUpdates { + _batchUpdates = batchUpdates; -- (void)sdl_uploadInitialStateImages { - NSMutableArray<SDLArtwork *> *initialStatesToBeUploaded = [NSMutableArray array]; - // Upload all soft button images, the initial state images first, then the other states. We need to send updates when the initial state is ready. - for (SDLSoftButtonObject *object in self.softButtonObjects) { - if ([self sdl_artworkNeedsUpload:object.currentState.artwork]) { - [initialStatesToBeUploaded addObject:object.currentState.artwork]; - } - } - - // Upload initial images, then other state images - if (initialStatesToBeUploaded.count > 0) { - SDLLogD(@"Uploading soft button initial artworks"); - [self.fileManager uploadArtworks:[initialStatesToBeUploaded copy] completionHandler:^(NSArray<NSString *> * _Nonnull artworkNames, NSError * _Nullable error) { - if (error != nil) { - SDLLogE(@"Error uploading soft button artworks: %@", error); - } - - SDLLogD(@"Soft button initial artworks uploaded"); - [self sdl_updateWithCompletionHandler:nil]; - }]; - } + _transactionQueue.suspended = batchUpdates; } -- (void)sdl_uploadOtherStateImages { - NSMutableArray<SDLArtwork *> *otherStatesToBeUploaded = [NSMutableArray array]; - // Upload all soft button images, the initial state images first, then the other states. We need to send updates when the initial state is ready. - for (SDLSoftButtonObject *object in self.softButtonObjects) { - for (SDLSoftButtonState *state in object.states) { - if ([state.name isEqualToString:object.currentState.name]) { continue; } - if ([self sdl_artworkNeedsUpload:state.artwork]) { - [otherStatesToBeUploaded addObject:state.artwork]; - } - } - } - - if (otherStatesToBeUploaded.count > 0) { - SDLLogD(@"Uploading soft button other state artworks"); - [self.fileManager uploadArtworks:[otherStatesToBeUploaded copy] completionHandler:^(NSArray<NSString *> * _Nonnull artworkNames, NSError * _Nullable error) { - if (error != nil) { - SDLLogE(@"Error uploading soft button artworks: %@", error); - } +- (void)setCurrentMainField1:(nullable NSString *)currentMainField1 { + _currentMainField1 = currentMainField1; - SDLLogD(@"Soft button other state artworks uploaded"); - // In case our soft button states have changed in the meantime - [self sdl_updateWithCompletionHandler:nil]; - }]; - } -} - -- (BOOL)sdl_artworkNeedsUpload:(SDLArtwork *)artwork { - return (artwork != nil && ![self.fileManager hasUploadedFile:artwork] && !artwork.isStaticIcon); -} - -#pragma mark - Creating Soft Buttons - -/** - Returns text soft buttons representing the initial states of the button objects, or nil if _any_ of the buttons' current states are image only buttons. - - @return The text soft buttons - */ -- (nullable NSArray<SDLSoftButton *> *)sdl_textButtonsForCurrentState { - NSMutableArray<SDLSoftButton *> *textButtons = [NSMutableArray arrayWithCapacity:self.softButtonObjects.count]; - for (SDLSoftButtonObject *buttonObject in self.softButtonObjects) { - SDLSoftButton *button = buttonObject.currentStateSoftButton; - if (button.text == nil) { - return nil; + for (NSUInteger i = 0; i < self.transactionQueue.operations.count; i++) { + if ([self.transactionQueue.operations[i] isMemberOfClass:[SDLSoftButtonReplaceOperation class]]) { + SDLSoftButtonReplaceOperation *op = self.transactionQueue.operations[i]; + op.mainField1 = currentMainField1; + } else if ([self.transactionQueue.operations[i] isMemberOfClass:[SDLSoftButtonTransitionOperation class]]) { + SDLSoftButtonTransitionOperation *op = self.transactionQueue.operations[i]; + op.mainField1 = currentMainField1; } - - button.image = nil; - button.type = SDLSoftButtonTypeText; - [textButtons addObject:button]; - } - - return [textButtons copy]; -} - -- (NSArray<SDLSoftButton *> *)sdl_softButtonsForCurrentState { - NSMutableArray<SDLSoftButton *> *softButtons = [NSMutableArray arrayWithCapacity:self.softButtonObjects.count]; - for (SDLSoftButtonObject *button in self.softButtonObjects) { - [softButtons addObject:button.currentStateSoftButton]; } - - return [softButtons copy]; } -#pragma mark - Getters - -- (BOOL)hasQueuedUpdate { - return (_queuedUpdateHandler != nil ?: _hasQueuedUpdate); -} #pragma mark - RPC Responses @@ -382,9 +199,10 @@ NS_ASSUME_NONNULL_BEGIN self.softButtonCapabilities = response.softButtonCapabilities ? response.softButtonCapabilities.firstObject : nil; self.displayCapabilities = response.displayCapabilities; - // Auto-send an updated Show + // Auto-send an updated Show to account for changes to the capabilities if (self.softButtonObjects.count > 0) { - [self updateWithCompletionHandler:nil]; + SDLSoftButtonReplaceOperation *op = [[SDLSoftButtonReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager capabilities:self.softButtonCapabilities softButtonObjects:self.softButtonObjects mainField1:self.currentMainField1]; + [self.transactionQueue addOperation:op]; } } @@ -392,12 +210,15 @@ NS_ASSUME_NONNULL_BEGIN SDLOnHMIStatus *hmiStatus = (SDLOnHMIStatus *)notification.notification; SDLHMILevel oldHMILevel = self.currentLevel; - self.currentLevel = hmiStatus.hmiLevel; - - // Auto-send an updated show if we were in NONE and now we are not - if ([oldHMILevel isEqualToString:SDLHMILevelNone] && ![self.currentLevel isEqualToString:SDLHMILevelNone] && self.waitingOnHMILevelUpdateToUpdate) { - [self updateWithCompletionHandler:nil]; + if (![oldHMILevel isEqualToEnum:hmiStatus.hmiLevel]) { + if ([hmiStatus.hmiLevel isEqualToEnum:SDLHMILevelNone]) { + self.transactionQueue.suspended = YES; + } else { + self.transactionQueue.suspended = NO; + } } + + self.currentLevel = hmiStatus.hmiLevel; } @end diff --git a/SmartDeviceLink/SDLSoftButtonReplaceOperation.h b/SmartDeviceLink/SDLSoftButtonReplaceOperation.h new file mode 100644 index 000000000..1d46db59a --- /dev/null +++ b/SmartDeviceLink/SDLSoftButtonReplaceOperation.h @@ -0,0 +1,27 @@ +// +// SDLSoftButtonReplaceOperation.h +// SmartDeviceLink +// +// Created by Joel Fischer on 4/25/19. +// Copyright © 2019 smartdevicelink. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#import "SDLAsynchronousOperation.h" + +@class SDLSoftButtonCapabilities; +@class SDLFileManager; +@class SDLSoftButtonObject; + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLSoftButtonReplaceOperation : SDLAsynchronousOperation + +@property (strong, nonatomic) NSString *mainField1; + +- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager fileManager:(SDLFileManager *)fileManager capabilities:(SDLSoftButtonCapabilities *)capabilities softButtonObjects:(NSArray<SDLSoftButtonObject *> *)softButtonObjects mainField1:(NSString *)mainField1; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLSoftButtonReplaceOperation.m b/SmartDeviceLink/SDLSoftButtonReplaceOperation.m new file mode 100644 index 000000000..f54e2eddf --- /dev/null +++ b/SmartDeviceLink/SDLSoftButtonReplaceOperation.m @@ -0,0 +1,293 @@ +// +// SDLSoftButtonReplaceOperation.m +// SmartDeviceLink +// +// Created by Joel Fischer on 4/25/19. +// Copyright © 2019 smartdevicelink. All rights reserved. +// + +#import "SDLSoftButtonReplaceOperation.h" + +#import "SDLArtwork.h" +#import "SDLConnectionManagerType.h" +#import "SDLDisplayCapabilities+ShowManagerExtensions.h" +#import "SDLFileManager.h" +#import "SDLLogMacros.h" +#import "SDLShow.h" +#import "SDLSoftButton.h" +#import "SDLSoftButtonCapabilities.h" +#import "SDLSoftButtonObject.h" +#import "SDLSoftButtonState.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLSoftButtonReplaceOperation () + +@property (strong, nonatomic) SDLSoftButtonCapabilities *softButtonCapabilities; +@property (strong, nonatomic) NSArray<SDLSoftButtonObject *> *softButtonObjects; + +@property (weak, nonatomic) id<SDLConnectionManagerType> connectionManager; +@property (weak, nonatomic) SDLFileManager *fileManager; +@property (copy, nonatomic, nullable) NSError *internalError; + +@end + +@implementation SDLSoftButtonReplaceOperation + +- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager fileManager:(SDLFileManager *)fileManager capabilities:(SDLSoftButtonCapabilities *)capabilities softButtonObjects:(NSArray<SDLSoftButtonObject *> *)softButtonObjects mainField1:(NSString *)mainField1 { + self = [super init]; + if (!self) { return nil; } + + _connectionManager = connectionManager; + _softButtonCapabilities = capabilities; + _softButtonObjects = softButtonObjects; + _mainField1 = mainField1; + + return self; +} + +- (void)start { + [super start]; + + // Check the state of our images + if (![self sdl_supportsSoftButtonImages]) { + // We don't support images at all + SDLLogW(@"Soft button images are not supported. Attempting to send text-only soft buttons. If any button does not contain text, no buttons will be sent."); + + // Send text buttons if all the soft buttons have text + [self sdl_sendCurrentStateTextOnlySoftButtonsWithCompletionHandler:^{}]; + } else if ([self sdl_currentStateHasImages] && ![self sdl_allCurrentStateImagesAreUploaded]) { + // If there are images that aren't uploaded + // Send text buttons if all the soft buttons have text + [self sdl_sendCurrentStateTextOnlySoftButtonsWithCompletionHandler:^{}]; + + // Upload initial images + __weak typeof(self) weakself = self; + [self sdl_uploadInitialStateImagesWithCompletionHandler:^{ + // Send initial soft buttons w/ images + [weakself sdl_sendCurrentStateSoftButtonsWithCompletionHandler:^{ + // Upload other images + [weakself sdl_uploadOtherStateImagesWithCompletionHandler:^{ + __strong typeof(weakself) strongself = weakself; + SDLLogV(@"Finished sending other images for soft buttons"); + [strongself finishOperation]; + }]; + }]; + }]; + } else { + // All the images are already uploaded. Send initial soft buttons w/ images. + __weak typeof(self) weakself = self; + [self sdl_sendCurrentStateSoftButtonsWithCompletionHandler:^{ + __strong typeof(weakself) strongself = weakself; + SDLLogV(@"Finished sending soft buttons with images"); + [strongself finishOperation]; + }]; + } +} + + +#pragma mark - Uploading Images + +- (void)sdl_uploadInitialStateImagesWithCompletionHandler:(void (^)(void))handler { + NSMutableArray<SDLArtwork *> *initialStatesToBeUploaded = [NSMutableArray array]; + // Upload all soft button images, the initial state images first, then the other states. We need to send updates when the initial state is ready. + for (SDLSoftButtonObject *object in self.softButtonObjects) { + if ([self sdl_artworkNeedsUpload:object.currentState.artwork]) { + [initialStatesToBeUploaded addObject:object.currentState.artwork]; + } + } + + if (initialStatesToBeUploaded.count == 0) { + SDLLogV(@"No initial state artworks to upload"); + handler(); + return; + } + + SDLLogD(@"Uploading soft button initial artworks"); + NSMutableArray<NSString *> *failedArtworkNames = [NSMutableArray array]; + __weak typeof(self) weakself = self; + [self.fileManager uploadArtworks:[initialStatesToBeUploaded copy] progressHandler:^BOOL(NSString * _Nonnull artworkName, float uploadPercentage, NSError * _Nullable error) { + SDLLogV(@"Uploaded initial state artwork: %@, error: %@, percent complete: %f.2%%", artworkName, error, uploadPercentage * 100); + if (error != nil) { + [failedArtworkNames addObject:artworkName]; + } + + if (weakself.isCancelled) { + [weakself finishOperation]; + return NO; + } + + return YES; + } completionHandler:^(NSArray<NSString *> * _Nonnull artworkNames, NSError * _Nullable error) { + if (error != nil) { + SDLLogE(@"Error uploading soft button artworks: %@", error); + } else { + SDLLogD(@"Soft button initial state artworks uploaded"); + } + + handler(); + }]; +} + +- (void)sdl_uploadOtherStateImagesWithCompletionHandler:(void (^)(void))handler { + NSMutableArray<SDLArtwork *> *otherStatesToBeUploaded = [NSMutableArray array]; + // Upload all soft button images, the initial state images first, then the other states. We need to send updates when the initial state is ready. + for (SDLSoftButtonObject *object in self.softButtonObjects) { + for (SDLSoftButtonState *state in object.states) { + if ([state.name isEqualToString:object.currentState.name]) { continue; } + if ([self sdl_artworkNeedsUpload:state.artwork]) { + [otherStatesToBeUploaded addObject:state.artwork]; + } + } + } + + if (otherStatesToBeUploaded.count == 0) { + SDLLogV(@"No other state artworks to upload"); + handler(); + return; + } + + SDLLogD(@"Uploading soft button other state artworks"); + NSMutableArray<NSString *> *failedArtworkNames = [NSMutableArray array]; + __weak typeof(self) weakself = self; + [self.fileManager uploadArtworks:[otherStatesToBeUploaded copy] progressHandler:^BOOL(NSString * _Nonnull artworkName, float uploadPercentage, NSError * _Nullable error) { + SDLLogV(@"Uploaded other state artwork: %@, error: %@, percent complete: %f.2%%", artworkName, error, uploadPercentage * 100); + if (error != nil) { + [failedArtworkNames addObject:artworkName]; + } + + if (weakself.isCancelled) { + [weakself finishOperation]; + return NO; + } + + return YES; + } completionHandler:^(NSArray<NSString *> * _Nonnull artworkNames, NSError * _Nullable error) { + if (error != nil) { + SDLLogE(@"Error uploading soft button artworks: %@", error); + } else { + SDLLogD(@"Soft button other state artworks uploaded"); + } + + handler(); + }]; +} + + +#pragma mark - Sending the Soft Buttons + +- (void)sdl_sendCurrentStateSoftButtonsWithCompletionHandler:(void (^)(void))handler { + if (self.isCancelled) { + [self finishOperation]; + } + + SDLLogV(@"Preparing to send full soft buttons"); + NSMutableArray<SDLSoftButton *> *softButtons = [NSMutableArray arrayWithCapacity:self.softButtonObjects.count]; + for (SDLSoftButtonObject *buttonObject in self.softButtonObjects) { + [softButtons addObject:buttonObject.currentStateSoftButton]; + } + + SDLShow *show = [[SDLShow alloc] init]; + show.mainField1 = self.mainField1; + show.softButtons = [softButtons copy]; + + [self.connectionManager sendConnectionRequest:show withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { + if (error != nil) { + SDLLogW(@"Failed to update soft buttons with text buttons: %@", error); + } + + SDLLogD(@"Finished sending text only soft buttons"); + handler(); + }]; +} + +/** + Returns text soft buttons representing the current states of the button objects, or returns if _any_ of the buttons' current states are image only buttons. +*/ +- (void)sdl_sendCurrentStateTextOnlySoftButtonsWithCompletionHandler:(void (^)(void))handler { + if (self.isCancelled) { + [self finishOperation]; + } + + SDLLogV(@"Preparing to send text-only soft buttons"); + NSMutableArray<SDLSoftButton *> *textButtons = [NSMutableArray arrayWithCapacity:self.softButtonObjects.count]; + for (SDLSoftButtonObject *buttonObject in self.softButtonObjects) { + SDLSoftButton *button = buttonObject.currentStateSoftButton; + if (button.text == nil) { + SDLLogW(@"Attempted to create text buttons, but some buttons don't support text, so no text-only soft buttons will be sent"); + handler(); + return; + } + + button.image = nil; + button.type = SDLSoftButtonTypeText; + [textButtons addObject:button]; + } + + SDLShow *show = [[SDLShow alloc] init]; + show.mainField1 = self.mainField1; + show.softButtons = [textButtons copy]; + + [self.connectionManager sendConnectionRequest:show withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { + if (error != nil) { + SDLLogW(@"Failed to update soft buttons with text buttons: %@", error); + } + + SDLLogD(@"Finished sending text only soft buttons"); + handler(); + }]; +} + +#pragma mark - Images + +- (BOOL)sdl_artworkNeedsUpload:(SDLArtwork *)artwork { + return (artwork != nil && ![self.fileManager hasUploadedFile:artwork] && !artwork.isStaticIcon); +} + +- (BOOL)sdl_currentStateHasImages { + for (SDLSoftButtonObject *object in self.softButtonObjects) { + if (object.currentState.artwork != nil) { + return YES; + } + } + + return NO; +} + +- (BOOL)sdl_allCurrentStateImagesAreUploaded { + for (SDLSoftButtonObject *button in self.softButtonObjects) { + SDLArtwork *artwork = button.currentState.artwork; + if ([self sdl_artworkNeedsUpload:artwork]) { + return NO; + } + } + + return YES; +} + +- (BOOL)sdl_supportsSoftButtonImages { + return self.softButtonCapabilities ? self.softButtonCapabilities.imageSupported.boolValue : NO; +} + +#pragma mark - Property Overrides + +- (void)finishOperation { + SDLLogV(@"Finishing soft button replace operation"); + [super finishOperation]; +} + +- (nullable NSString *)name { + return @"com.sdl.softbuttonmanager.replace"; +} + +- (NSOperationQueuePriority)queuePriority { + return NSOperationQueuePriorityNormal; +} + +- (nullable NSError *)error { + return self.internalError; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLSoftButtonTransitionOperation.h b/SmartDeviceLink/SDLSoftButtonTransitionOperation.h new file mode 100644 index 000000000..f167028b4 --- /dev/null +++ b/SmartDeviceLink/SDLSoftButtonTransitionOperation.h @@ -0,0 +1,28 @@ +// +// SDLSoftButtonTransitionOperation.h +// SmartDeviceLink +// +// Created by Joel Fischer on 4/25/19. +// Copyright © 2019 smartdevicelink. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#import "SDLAsynchronousOperation.h" + +@class SDLSoftButtonCapabilities; +@class SDLSoftButton; + +@protocol SDLConnectionManagerType; + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLSoftButtonTransitionOperation : SDLAsynchronousOperation + +@property (strong, nonatomic) NSString *mainField1; + +- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager capabilities:(SDLSoftButtonCapabilities *)capabilities softButtons:(NSArray<SDLSoftButton *> *)softButtons mainField1:(NSString *)mainField1; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLSoftButtonTransitionOperation.m b/SmartDeviceLink/SDLSoftButtonTransitionOperation.m new file mode 100644 index 000000000..9ea155e0f --- /dev/null +++ b/SmartDeviceLink/SDLSoftButtonTransitionOperation.m @@ -0,0 +1,78 @@ +// +// SDLSoftButtonTransitionOperation.m +// SmartDeviceLink +// +// Created by Joel Fischer on 4/25/19. +// Copyright © 2019 smartdevicelink. All rights reserved. +// + +#import "SDLSoftButtonTransitionOperation.h" + +#import "SDLConnectionManagerType.h" +#import "SDLSoftButtonCapabilities.h" +#import "SDLShow.h" +#import "SDLSoftButton.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLSoftButtonTransitionOperation () + +@property (strong, nonatomic) SDLSoftButtonCapabilities *softButtonCapabilities; +@property (strong, nonatomic) NSArray<SDLSoftButton *> *softButtons; + +@property (weak, nonatomic) id<SDLConnectionManagerType> connectionManager; +@property (copy, nonatomic, nullable) NSError *internalError; + +@end + +@implementation SDLSoftButtonTransitionOperation + +- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager capabilities:(SDLSoftButtonCapabilities *)capabilities softButtons:(NSArray<SDLSoftButton *> *)softButtons mainField1:(NSString *)mainField1 { + self = [super init]; + if (!self) { return nil; } + + _connectionManager = connectionManager; + _softButtonCapabilities = capabilities; + _softButtons = softButtons; + _mainField1 = mainField1; + + return self; +} + +- (void)start { + [super start]; + + [self sdl_sendNewSoftButtons]; +} + +- (void)sdl_sendNewSoftButtons { + SDLShow *newShow = [[SDLShow alloc] init]; + newShow.mainField1 = self.mainField1; + newShow.softButtons = self.softButtons; + + [self.connectionManager sendConnectionRequest:newShow withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { + if (error != nil) { + self.internalError = error; + } + + [self finishOperation]; + }]; +} + +#pragma mark - Property Overrides + +- (nullable NSString *)name { + return @"com.sdl.softbuttonmanager.transition"; +} + +- (NSOperationQueuePriority)queuePriority { + return NSOperationQueuePriorityNormal; +} + +- (nullable NSError *)error { + return self.internalError; +} + +@end + +NS_ASSUME_NONNULL_END |