summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Fischer <joeljfischer@gmail.com>2019-04-25 16:50:02 -0400
committerJoel Fischer <joeljfischer@gmail.com>2019-04-25 16:50:02 -0400
commit9e4090122a81e91c3cb445aaa271c23041638453 (patch)
tree45048f032543059e10cf38a4acb16bd3264ad3d0
parentf2f33e8a9c65914fdf5ee683918ab0f3590dc29d (diff)
downloadsdl_ios-9e4090122a81e91c3cb445aaa271c23041638453.tar.gz
Initial work on re-doing the soft button manager
-rw-r--r--Example Apps/Example ObjC/ButtonManager.m3
-rw-r--r--SmartDeviceLink-iOS.xcodeproj/project.pbxproj42
-rw-r--r--SmartDeviceLink/SDLChoiceSetManager.m2
-rw-r--r--SmartDeviceLink/SDLSoftButtonManager.h7
-rw-r--r--SmartDeviceLink/SDLSoftButtonManager.m295
-rw-r--r--SmartDeviceLink/SDLSoftButtonReplaceOperation.h27
-rw-r--r--SmartDeviceLink/SDLSoftButtonReplaceOperation.m293
-rw-r--r--SmartDeviceLink/SDLSoftButtonTransitionOperation.h28
-rw-r--r--SmartDeviceLink/SDLSoftButtonTransitionOperation.m78
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