From a30ce6e4ce1a772384691607f6b556a19f095ef6 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 21 Jan 2021 16:15:07 -0500 Subject: [PATCH 001/112] First round of updates for the menu manager * Added stubs for operations * Added configuration update operation * Added open menu operation --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 46 ++++- SmartDeviceLink/private/SDLError.h | 7 + SmartDeviceLink/private/SDLError.m | 41 +++++ .../SDLMenuConfigurationUpdateOperation.h | 23 +++ .../SDLMenuConfigurationUpdateOperation.m | 100 +++++++++++ SmartDeviceLink/private/SDLMenuManager.m | 168 +++++++++++------- .../private/SDLMenuReplaceDynamicOperation.h | 21 +++ .../private/SDLMenuReplaceDynamicOperation.m | 13 ++ .../private/SDLMenuReplaceStaticOperation.h | 23 +++ .../private/SDLMenuReplaceStaticOperation.m | 13 ++ .../private/SDLMenuShowOperation.h | 24 +++ .../private/SDLMenuShowOperation.m | 104 +++++++++++ .../private/SDLVoiceCommandManager.m | 2 + SmartDeviceLink/public/SDLErrorConstants.h | 6 +- .../DevAPISpecs/SDLMenuManagerSpec.m | 6 +- 15 files changed, 526 insertions(+), 71 deletions(-) create mode 100644 SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h create mode 100644 SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m create mode 100644 SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h create mode 100644 SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m create mode 100644 SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h create mode 100644 SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m create mode 100644 SmartDeviceLink/private/SDLMenuShowOperation.h create mode 100644 SmartDeviceLink/private/SDLMenuShowOperation.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 2773665f2..e472ee38a 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -511,6 +511,14 @@ 4A8BD3CD24F999BE000945E3 /* TestSubscribeButtonObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8BD3CC24F999BE000945E3 /* TestSubscribeButtonObserver.m */; }; 4A8BD3D024FE7CF1000945E3 /* SDLPermissionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8BD3CE24FE7CF1000945E3 /* SDLPermissionManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4A8BD3D124FE7CF1000945E3 /* SDLPermissionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8BD3CF24FE7CF1000945E3 /* SDLPermissionManager.m */; }; + 4A93893D25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93893B25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.h */; }; + 4A93893E25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93893C25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.m */; }; + 4A93894525B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93894325B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h */; }; + 4A93894625B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93894425B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m */; }; + 4A93895325B9DACA0069F438 /* SDLMenuShowOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93895125B9DACA0069F438 /* SDLMenuShowOperation.h */; }; + 4A93895425B9DACA0069F438 /* SDLMenuShowOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93895225B9DACA0069F438 /* SDLMenuShowOperation.m */; }; + 4A93895925B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93895725B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h */; }; + 4A93895A25B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93895825B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m */; }; 4ABB24BA24F592620061BF55 /* NSMutableArray+Safe.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ABB24B224F592620061BF55 /* NSMutableArray+Safe.h */; }; 4ABB24BB24F592620061BF55 /* NSMutableArray+Safe.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABB24B324F592620061BF55 /* NSMutableArray+Safe.m */; }; 4ABB24BC24F592620061BF55 /* NSBundle+SDLBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABB24B424F592620061BF55 /* NSBundle+SDLBundle.m */; }; @@ -2297,6 +2305,14 @@ 4A8BD3CC24F999BE000945E3 /* TestSubscribeButtonObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestSubscribeButtonObserver.m; sourceTree = ""; }; 4A8BD3CE24FE7CF1000945E3 /* SDLPermissionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLPermissionManager.h; path = public/SDLPermissionManager.h; sourceTree = ""; }; 4A8BD3CF24FE7CF1000945E3 /* SDLPermissionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLPermissionManager.m; path = public/SDLPermissionManager.m; sourceTree = ""; }; + 4A93893B25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuReplaceDynamicOperation.h; path = private/SDLMenuReplaceDynamicOperation.h; sourceTree = ""; }; + 4A93893C25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceDynamicOperation.m; path = private/SDLMenuReplaceDynamicOperation.m; sourceTree = ""; }; + 4A93894325B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuReplaceStaticOperation.h; path = private/SDLMenuReplaceStaticOperation.h; sourceTree = ""; }; + 4A93894425B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceStaticOperation.m; path = private/SDLMenuReplaceStaticOperation.m; sourceTree = ""; }; + 4A93895125B9DACA0069F438 /* SDLMenuShowOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuShowOperation.h; path = private/SDLMenuShowOperation.h; sourceTree = ""; }; + 4A93895225B9DACA0069F438 /* SDLMenuShowOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuShowOperation.m; path = private/SDLMenuShowOperation.m; sourceTree = ""; }; + 4A93895725B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuConfigurationUpdateOperation.h; path = private/SDLMenuConfigurationUpdateOperation.h; sourceTree = ""; }; + 4A93895825B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuConfigurationUpdateOperation.m; path = private/SDLMenuConfigurationUpdateOperation.m; sourceTree = ""; }; 4ABB24B224F592620061BF55 /* NSMutableArray+Safe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSMutableArray+Safe.h"; path = "private/NSMutableArray+Safe.h"; sourceTree = ""; }; 4ABB24B324F592620061BF55 /* NSMutableArray+Safe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSMutableArray+Safe.m"; path = "private/NSMutableArray+Safe.m"; sourceTree = ""; }; 4ABB24B424F592620061BF55 /* NSBundle+SDLBundle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSBundle+SDLBundle.m"; path = "private/NSBundle+SDLBundle.m"; sourceTree = ""; }; @@ -4127,7 +4143,7 @@ path = MessageSpecs; sourceTree = ""; }; - 4A32B3E425559D93001FFA26 /* Voice Cammands */ = { + 4A32B3E425559D93001FFA26 /* Voice Commands */ = { isa = PBXGroup; children = ( 4A32B3E525559DA4001FFA26 /* Cells */, @@ -4135,7 +4151,7 @@ 4ABB25A824F7E6E10061BF55 /* SDLVoiceCommandManager.h */, 4ABB25A724F7E6E10061BF55 /* SDLVoiceCommandManager.m */, ); - name = "Voice Cammands"; + name = "Voice Commands"; sourceTree = ""; }; 4A32B3E525559DA4001FFA26 /* Cells */ = { @@ -4159,6 +4175,7 @@ 4A32B3F325559F37001FFA26 /* Menu */ = { isa = PBXGroup; children = ( + 4A93893A25B8CB480069F438 /* Operations */, 5D76751022D907F500E8D71A /* Configuration */, 755F175E229F14F70041B9CB /* Dynamic Menu Update Utilities */, 5D339CEC207C08AB000CC364 /* Cells */, @@ -4242,6 +4259,21 @@ path = Utilities; sourceTree = ""; }; + 4A93893A25B8CB480069F438 /* Operations */ = { + isa = PBXGroup; + children = ( + 4A93895725B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h */, + 4A93895825B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m */, + 4A93893B25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.h */, + 4A93893C25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.m */, + 4A93894325B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h */, + 4A93894425B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m */, + 4A93895125B9DACA0069F438 /* SDLMenuShowOperation.h */, + 4A93895225B9DACA0069F438 /* SDLMenuShowOperation.m */, + ); + name = Operations; + sourceTree = ""; + }; 4A9D02BB2497EEB400FBE99B /* Custom RPC Adapters */ = { isa = PBXGroup; children = ( @@ -4443,7 +4475,7 @@ isa = PBXGroup; children = ( 4A32B3F325559F37001FFA26 /* Menu */, - 4A32B3E425559D93001FFA26 /* Voice Cammands */, + 4A32B3E425559D93001FFA26 /* Voice Commands */, ); name = Menu; sourceTree = ""; @@ -6908,6 +6940,7 @@ 4ABB2A3624F847980061BF55 /* SDLDialNumberResponse.h in Headers */, 4ABB297724F844D30061BF55 /* SDLPerformAppServiceInteraction.h in Headers */, 4ABB27E124F800CA0061BF55 /* SDLPowerModeQualificationStatus.h in Headers */, + 4A93894525B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h in Headers */, 4A8BD2D124F93803000945E3 /* SDLTemperature.h in Headers */, 4ABB282624F824E70061BF55 /* SDLTemperatureUnit.h in Headers */, 4ABB262424F7F3A30061BF55 /* CVPixelBufferRef+SDLUtil.h in Headers */, @@ -6975,6 +7008,7 @@ 4ABB27DE24F800CA0061BF55 /* SDLPredefinedLayout.h in Headers */, 4ABB27E424F800CA0061BF55 /* SDLPrerecordedSpeech.h in Headers */, 4ABB2A2E24F847980061BF55 /* SDLDeleteCommandResponse.h in Headers */, + 4A93895325B9DACA0069F438 /* SDLMenuShowOperation.h in Headers */, 4ABB277524F7FE910061BF55 /* SDLIgnitionStatus.h in Headers */, 4A8BD27924F9343F000945E3 /* SDLPermissionItem.h in Headers */, 4ABB2B5024F84EF50061BF55 /* SDLClusterModeStatus.h in Headers */, @@ -7018,6 +7052,7 @@ 4ABB2AA724F847F40061BF55 /* SDLShowConstantTBTResponse.h in Headers */, 4ABB28ED24F82A6A0061BF55 /* SDLOnKeyboardInput.h in Headers */, 5D9FDA991F2A7D3F00A495C8 /* emhashmap.h in Headers */, + 4A93893D25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.h in Headers */, 4ABB255F24F7E59E0061BF55 /* SDLPermissionConstants.h in Headers */, 4ABB270324F7FB8F0061BF55 /* SDLButtonName.h in Headers */, 4ABB25B324F7E6F60061BF55 /* SDLSoftButtonTransitionOperation.h in Headers */, @@ -7076,6 +7111,7 @@ 4ABB258124F7E61E0061BF55 /* SDLChoiceCell.h in Headers */, 4ABB270124F7FB8F0061BF55 /* SDLBitsPerSample.h in Headers */, 4ABB253224F7E43A0061BF55 /* SDLAsynchronousRPCOperation.h in Headers */, + 4A93895925B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h in Headers */, 4ABB25CC24F7E74F0061BF55 /* SDLTextAndGraphicManager.h in Headers */, 4A8BD3B824F98F64000945E3 /* SDLOnUpdateSubMenu.h in Headers */, 4ABB291324F842160061BF55 /* SDLCreateInteractionChoiceSet.h in Headers */, @@ -7847,6 +7883,7 @@ 4ABB2B9E24F850AE0061BF55 /* SDLImage.m in Sources */, 4A40254324FFDA660080E159 /* SDLTextAndGraphicUpdateOperation.m in Sources */, 4ABB29C124F845DB0061BF55 /* SDLShow.m in Sources */, + 4A93894625B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m in Sources */, 4ABB2BA524F850AE0061BF55 /* SDLLightControlData.m in Sources */, 4ABB272724F7FCAE0061BF55 /* SDLDeliveryMode.m in Sources */, 4ABB295F24F844020061BF55 /* SDLGetSystemCapability.m in Sources */, @@ -7874,6 +7911,7 @@ 4ABB292D24F842A00061BF55 /* SDLDeleteWindow.m in Sources */, 4ABB299A24F845440061BF55 /* SDLSendHapticData.m in Sources */, 4ABB29DB24F846880061BF55 /* SDLUnregisterAppInterface.m in Sources */, + 4A93895425B9DACA0069F438 /* SDLMenuShowOperation.m in Sources */, 4ABB270824F7FB8F0061BF55 /* SDLAppHMIType.m in Sources */, 4ABB282A24F824E70061BF55 /* SDLSystemAction.m in Sources */, 4ABB297824F844D30061BF55 /* SDLPublishAppService.m in Sources */, @@ -8064,6 +8102,7 @@ 4ABB28C924F82A6A0061BF55 /* SDLOnRCStatus.m in Sources */, 4ABB253124F7E43A0061BF55 /* SDLAsynchronousRPCOperation.m in Sources */, 4ABB2A7524F847D40061BF55 /* SDLPublishAppServiceResponse.m in Sources */, + 4A93893E25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.m in Sources */, 4A8BD3AA24F948CF000945E3 /* SDLDeleteFileOperation.m in Sources */, 4ABB2B4524F84EF50061BF55 /* SDLBodyInformation.m in Sources */, 4ABB25BB24F7E70E0061BF55 /* SDLSoftButtonObject.m in Sources */, @@ -8097,6 +8136,7 @@ 4A8BD31124F938D6000945E3 /* SDLWeatherServiceManifest.m in Sources */, 4ABB271524F7FC4E0061BF55 /* SDLCompassDirection.m in Sources */, 4ABB254424F7E48D0061BF55 /* SDLLockScreenRootViewController.m in Sources */, + 4A93895A25B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m in Sources */, 4ABB265F24F7F5F20061BF55 /* SDLNotificationDispatcher.m in Sources */, 4A8BD31624F938D6000945E3 /* SDLWindowTypeCapabilities.m in Sources */, 4ABB280924F824600061BF55 /* SDLSoftButtonType.m in Sources */, diff --git a/SmartDeviceLink/private/SDLError.h b/SmartDeviceLink/private/SDLError.h index fae799fe9..0b6a7d9a5 100644 --- a/SmartDeviceLink/private/SDLError.h +++ b/SmartDeviceLink/private/SDLError.h @@ -11,6 +11,9 @@ #import "SDLErrorConstants.h" #import "SDLResult.h" +@class SDLMenuCell; +@class SDLMenuConfiguration; + NS_ASSUME_NONNULL_BEGIN @@ -54,6 +57,10 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark Menu Manager ++ (NSError *)sdl_menuManager_configurationOperationCancelled; ++ (NSError *)sdl_menuManager_configurationOperationFailed:(SDLMenuConfiguration *)failedConfiguration; ++ (NSError *)sdl_menuManager_openMenuOperationCancelled; ++ (NSError *)sdl_menuManager_openMenuOperationFailed:(nullable SDLMenuCell *)menuCell; + (NSError *)sdl_menuManager_failedToUpdateWithDictionary:(NSDictionary *)userInfo; + (NSError *)sdl_voiceCommandManager_pendingUpdateSuperseded; diff --git a/SmartDeviceLink/private/SDLError.m b/SmartDeviceLink/private/SDLError.m index a54cee83e..57fab7809 100644 --- a/SmartDeviceLink/private/SDLError.m +++ b/SmartDeviceLink/private/SDLError.m @@ -9,6 +9,7 @@ #import "SDLError.h" #import "SDLChoiceSetManager.h" +#import "SDLMenuConfiguration.h" NS_ASSUME_NONNULL_BEGIN @@ -258,6 +259,46 @@ + (NSError *)sdl_subscribeButtonManager_notSubscribed { #pragma mark Menu Manager ++ (NSError *)sdl_menuManager_configurationOperationCancelled { + return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorConfigurationUpdateCancelled userInfo:@{ + NSLocalizedDescriptionKey: @"Menu Manager - Configuration Update Cancelled", + NSLocalizedFailureReasonErrorKey: @"The menu manager was probably stopped or another configuration update was requested.", + NSLocalizedRecoverySuggestionErrorKey: @"This error probably does not need recovery." + }]; +} + ++ (NSError *)sdl_menuManager_configurationOperationFailed:(SDLMenuConfiguration *)failedConfiguration { + return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorConfigurationUpdateFailed userInfo:@{ + @"Failed Configuration": failedConfiguration, + NSLocalizedDescriptionKey: @"Menu Manager - Configuration Update Failed", + NSLocalizedFailureReasonErrorKey: @"The configuration may not be supported by the connected head unit", + NSLocalizedRecoverySuggestionErrorKey: @"Check SystemCapabilityManager.defaultWindowCapability.menuLayouts to ensure the set configuration is supported" + }]; +} + ++ (NSError *)sdl_menuManager_openMenuOperationCancelled { + return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorConfigurationUpdateCancelled userInfo:@{ + NSLocalizedDescriptionKey: @"Menu Manager - Open Menu Cancelled", + NSLocalizedFailureReasonErrorKey: @"The menu manager was probably stopped or opening another menu item was requested.", + NSLocalizedRecoverySuggestionErrorKey: @"This error probably does not need recovery." + }]; +} + ++ (NSError *)sdl_menuManager_openMenuOperationFailed:(nullable SDLMenuCell *)menuCell { + NSString *failureReason = nil; + if (menuCell != nil) { + failureReason = @"Something went wrong attempting to open the menu." + } else { + failureReason = [NSString stringWithFormat:@"Something went wrong attempting to open the menu to the given subcell: %@", menuCell]; + } + + return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorOpenMenuFailed userInfo:@{ + NSLocalizedDescriptionKey: @"Menu Manager - Open Menu Failed", + NSLocalizedFailureReasonErrorKey: failureReason, + NSLocalizedRecoverySuggestionErrorKey: @"Check the error logs for more information on the RPC failure." + }]; +} + + (NSError *)sdl_menuManager_failedToUpdateWithDictionary:(NSDictionary *)userInfo { return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorRPCsFailed userInfo:userInfo]; } diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h new file mode 100644 index 000000000..21fd78d70 --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h @@ -0,0 +1,23 @@ +// +// SDLMenuConfigurationUpdateOperation.h +// SmartDeviceLink +// +// Created by Joel Fischer on 1/21/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import "SDLAsynchronousOperation.h" + +#import "SDLConnectionManagerType.h" +#import "SDLMenuConfiguration.h" +#import "SDLWindowCapability.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLMenuConfigurationUpdateOperation : SDLAsynchronousOperation + +- (instancetype)initWithConnectionManager:(id)connectionManager windowCapability:(SDLWindowCapability *)windowCapability newMenuConfiguration:(SDLMenuConfiguration *)newConfiguration; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m new file mode 100644 index 000000000..146f38b02 --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m @@ -0,0 +1,100 @@ +// +// SDLMenuConfigurationUpdateOperation.m +// SmartDeviceLink +// +// Created by Joel Fischer on 1/21/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import "SDLMenuConfigurationUpdateOperation.h" + +#import "SDLError.h" +#import "SDLGlobals.h" +#import "SDLLogMacros.h" +#import "SDLSetGlobalProperties.h" +#import "SDLVersion.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLMenuConfigurationUpdateOperation () + +@property (weak, nonatomic) id connectionManager; +@property (strong, nonatomic) NSArray *availableMenuLayouts; +@property (strong, nonatomic) SDLMenuConfiguration *updatedMenuConfiguration; + +@property (copy, nonatomic, nullable) NSError *internalError; + +@end + +@implementation SDLMenuConfigurationUpdateOperation + +- (instancetype)initWithConnectionManager:(id)connectionManager windowCapability:(SDLWindowCapability *)windowCapability newMenuConfiguration:(SDLMenuConfiguration *)newConfiguration { + self = [super init]; + if (!self) { return nil; } + + _connectionManager = connectionManager; + _availableMenuLayouts = windowCapability.menuLayoutsAvailable; + _updatedMenuConfiguration = newConfiguration; + + return self; +} + +- (void)start { + [super start]; + if (self.isCancelled) { + [self finishOperation]; + return; + } + + if ([[SDLGlobals sharedGlobals].rpcVersion isLessThanVersion:[SDLVersion versionWithMajor:6 minor:0 patch:0]]) { + SDLLogW(@"Menu configurations is only supported on head units with RPC spec version 6.0.0 or later. Currently connected head unit RPC spec version is %@", [SDLGlobals sharedGlobals].rpcVersion); + return; + } else if (self.availableMenuLayouts == nil) { + SDLLogW(@"Could not set the main menu configuration. Which menu layouts can be used is not available"); + return; + } else if (![self.availableMenuLayouts containsObject:self.updatedMenuConfiguration.mainMenuLayout] + || ![self.availableMenuLayouts containsObject:self.updatedMenuConfiguration.defaultSubmenuLayout]) { + SDLLogE(@"One or more of the set menu layouts are not available on this system. The menu configuration will not be set. Available menu layouts: %@, set menu layouts: %@", self.availableMenuLayouts, self.updatedMenuConfiguration); + return; + } + + SDLSetGlobalProperties *setGlobalsRPC = [[SDLSetGlobalProperties alloc] init]; + setGlobalsRPC.menuLayout = self.updatedMenuConfiguration.mainMenuLayout; + + __weak typeof(self) weakself = self; + [self.connectionManager sendConnectionRequest:setGlobalsRPC withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { + __strong typeof(weakself) strongself = weakself; + if (error != nil) { + strongself.internalError = [NSError sdl_menuManager_configurationOperationFailed:strongself.updatedMenuConfiguration]; + } + + [strongself finishOperation]; + }]; +} + +#pragma mark - Operation Overrides + +- (void)finishOperation { + SDLLogV(@"Finishing menu manager configuration update operation"); + if (self.isCancelled) { + self.internalError = [NSError sdl_menuManager_configurationOperationCancelled]; + } + + [super finishOperation]; +} + +- (nullable NSString *)name { + return @"com.sdl.menuManager.configurationUpdate"; +} + +- (NSOperationQueuePriority)queuePriority { + return NSOperationQueuePriorityNormal; +} + +- (nullable NSError *)error { + return self.internalError; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index b78f8ee99..10726d460 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -16,6 +16,8 @@ #import "SDLDeleteSubMenu.h" #import "SDLDisplayCapability.h" #import "SDLDisplayType.h" +#import "SDLDynamicMenuUpdateRunScore.h" +#import "SDLDynamicMenuUpdateAlgorithm.h" #import "SDLError.h" #import "SDLFileManager.h" #import "SDLGlobals.h" @@ -23,9 +25,9 @@ #import "SDLLogMacros.h" #import "SDLMenuCell.h" #import "SDLMenuConfiguration.h" +#import "SDLMenuConfigurationUpdateOperation.h" #import "SDLMenuParams.h" -#import "SDLDynamicMenuUpdateRunScore.h" -#import "SDLDynamicMenuUpdateAlgorithm.h" +#import "SDLMenuShowOperation.h" #import "SDLOnCommand.h" #import "SDLOnHMIStatus.h" #import "SDLPredefinedWindows.h" @@ -58,17 +60,17 @@ @interface SDLMenuManager() @property (weak, nonatomic) SDLFileManager *fileManager; @property (weak, nonatomic) SDLSystemCapabilityManager *systemCapabilityManager; -@property (copy, nonatomic, nullable) SDLHMILevel currentHMILevel; -@property (copy, nonatomic, nullable) SDLSystemContext currentSystemContext; - -@property (strong, nonatomic, nullable) NSArray *inProgressUpdate; -@property (assign, nonatomic) BOOL hasQueuedUpdate; -@property (assign, nonatomic) BOOL waitingOnHMIUpdate; +@property (strong, nonatomic) NSOperationQueue *transactionQueue; @property (copy, nonatomic) NSArray *waitingUpdateMenuCells; @property (strong, nonatomic, nullable) SDLWindowCapability *windowCapability; +@property (copy, nonatomic, nullable) SDLHMILevel currentHMILevel; +@property (copy, nonatomic, nullable) SDLSystemContext currentSystemContext; + @property (assign, nonatomic) UInt32 lastMenuId; -@property (copy, nonatomic) NSArray *oldMenuCells; +@property (copy, nonatomic) NSArray *currentMenuCells; + +@property (strong, nonatomic) SDLMenuConfiguration *oldMenuConfiguration; @end @@ -84,8 +86,9 @@ - (instancetype)init { _lastMenuId = MenuCellIdMin; _menuConfiguration = [[SDLMenuConfiguration alloc] init]; _menuCells = @[]; - _oldMenuCells = @[]; + _currentMenuCells = @[]; _dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeOnWithCompatibility; + _transactionQueue = [self sdl_newTransactionQueue]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_hmiStatusNotification:) name:SDLDidChangeHMIStatusNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_commandNotification:) name:SDLDidReceiveCommandNotification object:nil]; @@ -111,52 +114,71 @@ - (void)start { - (void)stop { _lastMenuId = MenuCellIdMin; _menuCells = @[]; - _oldMenuCells = @[]; + _currentMenuCells = @[]; + _transactionQueue = [self sdl_newTransactionQueue]; _currentHMILevel = nil; _currentSystemContext = SDLSystemContextMain; - _inProgressUpdate = nil; - _hasQueuedUpdate = NO; - _waitingOnHMIUpdate = NO; - _waitingUpdateMenuCells = @[]; +} + +#pragma mark Transaction Queue + +- (NSOperationQueue *)sdl_newTransactionQueue { + NSOperationQueue *queue = [[NSOperationQueue alloc] init]; + queue.name = @"SDLMenuManager Transaction Queue"; + queue.maxConcurrentOperationCount = 1; + queue.qualityOfService = NSQualityOfServiceUserInitiated; + queue.underlyingQueue = [SDLGlobals sharedGlobals].sdlConcurrentQueue; + queue.suspended = YES; + + return queue; +} + +/// Suspend the queue if the HMI level is NONE since we want to delay sending RPCs until we're in non-NONE +- (void)sdl_updateTransactionQueueSuspended { + if ([self.currentLevel isEqualToEnum:SDLHMILevelNone]) { + SDLLogD(@"Suspending the transaction queue. Current HMI level is NONE: %@", ([self.currentLevel isEqualToEnum:SDLHMILevelNone] ? @"YES" : @"NO")); + self.transactionQueue.suspended = YES; + } else { + SDLLogD(@"Starting the transaction queue"); + self.transactionQueue.suspended = NO; + } } #pragma mark - Setters - (void)setMenuConfiguration:(SDLMenuConfiguration *)menuConfiguration { - NSArray *layoutsAvailable = self.windowCapability.menuLayoutsAvailable; - - if ([[SDLGlobals sharedGlobals].rpcVersion isLessThanVersion:[SDLVersion versionWithMajor:6 minor:0 patch:0]]) { - SDLLogW(@"Menu configurations is only supported on head units with RPC spec version 6.0.0 or later. Currently connected head unit RPC spec version is %@", [SDLGlobals sharedGlobals].rpcVersion); - return; - } else if (layoutsAvailable == nil) { - SDLLogW(@"Could not set the main menu configuration. Which menu layouts can be used is not available"); - return; - } else if (![layoutsAvailable containsObject:menuConfiguration.mainMenuLayout] - || ![layoutsAvailable containsObject:menuConfiguration.defaultSubmenuLayout]) { - SDLLogE(@"One or more of the set menu layouts are not available on this system. The menu configuration will not be set. Available menu layouts: %@, set menu layouts: %@", layoutsAvailable, menuConfiguration); - return; - } else if (self.currentHMILevel == nil - || [self.currentHMILevel isEqualToEnum:SDLHMILevelNone]) { - SDLLogE(@"Could not set main menu configuration, HMI level: %@, required: 'Not-NONE', system context: %@, required: 'Not MENU'", self.currentHMILevel, self.currentSystemContext); + if (menuConfiguration == self.menuConfiguration) { + SDLLogD(@"New menu configuration is equal to existing one, will not set new configuration"); return; } - SDLMenuConfiguration *oldConfig = _menuConfiguration; - _menuConfiguration = menuConfiguration; + // Keep the old configuration so that we can reset it if necessary + self.oldMenuConfiguration = self.menuConfiguration; - SDLSetGlobalProperties *setGlobalsRPC = [[SDLSetGlobalProperties alloc] init]; - setGlobalsRPC.menuLayout = menuConfiguration.mainMenuLayout; + // Create the operation + SDLMenuConfigurationUpdateOperation *configurationUpdateOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:self.connectionManager windowCapability:self.windowCapability newMenuConfiguration:menuConfiguration]; __weak typeof(self) weakself = self; - [self.connectionManager sendConnectionRequest:setGlobalsRPC withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { + configurationUpdateOp.completionBlock = ^{ __strong typeof(weakself) strongself = weakself; - if (error != nil) { - SDLLogE(@"Could not set main menu configuration: %@", error); - strongself.menuConfiguration = oldConfig; - return; + if (configurationUpdateOp.error != nil) { + SDLLogE(@"Error setting new menu configuration with error: %@, info: %@. Will revert to old menu configuration: %@", configurationUpdateOp.error, configurationUpdateOp.error.userInfo, weakself.oldMenuConfiguration); + strongself->_menuConfiguration = strongself.oldMenuConfiguration; + } else { + strongself.oldMenuConfiguration = nil; } - }]; + }; + + // Cancel previous menu configuration operations + for (NSOperation *operation in self.transactionQueue.operations) { + if ([operation isMemberOfClass:[SDLMenuConfigurationUpdateOperation class]]) { + [operation cancel]; + } + } + + // Add the new menu configuration operation to the queue + [self.transactionQueue addOperation:configurationUpdateOp]; } - (void)setMenuCells:(NSArray *)menuCells { @@ -193,7 +215,7 @@ - (void)setMenuCells:(NSArray *)menuCells { return; } - _oldMenuCells = _menuCells; + _currentMenuCells = _menuCells; _menuCells = menuCells; if ([self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) { @@ -211,17 +233,26 @@ - (BOOL)openMenu { return NO; } - SDLShowAppMenu *openMenu = [[SDLShowAppMenu alloc] init]; + // Create the operation + SDLMenuShowOperation *showMenuOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:self.connectionManager toMenuCell:nil]; - [self.connectionManager sendConnectionRequest:openMenu withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { - if ([response.resultCode isEqualToEnum:SDLResultWarnings]) { - SDLLogW(@"Warning opening application menu: %@", error); - } else if (![response.resultCode isEqualToEnum:SDLResultSuccess]) { - SDLLogE(@"Error opening application menu: %@", error); - } else { - SDLLogD(@"Successfully opened application main menu"); + __weak typeof(self) weakself = self; + showMenuOp.completionBlock = ^{ + __strong typeof(weakself) strongself = weakself; + if (showMenuOp.error != nil) { + SDLLogE(@"Opening menu with error: %@, info: %@", showMenuOp.error, showMenuOp.error.userInfo); } - }]; + }; + + // Cancel previous open menu operations + for (NSOperation *operation in self.transactionQueue.operations) { + if ([operation isMemberOfClass:[SDLMenuShowOperation class]]) { + [operation cancel]; + } + } + + // Add the new open menu operation to the queue + [self.transactionQueue addOperation:showMenuOp]; return YES; } @@ -238,17 +269,26 @@ - (BOOL)openSubmenu:(SDLMenuCell *)cell { return NO; } - SDLShowAppMenu *subMenu = [[SDLShowAppMenu alloc] initWithMenuID:cell.cellId]; + // Create the operation + SDLMenuShowOperation *showMenuOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:self.connectionManager toMenuCell:cell]; - [self.connectionManager sendConnectionRequest:subMenu withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { - if ([response.resultCode isEqualToEnum:SDLResultWarnings]) { - SDLLogW(@"Warning opening application menu to submenu cell %@, with error: %@", cell, error); - } else if (![response.resultCode isEqualToEnum:SDLResultSuccess]) { - SDLLogE(@"Error opening application menu to submenu cell %@, with error: %@", cell, error); - } else { - SDLLogD(@"Successfully opened application menu to submenu cell: %@", cell); + __weak typeof(self) weakself = self; + showMenuOp.completionBlock = ^{ + __strong typeof(weakself) strongself = weakself; + if (showMenuOp.error != nil) { + SDLLogE(@"Opening menu with error: %@, info: %@. Failed subcell: %@", showMenuOp.error, showMenuOp.error.userInfo, cell); } - }]; + }; + + // Cancel previous open menu operations + for (NSOperation *operation in self.transactionQueue.operations) { + if ([operation isMemberOfClass:[SDLMenuShowOperation class]]) { + [operation cancel]; + } + } + + // Add the new open menu operation to the queue + [self.transactionQueue addOperation:showMenuOp]; return YES; } @@ -343,15 +383,15 @@ - (void)transferCellIDFromOldCells:(NSArray *)oldCells toKeptCell #pragma mark - Updating System - (void)sdl_startDynamicMenuUpdate { - SDLDynamicMenuUpdateRunScore *runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:self.oldMenuCells updatedMenuCells:self.menuCells]; + SDLDynamicMenuUpdateRunScore *runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:self.currentMenuCells updatedMenuCells:self.menuCells]; NSArray *deleteMenuStatus = runScore.oldStatus; NSArray *addMenuStatus = runScore.updatedStatus; - NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:self.oldMenuCells basedOnStatusList:deleteMenuStatus]; + NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:self.currentMenuCells basedOnStatusList:deleteMenuStatus]; NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:self.menuCells basedOnStatusList:addMenuStatus]; // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares - NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:self.oldMenuCells basedOnStatusList:deleteMenuStatus]; + NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:self.currentMenuCells basedOnStatusList:deleteMenuStatus]; NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.menuCells basedOnStatusList:addMenuStatus]; // Cells that will be added need new ids @@ -394,11 +434,11 @@ - (void)sdl_startNonDynamicMenuUpdate { } SDLLogD(@"Menu artworks uploaded"); - [self sdl_updateMenuWithCellsToDelete:self.oldMenuCells cellsToAdd:self.menuCells completionHandler:nil]; + [self sdl_updateMenuWithCellsToDelete:self.currentMenuCells cellsToAdd:self.menuCells completionHandler:nil]; }]; } else { // Cells have no artwork to load - [self sdl_updateMenuWithCellsToDelete:self.oldMenuCells cellsToAdd:self.menuCells completionHandler:nil]; + [self sdl_updateMenuWithCellsToDelete:self.currentMenuCells cellsToAdd:self.menuCells completionHandler:nil]; } } diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h new file mode 100644 index 000000000..c9fbb9b80 --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h @@ -0,0 +1,21 @@ +// +// SDLMenuReplaceDynamicOperation.h +// SmartDeviceLink +// +// Created by Joel Fischer on 1/20/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import "SDLAsynchronousOperation.h" +#import "SDLConnectionManagerType.h" +#import "SDLFileManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLMenuReplaceDynamicOperation : SDLAsynchronousOperation + +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m new file mode 100644 index 000000000..18e09fd2c --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m @@ -0,0 +1,13 @@ +// +// SDLMenuReplaceDynamicOperation.m +// SmartDeviceLink +// +// Created by Joel Fischer on 1/20/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import "SDLMenuReplaceDynamicOperation.h" + +@implementation SDLMenuReplaceDynamicOperation + +@end diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h new file mode 100644 index 000000000..fd093c92b --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h @@ -0,0 +1,23 @@ +// +// SDLMenuReplaceStaticOperation.h +// SmartDeviceLink +// +// Created by Joel Fischer on 1/20/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import + +#import "SDLAsynchronousOperation.h" +#import "SDLConnectionManagerType.h" +#import "SDLFileManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLMenuReplaceStaticOperation : SDLAsynchronousOperation + +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m new file mode 100644 index 000000000..f1d42c14e --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -0,0 +1,13 @@ +// +// SDLMenuReplaceStaticOperation.m +// SmartDeviceLink +// +// Created by Joel Fischer on 1/20/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import "SDLMenuReplaceStaticOperation.h" + +@implementation SDLMenuReplaceStaticOperation + +@end diff --git a/SmartDeviceLink/private/SDLMenuShowOperation.h b/SmartDeviceLink/private/SDLMenuShowOperation.h new file mode 100644 index 000000000..52496a899 --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuShowOperation.h @@ -0,0 +1,24 @@ +// +// SDLMenuShowOperation.h +// SmartDeviceLink +// +// Created by Joel Fischer on 1/21/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import + +#import "SDLAsynchronousOperation.h" +#import "SDLConnectionManagerType.h" + +@class SDLMenuCell; + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLMenuShowOperation : SDLAsynchronousOperation + +- (instancetype)initWithConnectionManager:(id)connectionManager toMenuCell:(nullable SDLMenuCell *)menuCell; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuShowOperation.m b/SmartDeviceLink/private/SDLMenuShowOperation.m new file mode 100644 index 000000000..e1344a4e7 --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuShowOperation.m @@ -0,0 +1,104 @@ +// +// SDLMenuShowOperation.m +// SmartDeviceLink +// +// Created by Joel Fischer on 1/21/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import "SDLMenuShowOperation.h" + +#import "SDLError.h" +#import "SDLGlobals.h" +#import "SDLLogMacros.h" +#import "SDLMenuCell.h" +#import "SDLRPCResponse.h" +#import "SDLResult.h" +#import "SDLShowAppMenu.h" +#import "SDLVersion.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLMenuCell() + +@property (assign, nonatomic) UInt32 parentCellId; +@property (assign, nonatomic) UInt32 cellId; + +@end + +@interface SDLMenuShowOperation () + +@property (weak, nonatomic) id connectionManager; +@property (strong, nonatomic, nullable) SDLMenuCell *submenuCell; + +@property (copy, nonatomic, nullable) NSError *internalError; + +@end + +@implementation SDLMenuShowOperation + +- (instancetype)initWithConnectionManager:(id)connectionManager toMenuCell:(nullable SDLMenuCell *)menuCell { + self = [super init]; + if (!self) { return nil; } + + _connectionManager = connectionManager; + _submenuCell = menuCell; + + return self; +} + +- (void)start { + [super start]; + if (self.isCancelled) { + [self finishOperation]; + return; + } + + SDLShowAppMenu *openMenu = nil; + if (self.submenuCell != nil) { + openMenu = [[SDLShowAppMenu alloc] initWithMenuID:self.submenuCell.cellId]; + } else { + openMenu = [[SDLShowAppMenu alloc] init]; + } + + __weak typeof(self) weakself = self; + [self.connectionManager sendConnectionRequest:openMenu withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { + if ([response.resultCode isEqualToEnum:SDLResultWarnings]) { + SDLLogW(@"Warning opening application menu with error: %@", error); + } else if (![response.resultCode isEqualToEnum:SDLResultSuccess]) { + SDLLogE(@"Error opening application menu with error: %@", error); + self.internalError = [NSError sdl_menuManager_openMenuOperationFailed:weakself.submenuCell]; + } else { + SDLLogD(@"Successfully opened application menu"); + } + + [self finishOperation]; + }]; +} + +#pragma mark - Operation Overrides + +- (void)finishOperation { + SDLLogV(@"Finishing menu manager configuration update operation"); + if (self.isCancelled) { + self.internalError = [NSError sdl_menuManager_openMenuOperationCancelled]; + } + + [super finishOperation]; +} + +- (nullable NSString *)name { + return @"com.sdl.menuManager.openMenu"; +} + +- (NSOperationQueuePriority)queuePriority { + return NSOperationQueuePriorityNormal; +} + +- (nullable NSError *)error { + return self.internalError; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLVoiceCommandManager.m b/SmartDeviceLink/private/SDLVoiceCommandManager.m index fc0048736..978f53eee 100644 --- a/SmartDeviceLink/private/SDLVoiceCommandManager.m +++ b/SmartDeviceLink/private/SDLVoiceCommandManager.m @@ -12,6 +12,7 @@ #import "SDLConnectionManagerType.h" #import "SDLDeleteCommand.h" #import "SDLError.h" +#import "SDLGlobals.h" #import "SDLHMILevel.h" #import "SDLLogMacros.h" #import "SDLNotificationConstants.h" @@ -85,6 +86,7 @@ - (NSOperationQueue *)sdl_newTransactionQueue { queue.name = @"SDLVoiceCommandManager Transaction Queue"; queue.maxConcurrentOperationCount = 1; queue.qualityOfService = NSQualityOfServiceUserInitiated; + queue.underlyingQueue = [SDLGlobals sharedGlobals].sdlConcurrentQueue; queue.suspended = YES; return queue; diff --git a/SmartDeviceLink/public/SDLErrorConstants.h b/SmartDeviceLink/public/SDLErrorConstants.h index 12bb1c235..324dcd581 100644 --- a/SmartDeviceLink/public/SDLErrorConstants.h +++ b/SmartDeviceLink/public/SDLErrorConstants.h @@ -187,7 +187,11 @@ typedef NS_ENUM(NSInteger, SDLSubscribeButtonManagerError) { typedef NS_ENUM(NSInteger, SDLMenuManagerError) { /// Sending menu-related RPCs returned an error from the remote system SDLMenuManagerErrorRPCsFailed = -1, - SDLMenuManagerErrorPendingUpdateSuperseded = -2 + SDLMenuManagerErrorPendingUpdateSuperseded = -2, + SDLMenuManagerErrorConfigurationUpdateCancelled = -3, + SDLMenuManagerErrorConfigurationUpdateFailed = -4, + SDLMenuManagerErrorOpenMenuCancelled = -5, + SDLMenuManagerErrorOpenMenuFailed = -6 }; /// Errors associated with Choice Set class diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index 9bde38ac7..98ea5d5bf 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -33,7 +33,7 @@ @interface SDLMenuManager() @property (strong, nonatomic, nullable) SDLWindowCapability *windowCapability; @property (assign, nonatomic) UInt32 lastMenuId; -@property (copy, nonatomic) NSArray *oldMenuCells; +@property (copy, nonatomic) NSArray *currentMenuCells; - (BOOL)sdl_shouldRPCsIncludeImages:(NSArray *)cells; @@ -98,7 +98,7 @@ - (BOOL)sdl_shouldRPCsIncludeImages:(NSArray *)cells; expect(testManager.hasQueuedUpdate).to(beFalse()); expect(testManager.waitingOnHMIUpdate).to(beFalse()); expect(testManager.lastMenuId).to(equal(1)); - expect(testManager.oldMenuCells).to(beEmpty()); + expect(testManager.currentMenuCells).to(beEmpty()); expect(testManager.waitingUpdateMenuCells).to(beNil()); expect(testManager.menuConfiguration).toNot(beNil()); }); @@ -639,7 +639,7 @@ - (BOOL)sdl_shouldRPCsIncludeImages:(NSArray *)cells; expect(testManager.hasQueuedUpdate).to(beFalse()); expect(testManager.waitingOnHMIUpdate).to(beFalse()); expect(testManager.lastMenuId).to(equal(1)); - expect(testManager.oldMenuCells).to(beEmpty()); + expect(testManager.currentMenuCells).to(beEmpty()); expect(testManager.waitingUpdateMenuCells).to(beEmpty()); expect(testManager.menuConfiguration).toNot(beNil()); }); From a7aa504b0b7beef59afc63146a3e1d77a046d800 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 22 Jan 2021 08:46:27 -0500 Subject: [PATCH 002/112] Consolidate some opening menu code --- SmartDeviceLink/private/SDLMenuManager.h | 4 +-- SmartDeviceLink/private/SDLMenuManager.m | 42 ++++------------------- SmartDeviceLink/public/SDLScreenManager.m | 4 +-- 3 files changed, 9 insertions(+), 41 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuManager.h b/SmartDeviceLink/private/SDLMenuManager.h index a17775131..9847c6678 100644 --- a/SmartDeviceLink/private/SDLMenuManager.h +++ b/SmartDeviceLink/private/SDLMenuManager.h @@ -42,9 +42,7 @@ typedef void(^SDLMenuUpdateCompletionHandler)(NSError *__nullable error); @property (assign, nonatomic) SDLDynamicMenuUpdatesMode dynamicMenuUpdatesMode; -- (BOOL)openMenu; - -- (BOOL)openSubmenu:(SDLMenuCell *)cell; +- (BOOL)openMenu:(SDLMenuCell *)cell; @end diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 10726d460..422ef4287 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -227,46 +227,16 @@ - (void)setMenuCells:(NSArray *)menuCells { #pragma mark - Open Menu -- (BOOL)openMenu { - if ([SDLGlobals.sharedGlobals.rpcVersion isLessThanVersion:[[SDLVersion alloc] initWithMajor:6 minor:0 patch:0]]) { - SDLLogE(@"The openMenu method is not supported on this head unit."); - return NO; - } - - // Create the operation - SDLMenuShowOperation *showMenuOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:self.connectionManager toMenuCell:nil]; - - __weak typeof(self) weakself = self; - showMenuOp.completionBlock = ^{ - __strong typeof(weakself) strongself = weakself; - if (showMenuOp.error != nil) { - SDLLogE(@"Opening menu with error: %@, info: %@", showMenuOp.error, showMenuOp.error.userInfo); - } - }; - - // Cancel previous open menu operations - for (NSOperation *operation in self.transactionQueue.operations) { - if ([operation isMemberOfClass:[SDLMenuShowOperation class]]) { - [operation cancel]; - } - } - - // Add the new open menu operation to the queue - [self.transactionQueue addOperation:showMenuOp]; - - return YES; -} - -- (BOOL)openSubmenu:(SDLMenuCell *)cell { - if (cell.subCells.count == 0) { +- (BOOL)openMenu:(SDLMenuCell *)cell { + if (cell != nil && cell.subCells.count == 0) { SDLLogE(@"The cell %@ does not contain any sub cells, so no submenu can be opened", cell); return NO; + } else if (cell != nil && ![self.menuCells containsObject:cell]) { + SDLLogE(@"This cell has not been sent to the head unit, so no submenu can be opened. Make sure that the cell exists in the SDLManager.menu array"); + return NO; } else if ([SDLGlobals.sharedGlobals.rpcVersion isLessThanVersion:[[SDLVersion alloc] initWithMajor:6 minor:0 patch:0]]) { SDLLogE(@"The openSubmenu method is not supported on this head unit."); return NO; - } else if (![self.menuCells containsObject:cell]) { - SDLLogE(@"This cell has not been sent to the head unit, so no submenu can be opened. Make sure that the cell exists in the SDLManager.menu array"); - return NO; } // Create the operation @@ -276,7 +246,7 @@ - (BOOL)openSubmenu:(SDLMenuCell *)cell { showMenuOp.completionBlock = ^{ __strong typeof(weakself) strongself = weakself; if (showMenuOp.error != nil) { - SDLLogE(@"Opening menu with error: %@, info: %@. Failed subcell: %@", showMenuOp.error, showMenuOp.error.userInfo, cell); + SDLLogE(@"Opening menu with error: %@, info: %@. Failed subcell (if nil, attempted to open to main menu): %@", showMenuOp.error, showMenuOp.error.userInfo, cell); } }; diff --git a/SmartDeviceLink/public/SDLScreenManager.m b/SmartDeviceLink/public/SDLScreenManager.m index a8792e2fa..c89780035 100644 --- a/SmartDeviceLink/public/SDLScreenManager.m +++ b/SmartDeviceLink/public/SDLScreenManager.m @@ -319,11 +319,11 @@ - (void)dismissKeyboardWithCancelID:(NSNumber *)cancelID{ #pragma mark - Menu - (BOOL)openMenu { - return [self.menuManager openMenu]; + return [self.menuManager openMenu:nil]; } - (BOOL)openSubmenu:(SDLMenuCell *)cell { - return [self.menuManager openSubmenu:cell]; + return [self.menuManager openMenu:cell]; } @end From 3a17e6a06c68d361294f4be58ac76178bc3cc5a1 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 22 Jan 2021 10:34:59 -0500 Subject: [PATCH 003/112] Warning cleanup --- SmartDeviceLink/private/SDLMenuManager.h | 2 +- SmartDeviceLink/private/SDLMenuManager.m | 57 +++++------------------- 2 files changed, 12 insertions(+), 47 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuManager.h b/SmartDeviceLink/private/SDLMenuManager.h index 9847c6678..806e21a4a 100644 --- a/SmartDeviceLink/private/SDLMenuManager.h +++ b/SmartDeviceLink/private/SDLMenuManager.h @@ -42,7 +42,7 @@ typedef void(^SDLMenuUpdateCompletionHandler)(NSError *__nullable error); @property (assign, nonatomic) SDLDynamicMenuUpdatesMode dynamicMenuUpdatesMode; -- (BOOL)openMenu:(SDLMenuCell *)cell; +- (BOOL)openMenu:(nullable SDLMenuCell *)cell; @end diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 422ef4287..8d79b22a4 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -70,7 +70,7 @@ @interface SDLMenuManager() @property (assign, nonatomic) UInt32 lastMenuId; @property (copy, nonatomic) NSArray *currentMenuCells; -@property (strong, nonatomic) SDLMenuConfiguration *oldMenuConfiguration; +@property (strong, nonatomic, nullable) SDLMenuConfiguration *oldMenuConfiguration; @end @@ -136,8 +136,8 @@ - (NSOperationQueue *)sdl_newTransactionQueue { /// Suspend the queue if the HMI level is NONE since we want to delay sending RPCs until we're in non-NONE - (void)sdl_updateTransactionQueueSuspended { - if ([self.currentLevel isEqualToEnum:SDLHMILevelNone]) { - SDLLogD(@"Suspending the transaction queue. Current HMI level is NONE: %@", ([self.currentLevel isEqualToEnum:SDLHMILevelNone] ? @"YES" : @"NO")); + if ([self.currentHMILevel isEqualToEnum:SDLHMILevelNone]) { + SDLLogD(@"Suspending the transaction queue. Current HMI level is NONE: %@", ([self.currentHMILevel isEqualToEnum:SDLHMILevelNone] ? @"YES" : @"NO")); self.transactionQueue.suspended = YES; } else { SDLLogD(@"Starting the transaction queue"); @@ -160,10 +160,11 @@ - (void)setMenuConfiguration:(SDLMenuConfiguration *)menuConfiguration { SDLMenuConfigurationUpdateOperation *configurationUpdateOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:self.connectionManager windowCapability:self.windowCapability newMenuConfiguration:menuConfiguration]; __weak typeof(self) weakself = self; + __weak typeof(configurationUpdateOp) weakOp = configurationUpdateOp; configurationUpdateOp.completionBlock = ^{ __strong typeof(weakself) strongself = weakself; - if (configurationUpdateOp.error != nil) { - SDLLogE(@"Error setting new menu configuration with error: %@, info: %@. Will revert to old menu configuration: %@", configurationUpdateOp.error, configurationUpdateOp.error.userInfo, weakself.oldMenuConfiguration); + if (weakOp.error != nil) { + SDLLogE(@"Error setting new menu configuration with error: %@, info: %@. Will revert to old menu configuration: %@", weakOp.error, weakOp.error.userInfo, weakself.oldMenuConfiguration); strongself->_menuConfiguration = strongself.oldMenuConfiguration; } else { strongself.oldMenuConfiguration = nil; @@ -182,17 +183,6 @@ - (void)setMenuConfiguration:(SDLMenuConfiguration *)menuConfiguration { } - (void)setMenuCells:(NSArray *)menuCells { - if (self.currentHMILevel == nil - || [self.currentHMILevel isEqualToEnum:SDLHMILevelNone] - || [self.currentSystemContext isEqualToEnum:SDLSystemContextMenu]) { - SDLLogD(@"Waiting for HMI update to send menu cells"); - self.waitingOnHMIUpdate = YES; - self.waitingUpdateMenuCells = menuCells; - return; - } - - self.waitingOnHMIUpdate = NO; - NSMutableSet *titleCheckSet = [NSMutableSet set]; NSMutableSet *allMenuVoiceCommands = [NSMutableSet set]; NSUInteger voiceCommandCount = 0; @@ -227,7 +217,7 @@ - (void)setMenuCells:(NSArray *)menuCells { #pragma mark - Open Menu -- (BOOL)openMenu:(SDLMenuCell *)cell { +- (BOOL)openMenu:(nullable SDLMenuCell *)cell { if (cell != nil && cell.subCells.count == 0) { SDLLogE(@"The cell %@ does not contain any sub cells, so no submenu can be opened", cell); return NO; @@ -241,12 +231,10 @@ - (BOOL)openMenu:(SDLMenuCell *)cell { // Create the operation SDLMenuShowOperation *showMenuOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:self.connectionManager toMenuCell:cell]; - - __weak typeof(self) weakself = self; + __weak typeof(showMenuOp) weakMenuOp = showMenuOp; showMenuOp.completionBlock = ^{ - __strong typeof(weakself) strongself = weakself; - if (showMenuOp.error != nil) { - SDLLogE(@"Opening menu with error: %@, info: %@. Failed subcell (if nil, attempted to open to main menu): %@", showMenuOp.error, showMenuOp.error.userInfo, cell); + if (weakMenuOp.error != nil) { + SDLLogE(@"Opening menu with error: %@, info: %@. Failed subcell (if nil, attempted to open to main menu): %@", weakMenuOp.error, weakMenuOp.error.userInfo, cell); } }; @@ -413,33 +401,10 @@ - (void)sdl_startNonDynamicMenuUpdate { } - (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(nullable SDLMenuUpdateCompletionHandler)completionHandler { - if (self.currentHMILevel == nil - || [self.currentHMILevel isEqualToEnum:SDLHMILevelNone] - || [self.currentSystemContext isEqualToEnum:SDLSystemContextMenu]) { - self.waitingOnHMIUpdate = YES; - self.waitingUpdateMenuCells = self.menuCells; - return; - } - - if (self.inProgressUpdate != nil) { - // There's an in progress update, we need to put this on hold - self.hasQueuedUpdate = YES; - return; - } __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { - [weakself sdl_sendUpdatedMenu:addCells usingMenu:weakself.menuCells withCompletionHandler:^(NSError * _Nullable error) { - weakself.inProgressUpdate = nil; + [weakself sdl_sendUpdatedMenu:addCells usingMenu:weakself.menuCells withCompletionHandler:^(NSError * _Nullable error) { }]; - if (completionHandler != nil) { - completionHandler(error); - } - - if (weakself.hasQueuedUpdate) { - [weakself sdl_updateMenuWithCellsToDelete:deleteCells cellsToAdd:addCells completionHandler:nil]; - weakself.hasQueuedUpdate = NO; - } - }]; }]; } From 7332f5e99d6de045c37a69e008ffeadbcc54bb72 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 22 Jan 2021 16:01:11 -0500 Subject: [PATCH 004/112] A ton of work on the menu replace operations --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 16 + SmartDeviceLink/private/SDLError.m | 2 +- SmartDeviceLink/private/SDLMenuManager.m | 331 +----------------- .../private/SDLMenuReplaceDynamicOperation.h | 16 +- .../private/SDLMenuReplaceDynamicOperation.m | 139 ++++++++ .../private/SDLMenuReplaceStaticOperation.h | 14 +- .../private/SDLMenuReplaceStaticOperation.m | 178 ++++++++++ .../private/SDLMenuReplaceUtilities.h | 42 +++ .../private/SDLMenuReplaceUtilities.m | 158 +++++++++ 9 files changed, 562 insertions(+), 334 deletions(-) create mode 100644 SmartDeviceLink/private/SDLMenuReplaceUtilities.h create mode 100644 SmartDeviceLink/private/SDLMenuReplaceUtilities.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index e472ee38a..0d45f7f98 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -519,6 +519,8 @@ 4A93895425B9DACA0069F438 /* SDLMenuShowOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93895225B9DACA0069F438 /* SDLMenuShowOperation.m */; }; 4A93895925B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93895725B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h */; }; 4A93895A25B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93895825B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m */; }; + 4A93896625BB361C0069F438 /* SDLMenuReplaceUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93896425BB361C0069F438 /* SDLMenuReplaceUtilities.h */; }; + 4A93896725BB361C0069F438 /* SDLMenuReplaceUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */; }; 4ABB24BA24F592620061BF55 /* NSMutableArray+Safe.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ABB24B224F592620061BF55 /* NSMutableArray+Safe.h */; }; 4ABB24BB24F592620061BF55 /* NSMutableArray+Safe.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABB24B324F592620061BF55 /* NSMutableArray+Safe.m */; }; 4ABB24BC24F592620061BF55 /* NSBundle+SDLBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABB24B424F592620061BF55 /* NSBundle+SDLBundle.m */; }; @@ -2313,6 +2315,8 @@ 4A93895225B9DACA0069F438 /* SDLMenuShowOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuShowOperation.m; path = private/SDLMenuShowOperation.m; sourceTree = ""; }; 4A93895725B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuConfigurationUpdateOperation.h; path = private/SDLMenuConfigurationUpdateOperation.h; sourceTree = ""; }; 4A93895825B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuConfigurationUpdateOperation.m; path = private/SDLMenuConfigurationUpdateOperation.m; sourceTree = ""; }; + 4A93896425BB361C0069F438 /* SDLMenuReplaceUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuReplaceUtilities.h; path = private/SDLMenuReplaceUtilities.h; sourceTree = ""; }; + 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceUtilities.m; path = private/SDLMenuReplaceUtilities.m; sourceTree = ""; }; 4ABB24B224F592620061BF55 /* NSMutableArray+Safe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSMutableArray+Safe.h"; path = "private/NSMutableArray+Safe.h"; sourceTree = ""; }; 4ABB24B324F592620061BF55 /* NSMutableArray+Safe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSMutableArray+Safe.m"; path = "private/NSMutableArray+Safe.m"; sourceTree = ""; }; 4ABB24B424F592620061BF55 /* NSBundle+SDLBundle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSBundle+SDLBundle.m"; path = "private/NSBundle+SDLBundle.m"; sourceTree = ""; }; @@ -4270,10 +4274,20 @@ 4A93894425B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m */, 4A93895125B9DACA0069F438 /* SDLMenuShowOperation.h */, 4A93895225B9DACA0069F438 /* SDLMenuShowOperation.m */, + 4A93896325BB35FA0069F438 /* Menu Replace Utilities */, ); name = Operations; sourceTree = ""; }; + 4A93896325BB35FA0069F438 /* Menu Replace Utilities */ = { + isa = PBXGroup; + children = ( + 4A93896425BB361C0069F438 /* SDLMenuReplaceUtilities.h */, + 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */, + ); + name = "Menu Replace Utilities"; + sourceTree = ""; + }; 4A9D02BB2497EEB400FBE99B /* Custom RPC Adapters */ = { isa = PBXGroup; children = ( @@ -7146,6 +7160,7 @@ 4ABB2AF424F849CF0061BF55 /* SDLGenericResponse.h in Headers */, 4ABB280A24F824600061BF55 /* SDLServiceUpdateReason.h in Headers */, 4ABB2B5124F84EF50061BF55 /* SDLDisplayCapability.h in Headers */, + 4A93896625BB361C0069F438 /* SDLMenuReplaceUtilities.h in Headers */, 4ABB2ABD24F847FC0061BF55 /* SDLSliderResponse.h in Headers */, 4ABB28D924F82A6A0061BF55 /* SDLOnSyncPData.h in Headers */, 4A8BD2B924F935BC000945E3 /* SDLSoftButton.h in Headers */, @@ -7909,6 +7924,7 @@ 4ABB274F24F7FD9C0061BF55 /* SDLECallConfirmationStatus.m in Sources */, 4ABB27AF24F7FFDA0061BF55 /* SDLMaintenanceModeStatus.m in Sources */, 4ABB292D24F842A00061BF55 /* SDLDeleteWindow.m in Sources */, + 4A93896725BB361C0069F438 /* SDLMenuReplaceUtilities.m in Sources */, 4ABB299A24F845440061BF55 /* SDLSendHapticData.m in Sources */, 4ABB29DB24F846880061BF55 /* SDLUnregisterAppInterface.m in Sources */, 4A93895425B9DACA0069F438 /* SDLMenuShowOperation.m in Sources */, diff --git a/SmartDeviceLink/private/SDLError.m b/SmartDeviceLink/private/SDLError.m index 57fab7809..05338b72b 100644 --- a/SmartDeviceLink/private/SDLError.m +++ b/SmartDeviceLink/private/SDLError.m @@ -287,7 +287,7 @@ + (NSError *)sdl_menuManager_openMenuOperationCancelled { + (NSError *)sdl_menuManager_openMenuOperationFailed:(nullable SDLMenuCell *)menuCell { NSString *failureReason = nil; if (menuCell != nil) { - failureReason = @"Something went wrong attempting to open the menu." + failureReason = @"Something went wrong attempting to open the menu."; } else { failureReason = [NSString stringWithFormat:@"Something went wrong attempting to open the menu to the given subcell: %@", menuCell]; } diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 8d79b22a4..673804f85 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -211,7 +211,7 @@ - (void)setMenuCells:(NSArray *)menuCells { if ([self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) { [self sdl_startDynamicMenuUpdate]; } else { - [self sdl_startNonDynamicMenuUpdate]; + [self sdl_startStaticMenuUpdate]; } } @@ -254,10 +254,7 @@ - (BOOL)openMenu:(nullable SDLMenuCell *)cell { #pragma mark - Build Deletes, Keeps, Adds - (void)sdl_startSubMenuUpdatesWithOldKeptCells:(NSArray *)oldKeptCells newKeptCells:(NSArray *)newKeptCells atIndex:(NSUInteger)startIndex { - if (oldKeptCells.count == 0 || startIndex >= oldKeptCells.count) { - self.inProgressUpdate = nil; - return; - } + if (oldKeptCells.count == 0 || startIndex >= oldKeptCells.count) { return; } if (oldKeptCells[startIndex].subCells.count > 0) { SDLDynamicMenuUpdateRunScore *tempScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldKeptCells[startIndex].subCells updatedMenuCells:newKeptCells[startIndex].subCells]; @@ -286,206 +283,20 @@ - (void)sdl_startSubMenuUpdatesWithOldKeptCells:(NSArray *)oldKep } } -- (NSArray *)sdl_filterDeleteMenuItemsWithOldMenuItems:(NSArray *)oldMenuCells basedOnStatusList:(NSArray *)oldStatusList { - NSMutableArray *deleteCells = [[NSMutableArray alloc] init]; - // The index of the status should corrleate 1-1 with the number of items in the menu - // [2,0,2,0] => [A,B,C,D] = [B,D] - for (NSUInteger index = 0; index < oldStatusList.count; index++) { - if (oldStatusList[index].integerValue == MenuCellStateDelete) { - [deleteCells addObject:oldMenuCells[index]]; - } - } - return [deleteCells copy]; -} - -- (NSArray *)sdl_filterAddMenuItemsWithNewMenuItems:(NSArray *)newMenuCells basedOnStatusList:(NSArray *)newStatusList { - NSMutableArray *addCells = [[NSMutableArray alloc] init]; - // The index of the status should corrleate 1-1 with the number of items in the menu - // [2,1,2,1] => [A,B,C,D] = [B,D] - for (NSUInteger index = 0; index < newStatusList.count; index++) { - if (newStatusList[index].integerValue == MenuCellStateAdd) { - [addCells addObject:newMenuCells[index]]; - } - } - return [addCells copy]; -} - -- (NSArray *)sdl_filterKeepMenuItemsWithOldMenuItems:(NSArray *)oldMenuCells basedOnStatusList:(NSArray *)keepStatusList { - NSMutableArray *keepMenuCells = [[NSMutableArray alloc] init]; - - for (NSUInteger index = 0; index < keepStatusList.count; index++) { - if (keepStatusList[index].integerValue == MenuCellStateKeep) { - [keepMenuCells addObject:oldMenuCells[index]]; - } - } - return [keepMenuCells copy]; -} - -- (NSArray *)sdl_filterKeepMenuItemsWithNewMenuItems:(NSArray *)newMenuCells basedOnStatusList:(NSArray *)keepStatusList { - NSMutableArray *keepMenuCells = [[NSMutableArray alloc] init]; - for (NSUInteger index = 0; index < keepStatusList.count; index++) { - if (keepStatusList[index].integerValue == MenuCellStateKeep) { - [keepMenuCells addObject:newMenuCells[index]]; - } - } - return [keepMenuCells copy]; -} - -- (void)transferCellIDFromOldCells:(NSArray *)oldCells toKeptCells:(NSArray *)newCells { - if (oldCells.count == 0) { return; } - for (NSUInteger i = 0; i < newCells.count; i++) { - newCells[i].cellId = oldCells[i].cellId; - } -} - #pragma mark - Updating System - (void)sdl_startDynamicMenuUpdate { - SDLDynamicMenuUpdateRunScore *runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:self.currentMenuCells updatedMenuCells:self.menuCells]; - - NSArray *deleteMenuStatus = runScore.oldStatus; - NSArray *addMenuStatus = runScore.updatedStatus; - - NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:self.currentMenuCells basedOnStatusList:deleteMenuStatus]; - NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:self.menuCells basedOnStatusList:addMenuStatus]; - // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares - NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:self.currentMenuCells basedOnStatusList:deleteMenuStatus]; - NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.menuCells basedOnStatusList:addMenuStatus]; - - // Cells that will be added need new ids - [self sdl_updateIdsOnMenuCells:cellsToAdd parentId:ParentIdNotFound]; - - // Since we are creating a new Menu but keeping old cells we must firt transfer the old cellIDs to the new menus kept cells. - [self transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; - - // Upload the artworks - NSArray *artworksToBeUploaded = [self sdl_findAllArtworksToBeUploadedFromCells:cellsToAdd]; - if (artworksToBeUploaded.count > 0) { - [self.fileManager uploadArtworks:artworksToBeUploaded completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { - if (error != nil) { - SDLLogE(@"Error uploading menu artworks: %@", error); - } - SDLLogD(@"Menu artworks uploaded"); - // Update cells with artworks once they're uploaded - __weak typeof(self) weakself = self; - [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { - [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; - }]; - }]; - } else { - // Cells have no artwork to load - __weak typeof(self) weakself = self; - [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { - [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; - }]; - } -} -- (void)sdl_startNonDynamicMenuUpdate { - [self sdl_updateIdsOnMenuCells:self.menuCells parentId:ParentIdNotFound]; +} - NSArray *artworksToBeUploaded = [self sdl_findAllArtworksToBeUploadedFromCells:self.menuCells]; - if (artworksToBeUploaded.count > 0) { - [self.fileManager uploadArtworks:artworksToBeUploaded completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { - if (error != nil) { - SDLLogE(@"Error uploading menu artworks: %@", error); - } +- (void)sdl_startStaticMenuUpdate { - SDLLogD(@"Menu artworks uploaded"); - [self sdl_updateMenuWithCellsToDelete:self.currentMenuCells cellsToAdd:self.menuCells completionHandler:nil]; - }]; - } else { - // Cells have no artwork to load - [self sdl_updateMenuWithCellsToDelete:self.currentMenuCells cellsToAdd:self.menuCells completionHandler:nil]; - } } - (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(nullable SDLMenuUpdateCompletionHandler)completionHandler { __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { [weakself sdl_sendUpdatedMenu:addCells usingMenu:weakself.menuCells withCompletionHandler:^(NSError * _Nullable error) { }]; - - }]; -} - -#pragma mark Delete Old Menu Items - -- (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { - if (deleteMenuCells.count == 0) { - completionHandler(nil); - return; - } - - NSArray *deleteMenuCommands = [self sdl_deleteCommandsForCells:deleteMenuCells]; - [self.connectionManager sendRequests:deleteMenuCommands progressHandler:nil completionHandler:^(BOOL success) { - if (!success) { - SDLLogW(@"Unable to delete all old menu commands"); - } else { - SDLLogD(@"Finished deleting old menu"); - } - - completionHandler(nil); - }]; -} - -#pragma mark Send New Menu Items - -/** - Creates add commands - - @param updatedMenu The cells you will be adding - @param menu The list of all cells. This may be different then self.menuCells since this function is called on subcell cells as well. When comparing 2 sub menu cells this function will be passed the list of all subcells on that cell. - @param completionHandler handler - */ -- (void)sdl_sendUpdatedMenu:(NSArray *)updatedMenu usingMenu:(NSArray *)menu withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { - if (self.menuCells.count == 0 || updatedMenu.count == 0) { - SDLLogD(@"There are no cells to update."); - completionHandler(nil); - return; - } - - NSArray *mainMenuCommands = nil; - NSArray *subMenuCommands = nil; - - if (![self sdl_shouldRPCsIncludeImages:self.menuCells] || ![self.windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]) { - // Send artwork-less menu - mainMenuCommands = [self sdl_mainMenuCommandsForCells:updatedMenu withArtwork:NO usingIndexesFrom:menu]; - subMenuCommands = [self sdl_subMenuCommandsForCells:updatedMenu withArtwork:NO]; - } else { - // Send full artwork menu - mainMenuCommands = [self sdl_mainMenuCommandsForCells:updatedMenu withArtwork:YES usingIndexesFrom:menu]; - subMenuCommands = [self sdl_subMenuCommandsForCells:updatedMenu withArtwork:YES]; - } - - self.inProgressUpdate = [mainMenuCommands arrayByAddingObjectsFromArray:subMenuCommands]; - - __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; - __weak typeof(self) weakSelf = self; - [self.connectionManager sendRequests:mainMenuCommands progressHandler:^void(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { - if (error != nil) { - errors[request] = error; - } - } completionHandler:^(BOOL success) { - if (!success) { - SDLLogE(@"Failed to send main menu commands: %@", errors); - completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); - return; - } - - [weakSelf.connectionManager sendRequests:subMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { - if (error != nil) { - errors[request] = error; - } - } completionHandler:^(BOOL success) { - if (!success) { - SDLLogE(@"Failed to send sub menu commands: %@", errors); - completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); - return; - } - - SDLLogD(@"Finished updating menu"); - completionHandler(nil); - }]; }]; } @@ -505,40 +316,6 @@ - (BOOL)sdl_isDynamicMenuUpdateActive:(SDLDynamicMenuUpdatesMode)dynamicMenuUpda } } -#pragma mark Artworks - -- (NSArray *)sdl_findAllArtworksToBeUploadedFromCells:(NSArray *)cells { - if (![self.windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]) { - return @[]; - } - - NSMutableSet *mutableArtworks = [NSMutableSet set]; - for (SDLMenuCell *cell in cells) { - if ([self.fileManager fileNeedsUpload:cell.icon]) { - [mutableArtworks addObject:cell.icon]; - } - - if (cell.subCells.count > 0) { - [mutableArtworks addObjectsFromArray:[self sdl_findAllArtworksToBeUploadedFromCells:cell.subCells]]; - } - } - - return [mutableArtworks allObjects]; -} - -- (BOOL)sdl_shouldRPCsIncludeImages:(NSArray *)cells { - for (SDLMenuCell *cell in cells) { - SDLArtwork *artwork = cell.icon; - if (artwork != nil && !artwork.isStaticIcon && ![self.fileManager hasUploadedFile:artwork]) { - return NO; - } else if (cell.subCells.count > 0) { - return [self sdl_shouldRPCsIncludeImages:cell.subCells]; - } - } - - return YES; -} - #pragma mark IDs - (void)sdl_updateIdsOnMenuCells:(NSArray *)menuCells parentId:(UInt32)parentId { @@ -551,106 +328,6 @@ - (void)sdl_updateIdsOnMenuCells:(NSArray *)menuCells parentId:(U } } -#pragma mark Deletes - -- (NSArray *)sdl_deleteCommandsForCells:(NSArray *)cells { - NSMutableArray *mutableDeletes = [NSMutableArray array]; - for (SDLMenuCell *cell in cells) { - if (cell.subCells == nil) { - SDLDeleteCommand *delete = [[SDLDeleteCommand alloc] initWithId:cell.cellId]; - [mutableDeletes addObject:delete]; - } else { - SDLDeleteSubMenu *delete = [[SDLDeleteSubMenu alloc] initWithId:cell.cellId]; - [mutableDeletes addObject:delete]; - } - } - - return [mutableDeletes copy]; -} - -#pragma mark Commands / SubMenu RPCs -/** - This method will receive the cells to be added and the updated menu array. It will then build an array of add commands using the correct index to position the new items in the correct location. - - @param cells that will be added to the menu, this array must contain only cells that are not already in the menu. - @param shouldHaveArtwork artwork bool - @param menu the new menu array, this array should contain all the values the develeoper has set to be included in the new menu. This is used for placing the newly added cells in the correct locaiton. - e.g. If the new menu array is [A, B, C, D] but only [C, D] are new we need to pass [A, B , C , D] so C and D can be added to index 2 and 3 respectively. - @return list of SDLRPCRequest addCommands - */ -- (NSArray *)sdl_mainMenuCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork usingIndexesFrom:(NSArray *)menu { - NSMutableArray *mutableCommands = [NSMutableArray array]; - - for (NSUInteger menuInteger = 0; menuInteger < menu.count; menuInteger++) { - for (NSUInteger updateCellsIndex = 0; updateCellsIndex < cells.count; updateCellsIndex++) { - if ([menu[menuInteger] isEqual:cells[updateCellsIndex]]) { - if (cells[updateCellsIndex].subCells.count > 0) { - [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[updateCellsIndex] withArtwork:shouldHaveArtwork position:(UInt16)menuInteger]]; - } else { - [mutableCommands addObject:[self sdl_commandForMenuCell:cells[updateCellsIndex] withArtwork:shouldHaveArtwork position:(UInt16)menuInteger]]; - } - } - } - } - - return [mutableCommands copy]; -} - -- (NSArray *)sdl_subMenuCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork { - NSMutableArray *mutableCommands = [NSMutableArray array]; - for (SDLMenuCell *cell in cells) { - if (cell.subCells.count > 0) { - [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cell.subCells withArtwork:shouldHaveArtwork]]; - } - } - - return [mutableCommands copy]; -} - -- (NSArray *)sdl_allCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork { - NSMutableArray *mutableCommands = [NSMutableArray array]; - - for (NSUInteger cellIndex = 0; cellIndex < cells.count; cellIndex++) { - if (cells[cellIndex].subCells.count > 0) { - [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[cellIndex] withArtwork:shouldHaveArtwork position:(UInt16)cellIndex]]; - [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cells[cellIndex].subCells withArtwork:shouldHaveArtwork]]; - } else { - [mutableCommands addObject:[self sdl_commandForMenuCell:cells[cellIndex] withArtwork:shouldHaveArtwork position:(UInt16)cellIndex]]; - } - } - - return [mutableCommands copy]; -} - -- (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell withArtwork:(BOOL)shouldHaveArtwork position:(UInt16)position { - SDLAddCommand *command = [[SDLAddCommand alloc] init]; - - SDLMenuParams *params = [[SDLMenuParams alloc] init]; - params.menuName = cell.title; - params.parentID = cell.parentCellId != UINT32_MAX ? @(cell.parentCellId) : nil; - params.position = @(position); - - command.menuParams = params; - command.vrCommands = (cell.voiceCommands.count == 0) ? nil : cell.voiceCommands; - command.cmdIcon = (cell.icon && shouldHaveArtwork) ? cell.icon.imageRPC : nil; - command.cmdID = @(cell.cellId); - - return command; -} - -- (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell withArtwork:(BOOL)shouldHaveArtwork position:(UInt16)position { - SDLImage *icon = (shouldHaveArtwork && (cell.icon.name != nil)) ? cell.icon.imageRPC : nil; - - SDLMenuLayout submenuLayout = nil; - if (cell.submenuLayout && [self.systemCapabilityManager.defaultMainWindowCapability.menuLayoutsAvailable containsObject:cell.submenuLayout]) { - submenuLayout = cell.submenuLayout; - } else { - submenuLayout = self.menuConfiguration.defaultSubmenuLayout; - } - - return [[SDLAddSubMenu alloc] initWithMenuID:cell.cellId menuName:cell.title position:@(position) menuIcon:icon menuLayout:submenuLayout parentID:nil]; -} - #pragma mark - Calling handlers - (BOOL)sdl_callHandlerForCells:(NSArray *)cells command:(SDLOnCommand *)onCommand { diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h index c9fbb9b80..2a37d6c24 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h +++ b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h @@ -7,14 +7,24 @@ // #import "SDLAsynchronousOperation.h" -#import "SDLConnectionManagerType.h" -#import "SDLFileManager.h" + +#import "SDLAsynchronousOperation.h" + +@protocol SDLConnectionManagerType; + +@class SDLFileManager; +@class SDLMenuCell; +@class SDLMenuConfiguration; +@class SDLWindowCapability; NS_ASSUME_NONNULL_BEGIN @interface SDLMenuReplaceDynamicOperation : SDLAsynchronousOperation -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager; +@property (strong, nonatomic) SDLWindowCapability *windowCapability; +@property (strong, nonatomic) SDLMenuConfiguration *menuConfiguration; + +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m index 18e09fd2c..1714cba0d 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m @@ -8,6 +8,145 @@ #import "SDLMenuReplaceDynamicOperation.h" +#import "SDLArtwork.h" +#import "SDLConnectionManagerType.h" +#import "SDLDynamicMenuUpdateAlgorithm.h" +#import "SDLDynamicMenuUpdateRunScore.h" +#import "SDLError.h" +#import "SDLFileManager.h" +#import "SDLLogMacros.h" +#import "SDLMenuCell.h" +#import "SDLMenuConfiguration.h" +#import "SDLMenuReplaceUtilities.h" +#import "SDLTextFieldName.h" +#import "SDLWindowCapability.h" +#import "SDLWindowCapability+ScreenManagerExtensions.h" + +@interface SDLMenuReplaceDynamicOperation () + +@property (weak, nonatomic) id connectionManager; +@property (weak, nonatomic) SDLFileManager *fileManager; +@property (strong, nonatomic) NSArray *currentMenu; +@property (strong, nonatomic) NSArray *updatedMenu; + +@property (copy, nonatomic, nullable) NSError *internalError; + +@end + @implementation SDLMenuReplaceDynamicOperation +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu { + self = [super init]; + if (!self) { return nil; } + + _connectionManager = connectionManager; + _fileManager = fileManager; + _windowCapability = windowCapability; + _menuConfiguration = menuConfiguration; + _currentMenu = currentMenu; + _updatedMenu = updatedMenu; + + return self; +} + +- (void)start { + [super start]; + if (self.isCancelled) { + [self finishOperation]; + return; + } + + SDLDynamicMenuUpdateRunScore *runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; + NSArray *deleteMenuStatus = runScore.oldStatus; + NSArray *addMenuStatus = runScore.updatedStatus; + + NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:deleteMenuStatus]; + NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:addMenuStatus]; + // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares + NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:deleteMenuStatus]; + NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:addMenuStatus]; + + // Cells that will be added need new ids + [self sdl_updateIdsOnMenuCells:cellsToAdd parentId:ParentIdNotFound]; + + // Since we are creating a new Menu but keeping old cells we must firt transfer the old cellIDs to the new menus kept cells. + [self transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; + + // TODO: We don't check cancellation or finish + NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; + if (artworksToBeUploaded.count > 0) { + [self.fileManager uploadArtworks:artworksToBeUploaded completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { + if (error != nil) { + SDLLogE(@"Error uploading menu artworks: %@", error); + } + + SDLLogD(@"Menu artworks uploaded"); + __weak typeof(self) weakself = self; + [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { + [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; + }]; + }]; + } else { + // Cells have no artwork to load + __weak typeof(self) weakself = self; + [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { + [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; + }]; + } +} + +#pragma mark Dynamic Menu Helpers + +- (NSArray *)sdl_filterDeleteMenuItemsWithOldMenuItems:(NSArray *)oldMenuCells basedOnStatusList:(NSArray *)oldStatusList { + NSMutableArray *deleteCells = [[NSMutableArray alloc] init]; + // The index of the status should corrleate 1-1 with the number of items in the menu + // [2,0,2,0] => [A,B,C,D] = [B,D] + for (NSUInteger index = 0; index < oldStatusList.count; index++) { + if (oldStatusList[index].integerValue == MenuCellStateDelete) { + [deleteCells addObject:oldMenuCells[index]]; + } + } + return [deleteCells copy]; +} + +- (NSArray *)sdl_filterAddMenuItemsWithNewMenuItems:(NSArray *)newMenuCells basedOnStatusList:(NSArray *)newStatusList { + NSMutableArray *addCells = [[NSMutableArray alloc] init]; + // The index of the status should corrleate 1-1 with the number of items in the menu + // [2,1,2,1] => [A,B,C,D] = [B,D] + for (NSUInteger index = 0; index < newStatusList.count; index++) { + if (newStatusList[index].integerValue == MenuCellStateAdd) { + [addCells addObject:newMenuCells[index]]; + } + } + return [addCells copy]; +} + +- (NSArray *)sdl_filterKeepMenuItemsWithOldMenuItems:(NSArray *)oldMenuCells basedOnStatusList:(NSArray *)keepStatusList { + NSMutableArray *keepMenuCells = [[NSMutableArray alloc] init]; + + for (NSUInteger index = 0; index < keepStatusList.count; index++) { + if (keepStatusList[index].integerValue == MenuCellStateKeep) { + [keepMenuCells addObject:oldMenuCells[index]]; + } + } + return [keepMenuCells copy]; +} + +- (NSArray *)sdl_filterKeepMenuItemsWithNewMenuItems:(NSArray *)newMenuCells basedOnStatusList:(NSArray *)keepStatusList { + NSMutableArray *keepMenuCells = [[NSMutableArray alloc] init]; + for (NSUInteger index = 0; index < keepStatusList.count; index++) { + if (keepStatusList[index].integerValue == MenuCellStateKeep) { + [keepMenuCells addObject:newMenuCells[index]]; + } + } + return [keepMenuCells copy]; +} + +- (void)transferCellIDFromOldCells:(NSArray *)oldCells toKeptCells:(NSArray *)newCells { + if (oldCells.count == 0) { return; } + for (NSUInteger i = 0; i < newCells.count; i++) { + newCells[i].cellId = oldCells[i].cellId; + } +} + @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h index fd093c92b..25895f7c5 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h @@ -9,14 +9,22 @@ #import #import "SDLAsynchronousOperation.h" -#import "SDLConnectionManagerType.h" -#import "SDLFileManager.h" + +@protocol SDLConnectionManagerType; + +@class SDLFileManager; +@class SDLMenuCell; +@class SDLMenuConfiguration; +@class SDLWindowCapability; NS_ASSUME_NONNULL_BEGIN @interface SDLMenuReplaceStaticOperation : SDLAsynchronousOperation -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager; +@property (strong, nonatomic) SDLWindowCapability *windowCapability; +@property (strong, nonatomic) SDLMenuConfiguration *menuConfiguration; + +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index f1d42c14e..740f871c0 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -8,6 +8,184 @@ #import "SDLMenuReplaceStaticOperation.h" +#import "SDLArtwork.h" +#import "SDLConnectionManagerType.h" +#import "SDLError.h" +#import "SDLFileManager.h" +#import "SDLLogMacros.h" +#import "SDLMenuCell.h" +#import "SDLMenuConfiguration.h" +#import "SDLMenuReplaceUtilities.h" +#import "SDLTextFieldName.h" +#import "SDLWindowCapability.h" +#import "SDLWindowCapability+ScreenManagerExtensions.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^SDLMenuUpdateCompletionHandler)(NSError *__nullable error); + +@interface SDLMenuReplaceStaticOperation () + +@property (weak, nonatomic) id connectionManager; +@property (weak, nonatomic) SDLFileManager *fileManager; +@property (strong, nonatomic) NSArray *currentMenu; +@property (strong, nonatomic) NSArray *updatedMenu; + +@property (copy, nonatomic, nullable) NSError *internalError; + +@end + @implementation SDLMenuReplaceStaticOperation +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu { + self = [super init]; + if (!self) { return nil; } + + _connectionManager = connectionManager; + _fileManager = fileManager; + _windowCapability = windowCapability; + _menuConfiguration = menuConfiguration; + _currentMenu = currentMenu; + _updatedMenu = updatedMenu; + + return self; +} + +- (void)start { + [super start]; + if (self.isCancelled) { + [self finishOperation]; + return; + } + + // TODO: We don't check cancellation or finish + + NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; + if (artworksToBeUploaded.count > 0) { + [self.fileManager uploadArtworks:artworksToBeUploaded completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { + if (error != nil) { + SDLLogE(@"Error uploading menu artworks: %@", error); + } + + SDLLogD(@"Menu artworks uploaded"); + [self sdl_updateMenuWithCellsToDelete:self.currentMenu cellsToAdd:self.updatedMenu completionHandler:nil]; + }]; + } else { + // Cells have no artwork to load + [self sdl_updateMenuWithCellsToDelete:self.currentMenu cellsToAdd:self.updatedMenu completionHandler:nil]; + } +} + +#pragma mark - Private Helpers + +#pragma mark Sending Items + +- (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(nullable SDLMenuUpdateCompletionHandler)completionHandler { + __weak typeof(self) weakself = self; + [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { + [weakself sdl_sendNewMenuCells:addCells withCompletionHandler:^(NSError * _Nullable error) { }]; + }]; +} + +/** + Creates add commands + + @param newMenuCells The cells you will be adding + @param completionHandler handler + */ +- (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { + if (self.updatedMenu.count == 0 || newMenuCells.count == 0) { + SDLLogD(@"There are no cells to update."); + completionHandler(nil); + return; + } + + NSArray *mainMenuCommands = nil; + NSArray *subMenuCommands = nil; + + if (![SDLMenuReplaceUtilities shouldRPCsIncludeImages:self.updatedMenu fileManager:self.fileManager] || ![self.windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]) { + // Send artwork-less menu + mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells withArtwork:NO usingIndexesFrom:self.updatedMenu availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells withArtwork:NO availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + } else { + // Send full artwork menu + mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells withArtwork:YES usingIndexesFrom:self.updatedMenu availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells withArtwork:YES availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + } + + __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; + __weak typeof(self) weakSelf = self; + [self.connectionManager sendRequests:mainMenuCommands progressHandler:^void(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { + if (error != nil) { + errors[request] = error; + } + } completionHandler:^(BOOL success) { + if (!success) { + SDLLogE(@"Failed to send main menu commands: %@", errors); + completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); + return; + } + + [weakSelf.connectionManager sendRequests:subMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { + if (error != nil) { + errors[request] = error; + } + } completionHandler:^(BOOL success) { + if (!success) { + SDLLogE(@"Failed to send sub menu commands: %@", errors); + completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); + return; + } + + SDLLogD(@"Finished updating menu"); + completionHandler(nil); + }]; + }]; +} + +#pragma mark Delete Old Menu Items + +- (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { + if (deleteMenuCells.count == 0) { + completionHandler(nil); + return; + } + + NSArray *deleteMenuCommands = [SDLMenuReplaceUtilities deleteCommandsForCells:deleteMenuCells]; + [self.connectionManager sendRequests:deleteMenuCommands progressHandler:nil completionHandler:^(BOOL success) { + if (!success) { + SDLLogW(@"Unable to delete all old menu commands"); + } else { + SDLLogD(@"Finished deleting old menu"); + } + + completionHandler(nil); + }]; +} + +#pragma mark - Operation Overrides + +- (void)finishOperation { + SDLLogV(@"Finishing menu manager configuration update operation"); + if (self.isCancelled) { + self.internalError = [NSError sdl_menuManager_openMenuOperationCancelled]; + } + + [super finishOperation]; +} + +- (nullable NSString *)name { + return @"com.sdl.menuManager.replaceMenu.static"; +} + +- (NSOperationQueuePriority)queuePriority { + return NSOperationQueuePriorityNormal; +} + +- (nullable NSError *)error { + return self.internalError; +} + @end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h new file mode 100644 index 000000000..19eb28799 --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -0,0 +1,42 @@ +// +// SDLMenuReplaceUtilities.h +// SmartDeviceLink +// +// Created by Joel Fischer on 1/22/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import + +#import "SDLMenuLayout.h" + +@class SDLArtwork; +@class SDLFileManager; +@class SDLMenuCell; +@class SDLRPCRequest; +@class SDLWindowCapability; + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLMenuReplaceUtilities : NSObject + ++ (NSArray *)deleteCommandsForCells:(NSArray *)cells; + +/** + This method will receive the cells to be added and the updated menu array. It will then build an array of add commands using the correct index to position the new items in the correct location. + + @param cells that will be added to the menu, this array must contain only cells that are not already in the menu. + @param shouldHaveArtwork artwork bool + @param menu the new menu array, this array should contain all the values the develeoper has set to be included in the new menu. This is used for placing the newly added cells in the correct locaiton. + e.g. If the new menu array is [A, B, C, D] but only [C, D] are new we need to pass [A, B , C , D] so C and D can be added to index 2 and 3 respectively. + @return list of SDLRPCRequest addCommands + */ ++ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork usingIndexesFrom:(NSArray *)menu availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; ++ (NSArray *)subMenuCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; + ++ (NSArray *)findAllArtworksToBeUploadedFromCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability; ++ (BOOL)shouldRPCsIncludeImages:(NSArray *)cells fileManager:(SDLFileManager *)fileManager; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m new file mode 100644 index 000000000..4c3d47aee --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -0,0 +1,158 @@ +// +// SDLMenuReplaceUtilities.m +// SmartDeviceLink +// +// Created by Joel Fischer on 1/22/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import "SDLMenuReplaceUtilities.h" + +#import "SDLAddCommand.h" +#import "SDLAddSubMenu.h" +#import "SDLArtwork.h" +#import "SDLDeleteCommand.h" +#import "SDLDeleteSubMenu.h" +#import "SDLFileManager.h" +#import "SDLImage.h" +#import "SDLImageFieldName.h" +#import "SDLMenuCell.h" +#import "SDLMenuParams.h" +#import "SDLRPCRequest.h" +#import "SDLWindowCapability.h" +#import "SDLWindowCapability+ScreenManagerExtensions.h" + +@interface SDLMenuCell() + +@property (assign, nonatomic) UInt32 parentCellId; +@property (assign, nonatomic) UInt32 cellId; + +@end + +@implementation SDLMenuReplaceUtilities + +#pragma mark Delete Commands + ++ (NSArray *)deleteCommandsForCells:(NSArray *)cells { + NSMutableArray *mutableDeletes = [NSMutableArray array]; + for (SDLMenuCell *cell in cells) { + if (cell.subCells == nil) { + SDLDeleteCommand *delete = [[SDLDeleteCommand alloc] initWithId:cell.cellId]; + [mutableDeletes addObject:delete]; + } else { + SDLDeleteSubMenu *delete = [[SDLDeleteSubMenu alloc] initWithId:cell.cellId]; + [mutableDeletes addObject:delete]; + } + } + + return [mutableDeletes copy]; +} + +#pragma mark Artworks + ++ (NSArray *)findAllArtworksToBeUploadedFromCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { + if (![windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]) { + return @[]; + } + + NSMutableSet *mutableArtworks = [NSMutableSet set]; + for (SDLMenuCell *cell in cells) { + if ([fileManager fileNeedsUpload:cell.icon]) { + [mutableArtworks addObject:cell.icon]; + } + + if (cell.subCells.count > 0) { + [mutableArtworks addObjectsFromArray:[self findAllArtworksToBeUploadedFromCells:cell.subCells fileManager:fileManager windowCapability:windowCapability]]; + } + } + + return [mutableArtworks allObjects]; +} + ++ (BOOL)shouldRPCsIncludeImages:(NSArray *)cells fileManager:(SDLFileManager *)fileManager { + for (SDLMenuCell *cell in cells) { + SDLArtwork *artwork = cell.icon; + if (artwork != nil && !artwork.isStaticIcon && ![fileManager hasUploadedFile:artwork]) { + return NO; + } else if (cell.subCells.count > 0) { + return [self shouldRPCsIncludeImages:cell.subCells fileManager:fileManager]; + } + } + + return YES; +} + ++ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork usingIndexesFrom:(NSArray *)menu availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { + NSMutableArray *mutableCommands = [NSMutableArray array]; + + for (NSUInteger menuInteger = 0; menuInteger < menu.count; menuInteger++) { + for (NSUInteger updateCellsIndex = 0; updateCellsIndex < cells.count; updateCellsIndex++) { + if ([menu[menuInteger] isEqual:cells[updateCellsIndex]]) { + if (cells[updateCellsIndex].subCells.count > 0) { + [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[updateCellsIndex] withArtwork:shouldHaveArtwork position:(UInt16)menuInteger availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; + } else { + [mutableCommands addObject:[self sdl_commandForMenuCell:cells[updateCellsIndex] withArtwork:shouldHaveArtwork position:(UInt16)menuInteger]]; + } + } + } + } + + return [mutableCommands copy]; +} + ++ (NSArray *)subMenuCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { + NSMutableArray *mutableCommands = [NSMutableArray array]; + for (SDLMenuCell *cell in cells) { + if (cell.subCells.count > 0) { + [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cell.subCells withArtwork:shouldHaveArtwork availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; + } + } + + return [mutableCommands copy]; +} + ++ (NSArray *)sdl_allCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { + NSMutableArray *mutableCommands = [NSMutableArray array]; + + for (NSUInteger cellIndex = 0; cellIndex < cells.count; cellIndex++) { + if (cells[cellIndex].subCells.count > 0) { + [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[cellIndex] withArtwork:shouldHaveArtwork position:(UInt16)cellIndex availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; + [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cells[cellIndex].subCells withArtwork:shouldHaveArtwork availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; + } else { + [mutableCommands addObject:[self sdl_commandForMenuCell:cells[cellIndex] withArtwork:shouldHaveArtwork position:(UInt16)cellIndex]]; + } + } + + return [mutableCommands copy]; +} + ++ (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell withArtwork:(BOOL)shouldHaveArtwork position:(UInt16)position { + SDLAddCommand *command = [[SDLAddCommand alloc] init]; + + SDLMenuParams *params = [[SDLMenuParams alloc] init]; + params.menuName = cell.title; + params.parentID = cell.parentCellId != UINT32_MAX ? @(cell.parentCellId) : nil; + params.position = @(position); + + command.menuParams = params; + command.vrCommands = (cell.voiceCommands.count == 0) ? nil : cell.voiceCommands; + command.cmdIcon = (cell.icon && shouldHaveArtwork) ? cell.icon.imageRPC : nil; + command.cmdID = @(cell.cellId); + + return command; +} + ++ (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell withArtwork:(BOOL)shouldHaveArtwork position:(UInt16)position availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { + SDLImage *icon = (shouldHaveArtwork && (cell.icon.name != nil)) ? cell.icon.imageRPC : nil; + + SDLMenuLayout submenuLayout = nil; + if (cell.submenuLayout && [availableMenuLayouts containsObject:cell.submenuLayout]) { + submenuLayout = cell.submenuLayout; + } else { + submenuLayout = defaultSubmenuLayout; + } + + return [[SDLAddSubMenu alloc] initWithMenuID:cell.cellId menuName:cell.title position:@(position) menuIcon:icon menuLayout:submenuLayout parentID:nil]; +} + +@end From 3a83a1c198d307c043947e3f723765e1feafe7fd Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 25 Jan 2021 11:23:04 -0500 Subject: [PATCH 005/112] Move a method into dynamic operation * Configuration update early returns need to finish the operation --- .../SDLMenuConfigurationUpdateOperation.m | 6 ++-- SmartDeviceLink/private/SDLMenuManager.m | 34 ++----------------- .../private/SDLMenuReplaceDynamicOperation.m | 30 ++++++++++++++++ 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m index 146f38b02..c09069d91 100644 --- a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m @@ -48,14 +48,14 @@ - (void)start { if ([[SDLGlobals sharedGlobals].rpcVersion isLessThanVersion:[SDLVersion versionWithMajor:6 minor:0 patch:0]]) { SDLLogW(@"Menu configurations is only supported on head units with RPC spec version 6.0.0 or later. Currently connected head unit RPC spec version is %@", [SDLGlobals sharedGlobals].rpcVersion); - return; + return [self finishOperation]; } else if (self.availableMenuLayouts == nil) { SDLLogW(@"Could not set the main menu configuration. Which menu layouts can be used is not available"); - return; + return [self finishOperation]; } else if (![self.availableMenuLayouts containsObject:self.updatedMenuConfiguration.mainMenuLayout] || ![self.availableMenuLayouts containsObject:self.updatedMenuConfiguration.defaultSubmenuLayout]) { SDLLogE(@"One or more of the set menu layouts are not available on this system. The menu configuration will not be set. Available menu layouts: %@, set menu layouts: %@", self.availableMenuLayouts, self.updatedMenuConfiguration); - return; + return [self finishOperation]; } SDLSetGlobalProperties *setGlobalsRPC = [[SDLSetGlobalProperties alloc] init]; diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 673804f85..82082fdd6 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -169,6 +169,8 @@ - (void)setMenuConfiguration:(SDLMenuConfiguration *)menuConfiguration { } else { strongself.oldMenuConfiguration = nil; } + + // TODO: Use custom completion block and update the replace operations with new menu configuration }; // Cancel previous menu configuration operations @@ -251,38 +253,6 @@ - (BOOL)openMenu:(nullable SDLMenuCell *)cell { return YES; } -#pragma mark - Build Deletes, Keeps, Adds - -- (void)sdl_startSubMenuUpdatesWithOldKeptCells:(NSArray *)oldKeptCells newKeptCells:(NSArray *)newKeptCells atIndex:(NSUInteger)startIndex { - if (oldKeptCells.count == 0 || startIndex >= oldKeptCells.count) { return; } - - if (oldKeptCells[startIndex].subCells.count > 0) { - SDLDynamicMenuUpdateRunScore *tempScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldKeptCells[startIndex].subCells updatedMenuCells:newKeptCells[startIndex].subCells]; - NSArray *deleteMenuStatus = tempScore.oldStatus; - NSArray *addMenuStatus = tempScore.updatedStatus; - - NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; - NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; - - NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; - NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; - - [self sdl_updateIdsOnMenuCells:cellsToAdd parentId:newKeptCells[startIndex].cellId]; - [self transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; - - __weak typeof(self) weakself = self; - [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { - [weakself sdl_sendUpdatedMenu:cellsToAdd usingMenu:weakself.menuCells[startIndex].subCells withCompletionHandler:^(NSError * _Nullable error) { - // After the first set of submenu cells were added and deleted we must find the next set of subcells untll we loop through all the elemetns - [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; - }]; - }]; - } else { - // After the first set of submenu cells were added and deleted we must find the next set of subcells untll we loop through all the elemetns - [self sdl_startSubMenuUpdatesWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; - } -} - #pragma mark - Updating System - (void)sdl_startDynamicMenuUpdate { diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m index 1714cba0d..c03149a1a 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m @@ -97,6 +97,36 @@ - (void)start { #pragma mark Dynamic Menu Helpers +- (void)sdl_startSubMenuUpdatesWithOldKeptCells:(NSArray *)oldKeptCells newKeptCells:(NSArray *)newKeptCells atIndex:(NSUInteger)startIndex { + if (oldKeptCells.count == 0 || startIndex >= oldKeptCells.count) { return; } + + if (oldKeptCells[startIndex].subCells.count > 0) { + SDLDynamicMenuUpdateRunScore *tempScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldKeptCells[startIndex].subCells updatedMenuCells:newKeptCells[startIndex].subCells]; + NSArray *deleteMenuStatus = tempScore.oldStatus; + NSArray *addMenuStatus = tempScore.updatedStatus; + + NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; + NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; + + NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; + NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; + + [self sdl_updateIdsOnMenuCells:cellsToAdd parentId:newKeptCells[startIndex].cellId]; + [self transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; + + __weak typeof(self) weakself = self; + [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { + [weakself sdl_sendUpdatedMenu:cellsToAdd usingMenu:weakself.menuCells[startIndex].subCells withCompletionHandler:^(NSError * _Nullable error) { + // After the first set of submenu cells were added and deleted we must find the next set of subcells untll we loop through all the elemetns + [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; + }]; + }]; + } else { + // After the first set of submenu cells were added and deleted we must find the next set of subcells untll we loop through all the elemetns + [self sdl_startSubMenuUpdatesWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; + } +} + - (NSArray *)sdl_filterDeleteMenuItemsWithOldMenuItems:(NSArray *)oldMenuCells basedOnStatusList:(NSArray *)oldStatusList { NSMutableArray *deleteCells = [[NSMutableArray alloc] init]; // The index of the status should corrleate 1-1 with the number of items in the menu From e6ce6ead5c38b30834389daf3bc3c567dae7f32e Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 25 Jan 2021 13:52:15 -0500 Subject: [PATCH 006/112] Fixes for menu manager * Move enum to update algorithm file in order to make it more easily available to operations * Fix menu manager hmi level / menu context suspension of queue * Move updating menu cells to delete / send to dynamic operation * Fix setting ids to be in menu manager and out of dynamic operation --- .../private/SDLDynamicMenuUpdateAlgorithm.h | 14 +++ .../private/SDLDynamicMenuUpdateAlgorithm.m | 1 - SmartDeviceLink/private/SDLMenuManager.m | 46 ++------- .../private/SDLMenuReplaceDynamicOperation.m | 94 ++++++++++++++++++- .../private/SDLVoiceCommandManager.m | 4 +- .../public/SDLMenuManagerConstants.h | 15 --- 6 files changed, 113 insertions(+), 61 deletions(-) diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h index 64d7d2585..d2d117e51 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h @@ -13,6 +13,20 @@ NS_ASSUME_NONNULL_BEGIN +/// Menu cell state +/// +/// Cell state that tells the menu manager what it should do with a given SDLMenuCell +typedef NS_ENUM(NSUInteger, MenuCellState) { + /// Marks the cell to be deleted + MenuCellStateDelete = 0, + + /// Marks the cell to be added + MenuCellStateAdd, + + /// Marks the cell to be kept + MenuCellStateKeep +}; + @interface SDLDynamicMenuUpdateAlgorithm : NSObject /** diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m index 51bfd14e2..160c4ddda 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m @@ -10,7 +10,6 @@ #import "SDLDynamicMenuUpdateRunScore.h" #import "SDLMenuCell.h" #import "SDLLogMacros.h" -#import "SDLMenuManagerConstants.h" NS_ASSUME_NONNULL_BEGIN diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 82082fdd6..cb8a5b840 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -136,8 +136,8 @@ - (NSOperationQueue *)sdl_newTransactionQueue { /// Suspend the queue if the HMI level is NONE since we want to delay sending RPCs until we're in non-NONE - (void)sdl_updateTransactionQueueSuspended { - if ([self.currentHMILevel isEqualToEnum:SDLHMILevelNone]) { - SDLLogD(@"Suspending the transaction queue. Current HMI level is NONE: %@", ([self.currentHMILevel isEqualToEnum:SDLHMILevelNone] ? @"YES" : @"NO")); + if ([self.currentHMILevel isEqualToEnum:SDLHMILevelNone] || [self.currentSystemContext isEqualToEnum:SDLSystemContextMenu]) { + SDLLogD(@"Suspending the transaction queue. Current HMI level is NONE: %@, current system context is MENU: %@", ([self.currentHMILevel isEqualToEnum:SDLHMILevelNone] ? @"YES" : @"NO"), ([self.currentSystemContext isEqualToEnum:SDLSystemContextMenu] ? @"YES" : @"NO")); self.transactionQueue.suspended = YES; } else { SDLLogD(@"Starting the transaction queue"); @@ -207,7 +207,8 @@ - (void)setMenuCells:(NSArray *)menuCells { return; } - _currentMenuCells = _menuCells; + [self sdl_updateIdsOnMenuCells:menuCells parentId:ParentIdNotFound]; + _menuCells = menuCells; if ([self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) { @@ -263,13 +264,6 @@ - (void)sdl_startStaticMenuUpdate { } -- (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(nullable SDLMenuUpdateCompletionHandler)completionHandler { - __weak typeof(self) weakself = self; - [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { - [weakself sdl_sendUpdatedMenu:addCells usingMenu:weakself.menuCells withCompletionHandler:^(NSError * _Nullable error) { }]; - }]; -} - #pragma mark - Helpers - (BOOL)sdl_isDynamicMenuUpdateActive:(SDLDynamicMenuUpdatesMode)dynamicMenuUpdatesMode { @@ -291,7 +285,9 @@ - (BOOL)sdl_isDynamicMenuUpdateActive:(SDLDynamicMenuUpdatesMode)dynamicMenuUpda - (void)sdl_updateIdsOnMenuCells:(NSArray *)menuCells parentId:(UInt32)parentId { for (SDLMenuCell *cell in menuCells) { cell.cellId = self.lastMenuId++; - cell.parentCellId = parentId; + if (parentId != ParentIdNotFound) { + cell.parentCellId = parentId; + } if (cell.subCells.count > 0) { [self sdl_updateIdsOnMenuCells:cell.subCells parentId:cell.cellId]; } @@ -331,36 +327,12 @@ - (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability { - (void)sdl_hmiStatusNotification:(SDLRPCNotificationNotification *)notification { SDLOnHMIStatus *hmiStatus = (SDLOnHMIStatus *)notification.notification; + if ((hmiStatus.windowID != nil) && (hmiStatus.windowID.integerValue != SDLPredefinedWindowsDefaultWindow)) { return; } - if (hmiStatus.windowID != nil && hmiStatus.windowID.integerValue != SDLPredefinedWindowsDefaultWindow) { - return; - } - - SDLHMILevel oldHMILevel = self.currentHMILevel; self.currentHMILevel = hmiStatus.hmiLevel; - - // Auto-send an updated menu if we were in NONE and now we are not, and we need an update - if ([oldHMILevel isEqualToString:SDLHMILevelNone] && ![self.currentHMILevel isEqualToString:SDLHMILevelNone] && - ![self.currentSystemContext isEqualToEnum:SDLSystemContextMenu]) { - if (self.waitingOnHMIUpdate) { - [self setMenuCells:self.waitingUpdateMenuCells]; - self.waitingUpdateMenuCells = @[]; - return; - } - } - - // If we don't check for this and only update when not in the menu, there can be IN_USE errors, especially with submenus. We also don't want to encourage changing out the menu while the user is using it for usability reasons. - SDLSystemContext oldSystemContext = self.currentSystemContext; self.currentSystemContext = hmiStatus.systemContext; - if ([oldSystemContext isEqualToEnum:SDLSystemContextMenu] - && ![self.currentSystemContext isEqualToEnum:SDLSystemContextMenu] - && ![self.currentHMILevel isEqualToEnum:SDLHMILevelNone]) { - if (self.waitingOnHMIUpdate) { - [self setMenuCells:self.waitingUpdateMenuCells]; - self.waitingUpdateMenuCells = @[]; - } - } + [self sdl_updateTransactionQueueSuspended]; } @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m index c03149a1a..77d1ac392 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m @@ -22,6 +22,17 @@ #import "SDLWindowCapability.h" #import "SDLWindowCapability+ScreenManagerExtensions.h" +NS_ASSUME_NONNULL_BEGIN + +typedef void(^SDLMenuUpdateCompletionHandler)(NSError *__nullable error); + +@interface SDLMenuCell() + +@property (assign, nonatomic) UInt32 parentCellId; +@property (assign, nonatomic) UInt32 cellId; + +@end + @interface SDLMenuReplaceDynamicOperation () @property (weak, nonatomic) id connectionManager; @@ -66,9 +77,6 @@ - (void)start { NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:deleteMenuStatus]; NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:addMenuStatus]; - // Cells that will be added need new ids - [self sdl_updateIdsOnMenuCells:cellsToAdd parentId:ParentIdNotFound]; - // Since we are creating a new Menu but keeping old cells we must firt transfer the old cellIDs to the new menus kept cells. [self transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; @@ -95,6 +103,81 @@ - (void)start { } } +- (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(nullable SDLMenuUpdateCompletionHandler)completionHandler { + __weak typeof(self) weakself = self; + [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { + [weakself sdl_sendNewMenuCells:addCells oldMenu:weakself.currentMenu withCompletionHandler:^(NSError * _Nullable error) { }]; + }]; +} + +- (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { + if (deleteMenuCells.count == 0) { + completionHandler(nil); + return; + } + + NSArray *deleteMenuCommands = [SDLMenuReplaceUtilities deleteCommandsForCells:deleteMenuCells]; + [self.connectionManager sendRequests:deleteMenuCommands progressHandler:nil completionHandler:^(BOOL success) { + if (!success) { + SDLLogW(@"Unable to delete all old menu commands"); + } else { + SDLLogD(@"Finished deleting old menu"); + } + + completionHandler(nil); + }]; +} + +- (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSArray *)oldMenu withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { + if (self.updatedMenu.count == 0 || newMenuCells.count == 0) { + SDLLogD(@"There are no cells to update."); + completionHandler(nil); + return; + } + + NSArray *mainMenuCommands = nil; + NSArray *subMenuCommands = nil; + + if (![SDLMenuReplaceUtilities shouldRPCsIncludeImages:self.updatedMenu fileManager:self.fileManager] || ![self.windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]) { + // Send artwork-less menu + mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells withArtwork:NO usingIndexesFrom:self.updatedMenu availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells withArtwork:NO availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + } else { + // Send full artwork menu + mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells withArtwork:YES usingIndexesFrom:self.updatedMenu availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells withArtwork:YES availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + } + + __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; + __weak typeof(self) weakSelf = self; + [self.connectionManager sendRequests:mainMenuCommands progressHandler:^void(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { + if (error != nil) { + errors[request] = error; + } + } completionHandler:^(BOOL success) { + if (!success) { + SDLLogE(@"Failed to send main menu commands: %@", errors); + completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); + return; + } + + [weakSelf.connectionManager sendRequests:subMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { + if (error != nil) { + errors[request] = error; + } + } completionHandler:^(BOOL success) { + if (!success) { + SDLLogE(@"Failed to send sub menu commands: %@", errors); + completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); + return; + } + + SDLLogD(@"Finished updating menu"); + completionHandler(nil); + }]; + }]; +} + #pragma mark Dynamic Menu Helpers - (void)sdl_startSubMenuUpdatesWithOldKeptCells:(NSArray *)oldKeptCells newKeptCells:(NSArray *)newKeptCells atIndex:(NSUInteger)startIndex { @@ -111,12 +194,11 @@ - (void)sdl_startSubMenuUpdatesWithOldKeptCells:(NSArray *)oldKep NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; - [self sdl_updateIdsOnMenuCells:cellsToAdd parentId:newKeptCells[startIndex].cellId]; [self transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { - [weakself sdl_sendUpdatedMenu:cellsToAdd usingMenu:weakself.menuCells[startIndex].subCells withCompletionHandler:^(NSError * _Nullable error) { + [weakself sdl_sendNewMenuCells:cellsToAdd oldMenu:weakself.currentMenu[startIndex].subCells withCompletionHandler:^(NSError * _Nullable error) { // After the first set of submenu cells were added and deleted we must find the next set of subcells untll we loop through all the elemetns [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; }]; @@ -180,3 +262,5 @@ - (void)transferCellIDFromOldCells:(NSArray *)oldCells toKeptCell } @end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLVoiceCommandManager.m b/SmartDeviceLink/private/SDLVoiceCommandManager.m index 978f53eee..0adfb8fde 100644 --- a/SmartDeviceLink/private/SDLVoiceCommandManager.m +++ b/SmartDeviceLink/private/SDLVoiceCommandManager.m @@ -163,9 +163,7 @@ - (void)sdl_commandNotification:(SDLRPCNotificationNotification *)notification { - (void)sdl_hmiStatusNotification:(SDLRPCNotificationNotification *)notification { SDLOnHMIStatus *hmiStatus = (SDLOnHMIStatus *)notification.notification; - if (hmiStatus.windowID != nil && hmiStatus.windowID.integerValue != SDLPredefinedWindowsDefaultWindow) { - return; - } + if ((hmiStatus.windowID != nil) && (hmiStatus.windowID.integerValue != SDLPredefinedWindowsDefaultWindow)) { return; } self.currentLevel = hmiStatus.hmiLevel; [self sdl_updateTransactionQueueSuspended]; diff --git a/SmartDeviceLink/public/SDLMenuManagerConstants.h b/SmartDeviceLink/public/SDLMenuManagerConstants.h index 5e23971e9..5ceab2d4f 100644 --- a/SmartDeviceLink/public/SDLMenuManagerConstants.h +++ b/SmartDeviceLink/public/SDLMenuManagerConstants.h @@ -22,18 +22,3 @@ typedef NS_ENUM(NSUInteger, SDLDynamicMenuUpdatesMode) { /// This mode checks whether the phone is connected to a SYNC Gen 3 head unit, which has known menu ordering issues. If it is, it will always delete and re-add every menu item, if not, it will dynamically update the menus. SDLDynamicMenuUpdatesModeOnWithCompatibility }; - - -/// Menu cell state -/// -/// Cell state that tells the menu manager what it should do with a given SDLMenuCell -typedef NS_ENUM(NSUInteger, MenuCellState) { - /// Marks the cell to be deleted - MenuCellStateDelete = 0, - - /// Marks the cell to be added - MenuCellStateAdd, - - /// Marks the cell to be kept - MenuCellStateKeep -}; From 1b9caeea325e34d7a289c1c06d5a1ae76b6067c3 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 25 Jan 2021 15:54:19 -0500 Subject: [PATCH 007/112] Update static operation with finishOperation calls * Refactor checking artworks to use whichever ones actually uploaded and skipping showing any that failed * Add menu replace operation cancelled errors --- SmartDeviceLink/private/SDLError.h | 2 + SmartDeviceLink/private/SDLError.m | 12 +++- .../private/SDLMenuReplaceDynamicOperation.m | 41 ++++++++---- .../private/SDLMenuReplaceStaticOperation.m | 65 +++++++++++-------- .../private/SDLMenuReplaceUtilities.h | 15 +---- .../private/SDLMenuReplaceUtilities.m | 44 +++++-------- SmartDeviceLink/public/SDLErrorConstants.h | 5 +- 7 files changed, 100 insertions(+), 84 deletions(-) diff --git a/SmartDeviceLink/private/SDLError.h b/SmartDeviceLink/private/SDLError.h index 0b6a7d9a5..7099439b4 100644 --- a/SmartDeviceLink/private/SDLError.h +++ b/SmartDeviceLink/private/SDLError.h @@ -61,7 +61,9 @@ NS_ASSUME_NONNULL_BEGIN + (NSError *)sdl_menuManager_configurationOperationFailed:(SDLMenuConfiguration *)failedConfiguration; + (NSError *)sdl_menuManager_openMenuOperationCancelled; + (NSError *)sdl_menuManager_openMenuOperationFailed:(nullable SDLMenuCell *)menuCell; ++ (NSError *)sdl_menuManager_replaceOperationCancelled; + (NSError *)sdl_menuManager_failedToUpdateWithDictionary:(NSDictionary *)userInfo; + + (NSError *)sdl_voiceCommandManager_pendingUpdateSuperseded; #pragma mark Choice Set Manager diff --git a/SmartDeviceLink/private/SDLError.m b/SmartDeviceLink/private/SDLError.m index 05338b72b..bf3e1239e 100644 --- a/SmartDeviceLink/private/SDLError.m +++ b/SmartDeviceLink/private/SDLError.m @@ -260,7 +260,7 @@ + (NSError *)sdl_subscribeButtonManager_notSubscribed { #pragma mark Menu Manager + (NSError *)sdl_menuManager_configurationOperationCancelled { - return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorConfigurationUpdateCancelled userInfo:@{ + return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorOperationCancelled userInfo:@{ NSLocalizedDescriptionKey: @"Menu Manager - Configuration Update Cancelled", NSLocalizedFailureReasonErrorKey: @"The menu manager was probably stopped or another configuration update was requested.", NSLocalizedRecoverySuggestionErrorKey: @"This error probably does not need recovery." @@ -277,7 +277,7 @@ + (NSError *)sdl_menuManager_configurationOperationFailed:(SDLMenuConfiguration } + (NSError *)sdl_menuManager_openMenuOperationCancelled { - return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorConfigurationUpdateCancelled userInfo:@{ + return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorOperationCancelled userInfo:@{ NSLocalizedDescriptionKey: @"Menu Manager - Open Menu Cancelled", NSLocalizedFailureReasonErrorKey: @"The menu manager was probably stopped or opening another menu item was requested.", NSLocalizedRecoverySuggestionErrorKey: @"This error probably does not need recovery." @@ -299,6 +299,14 @@ + (NSError *)sdl_menuManager_openMenuOperationFailed:(nullable SDLMenuCell *)men }]; } ++ (NSError *)sdl_menuManager_replaceOperationCancelled { + return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorOperationCancelled userInfo:@{ + NSLocalizedDescriptionKey: @"Menu Manager - Menu Replace Cancelled", + NSLocalizedFailureReasonErrorKey: @"The menu manager was probably stopped or another menu update was requested.", + NSLocalizedRecoverySuggestionErrorKey: @"This error probably does not need recovery." + }]; +} + + (NSError *)sdl_menuManager_failedToUpdateWithDictionary:(NSDictionary *)userInfo { return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorRPCsFailed userInfo:userInfo]; } diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m index 77d1ac392..ae10c8697 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m @@ -82,6 +82,8 @@ - (void)start { // TODO: We don't check cancellation or finish NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; + + __weak typeof(self) weakself = self; if (artworksToBeUploaded.count > 0) { [self.fileManager uploadArtworks:artworksToBeUploaded completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { if (error != nil) { @@ -89,14 +91,12 @@ - (void)start { } SDLLogD(@"Menu artworks uploaded"); - __weak typeof(self) weakself = self; [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; }]; }]; } else { // Cells have no artwork to load - __weak typeof(self) weakself = self; [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; }]; @@ -135,18 +135,8 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA return; } - NSArray *mainMenuCommands = nil; - NSArray *subMenuCommands = nil; - - if (![SDLMenuReplaceUtilities shouldRPCsIncludeImages:self.updatedMenu fileManager:self.fileManager] || ![self.windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]) { - // Send artwork-less menu - mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells withArtwork:NO usingIndexesFrom:self.updatedMenu availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; - subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells withArtwork:NO availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; - } else { - // Send full artwork menu - mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells withArtwork:YES usingIndexesFrom:self.updatedMenu availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; - subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells withArtwork:YES availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; - } + NSArray *mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells fileManager:self.fileManager usingIndexesFrom:self.updatedMenu availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout];; + NSArray *subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells fileManager:self.fileManager availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; __weak typeof(self) weakSelf = self; @@ -261,6 +251,29 @@ - (void)transferCellIDFromOldCells:(NSArray *)oldCells toKeptCell } } +#pragma mark - Operation Overrides + +- (void)finishOperation { + SDLLogV(@"Finishing menu manager dynamic replace operation"); + if (self.isCancelled) { + self.internalError = [NSError sdl_menuManager_replaceOperationCancelled]; + } + + [super finishOperation]; +} + +- (nullable NSString *)name { + return @"com.sdl.menuManager.replaceMenu.dynamic"; +} + +- (NSOperationQueuePriority)queuePriority { + return NSOperationQueuePriorityNormal; +} + +- (nullable NSError *)error { + return self.internalError; +} + @end NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index 740f871c0..1b8dd902c 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -61,18 +61,27 @@ - (void)start { // TODO: We don't check cancellation or finish NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; + + __weak typeof(self) weakself = self; if (artworksToBeUploaded.count > 0) { - [self.fileManager uploadArtworks:artworksToBeUploaded completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { + [self.fileManager uploadArtworks:artworksToBeUploaded progressHandler:^BOOL(NSString * _Nonnull artworkName, float uploadPercentage, NSError * _Nullable error) { + if (weakself.isCancelled) { + [weakself finishOperation]; + return NO; + } + + return YES; + } completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { if (error != nil) { SDLLogE(@"Error uploading menu artworks: %@", error); } - SDLLogD(@"Menu artworks uploaded"); - [self sdl_updateMenuWithCellsToDelete:self.currentMenu cellsToAdd:self.updatedMenu completionHandler:nil]; + SDLLogD(@"All menu artworks uploaded"); + [self sdl_updateMenuWithCellsToDelete:self.currentMenu cellsToAdd:self.updatedMenu]; }]; } else { // Cells have no artwork to load - [self sdl_updateMenuWithCellsToDelete:self.currentMenu cellsToAdd:self.updatedMenu completionHandler:nil]; + [self sdl_updateMenuWithCellsToDelete:self.currentMenu cellsToAdd:self.updatedMenu]; } } @@ -80,10 +89,16 @@ - (void)start { #pragma mark Sending Items -- (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(nullable SDLMenuUpdateCompletionHandler)completionHandler { +- (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells { __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { - [weakself sdl_sendNewMenuCells:addCells withCompletionHandler:^(NSError * _Nullable error) { }]; + if (self.isCancelled) { + return [weakself finishOperation]; + } + + [weakself sdl_sendNewMenuCells:addCells withCompletionHandler:^(NSError * _Nullable error) { + [weakself finishOperation]; + }]; }]; } @@ -100,18 +115,8 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti return; } - NSArray *mainMenuCommands = nil; - NSArray *subMenuCommands = nil; - - if (![SDLMenuReplaceUtilities shouldRPCsIncludeImages:self.updatedMenu fileManager:self.fileManager] || ![self.windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]) { - // Send artwork-less menu - mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells withArtwork:NO usingIndexesFrom:self.updatedMenu availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; - subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells withArtwork:NO availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; - } else { - // Send full artwork menu - mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells withArtwork:YES usingIndexesFrom:self.updatedMenu availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; - subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells withArtwork:YES availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; - } + NSArray *mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells fileManager:self.fileManager usingIndexesFrom:self.updatedMenu availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + NSArray *subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells fileManager:self.fileManager availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; __weak typeof(self) weakSelf = self; @@ -126,6 +131,10 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti return; } + if (self.isCancelled) { + return [weakSelf finishOperation]; + } + [weakSelf.connectionManager sendRequests:subMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { if (error != nil) { errors[request] = error; @@ -147,28 +156,32 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { if (deleteMenuCells.count == 0) { - completionHandler(nil); - return; + return completionHandler(nil); } NSArray *deleteMenuCommands = [SDLMenuReplaceUtilities deleteCommandsForCells:deleteMenuCells]; - [self.connectionManager sendRequests:deleteMenuCommands progressHandler:nil completionHandler:^(BOOL success) { + __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; + [self.connectionManager sendRequests:deleteMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { + if (error != nil) { + errors[request] = error; + } + } completionHandler:^(BOOL success) { if (!success) { - SDLLogW(@"Unable to delete all old menu commands"); + SDLLogE(@"Unable to delete all old menu commands with errors: %@", errors); + completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); } else { SDLLogD(@"Finished deleting old menu"); + completionHandler(nil); } - - completionHandler(nil); }]; } #pragma mark - Operation Overrides - (void)finishOperation { - SDLLogV(@"Finishing menu manager configuration update operation"); + SDLLogV(@"Finishing menu manager static replace operation"); if (self.isCancelled) { - self.internalError = [NSError sdl_menuManager_openMenuOperationCancelled]; + self.internalError = [NSError sdl_menuManager_replaceOperationCancelled]; } [super finishOperation]; diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index 19eb28799..61bb608df 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -22,20 +22,11 @@ NS_ASSUME_NONNULL_BEGIN + (NSArray *)deleteCommandsForCells:(NSArray *)cells; -/** - This method will receive the cells to be added and the updated menu array. It will then build an array of add commands using the correct index to position the new items in the correct location. - - @param cells that will be added to the menu, this array must contain only cells that are not already in the menu. - @param shouldHaveArtwork artwork bool - @param menu the new menu array, this array should contain all the values the develeoper has set to be included in the new menu. This is used for placing the newly added cells in the correct locaiton. - e.g. If the new menu array is [A, B, C, D] but only [C, D] are new we need to pass [A, B , C , D] so C and D can be added to index 2 and 3 respectively. - @return list of SDLRPCRequest addCommands - */ -+ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork usingIndexesFrom:(NSArray *)menu availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; -+ (NSArray *)subMenuCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; ++ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingIndexesFrom:(NSArray *)menu availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; ++ (NSArray *)subMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; + (NSArray *)findAllArtworksToBeUploadedFromCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability; -+ (BOOL)shouldRPCsIncludeImages:(NSArray *)cells fileManager:(SDLFileManager *)fileManager; +//+ (BOOL)shouldRPCsIncludeImages:(NSArray *)cells fileManager:(SDLFileManager *)fileManager; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 4c3d47aee..a7d8e2fb0 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -51,9 +51,7 @@ @implementation SDLMenuReplaceUtilities #pragma mark Artworks + (NSArray *)findAllArtworksToBeUploadedFromCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { - if (![windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]) { - return @[]; - } + if (![windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]) { return @[]; } NSMutableSet *mutableArtworks = [NSMutableSet set]; for (SDLMenuCell *cell in cells) { @@ -69,29 +67,21 @@ @implementation SDLMenuReplaceUtilities return [mutableArtworks allObjects]; } -+ (BOOL)shouldRPCsIncludeImages:(NSArray *)cells fileManager:(SDLFileManager *)fileManager { - for (SDLMenuCell *cell in cells) { - SDLArtwork *artwork = cell.icon; - if (artwork != nil && !artwork.isStaticIcon && ![fileManager hasUploadedFile:artwork]) { - return NO; - } else if (cell.subCells.count > 0) { - return [self shouldRPCsIncludeImages:cell.subCells fileManager:fileManager]; - } - } - - return YES; ++ (BOOL)sdl_shouldCellIncludeImage:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager { + // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image + return ((cell.icon != nil && [fileManager hasUploadedFile:cell.icon]) || cell.icon.isStaticIcon); } -+ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork usingIndexesFrom:(NSArray *)menu availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { ++ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingIndexesFrom:(NSArray *)menu availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { NSMutableArray *mutableCommands = [NSMutableArray array]; for (NSUInteger menuInteger = 0; menuInteger < menu.count; menuInteger++) { for (NSUInteger updateCellsIndex = 0; updateCellsIndex < cells.count; updateCellsIndex++) { if ([menu[menuInteger] isEqual:cells[updateCellsIndex]]) { if (cells[updateCellsIndex].subCells.count > 0) { - [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[updateCellsIndex] withArtwork:shouldHaveArtwork position:(UInt16)menuInteger availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; + [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[updateCellsIndex] fileManager:fileManager position:(UInt16)menuInteger availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; } else { - [mutableCommands addObject:[self sdl_commandForMenuCell:cells[updateCellsIndex] withArtwork:shouldHaveArtwork position:(UInt16)menuInteger]]; + [mutableCommands addObject:[self sdl_commandForMenuCell:cells[updateCellsIndex] fileManager:fileManager position:(UInt16)menuInteger]]; } } } @@ -100,33 +90,33 @@ + (BOOL)shouldRPCsIncludeImages:(NSArray *)cells fileManager:(SDL return [mutableCommands copy]; } -+ (NSArray *)subMenuCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { ++ (NSArray *)subMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { NSMutableArray *mutableCommands = [NSMutableArray array]; for (SDLMenuCell *cell in cells) { if (cell.subCells.count > 0) { - [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cell.subCells withArtwork:shouldHaveArtwork availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; + [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cell.subCells fileManager:fileManager availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; } } return [mutableCommands copy]; } -+ (NSArray *)sdl_allCommandsForCells:(NSArray *)cells withArtwork:(BOOL)shouldHaveArtwork availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { ++ (NSArray *)sdl_allCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { NSMutableArray *mutableCommands = [NSMutableArray array]; for (NSUInteger cellIndex = 0; cellIndex < cells.count; cellIndex++) { if (cells[cellIndex].subCells.count > 0) { - [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[cellIndex] withArtwork:shouldHaveArtwork position:(UInt16)cellIndex availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; - [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cells[cellIndex].subCells withArtwork:shouldHaveArtwork availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; + [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[cellIndex] fileManager:fileManager position:(UInt16)cellIndex availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; + [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cells[cellIndex].subCells fileManager:fileManager availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; } else { - [mutableCommands addObject:[self sdl_commandForMenuCell:cells[cellIndex] withArtwork:shouldHaveArtwork position:(UInt16)cellIndex]]; + [mutableCommands addObject:[self sdl_commandForMenuCell:cells[cellIndex] fileManager:fileManager position:(UInt16)cellIndex]]; } } return [mutableCommands copy]; } -+ (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell withArtwork:(BOOL)shouldHaveArtwork position:(UInt16)position { ++ (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager position:(UInt16)position { SDLAddCommand *command = [[SDLAddCommand alloc] init]; SDLMenuParams *params = [[SDLMenuParams alloc] init]; @@ -136,14 +126,14 @@ + (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell withArtwork:(BOOL) command.menuParams = params; command.vrCommands = (cell.voiceCommands.count == 0) ? nil : cell.voiceCommands; - command.cmdIcon = (cell.icon && shouldHaveArtwork) ? cell.icon.imageRPC : nil; + command.cmdIcon = [self sdl_shouldCellIncludeImage:cell fileManager:fileManager] ? cell.icon.imageRPC : nil; command.cmdID = @(cell.cellId); return command; } -+ (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell withArtwork:(BOOL)shouldHaveArtwork position:(UInt16)position availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { - SDLImage *icon = (shouldHaveArtwork && (cell.icon.name != nil)) ? cell.icon.imageRPC : nil; ++ (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager position:(UInt16)position availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { + SDLImage *icon = [self sdl_shouldCellIncludeImage:cell fileManager:fileManager] ? cell.icon.imageRPC : nil; SDLMenuLayout submenuLayout = nil; if (cell.submenuLayout && [availableMenuLayouts containsObject:cell.submenuLayout]) { diff --git a/SmartDeviceLink/public/SDLErrorConstants.h b/SmartDeviceLink/public/SDLErrorConstants.h index 324dcd581..9bf68ef38 100644 --- a/SmartDeviceLink/public/SDLErrorConstants.h +++ b/SmartDeviceLink/public/SDLErrorConstants.h @@ -188,10 +188,9 @@ typedef NS_ENUM(NSInteger, SDLMenuManagerError) { /// Sending menu-related RPCs returned an error from the remote system SDLMenuManagerErrorRPCsFailed = -1, SDLMenuManagerErrorPendingUpdateSuperseded = -2, - SDLMenuManagerErrorConfigurationUpdateCancelled = -3, + SDLMenuManagerErrorOperationCancelled = -3, SDLMenuManagerErrorConfigurationUpdateFailed = -4, - SDLMenuManagerErrorOpenMenuCancelled = -5, - SDLMenuManagerErrorOpenMenuFailed = -6 + SDLMenuManagerErrorOpenMenuFailed = -5 }; /// Errors associated with Choice Set class From 52961d7a290426d4f717f42b7d21f784366fb189 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 25 Jan 2021 15:56:06 -0500 Subject: [PATCH 008/112] Minor updates --- .../private/SDLMenuReplaceStaticOperation.m | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index 1b8dd902c..a31dbbf6b 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -58,11 +58,8 @@ - (void)start { return; } - // TODO: We don't check cancellation or finish - - NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; - __weak typeof(self) weakself = self; + NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; if (artworksToBeUploaded.count > 0) { [self.fileManager uploadArtworks:artworksToBeUploaded progressHandler:^BOOL(NSString * _Nonnull artworkName, float uploadPercentage, NSError * _Nullable error) { if (weakself.isCancelled) { @@ -102,12 +99,6 @@ - (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells ce }]; } -/** - Creates add commands - - @param newMenuCells The cells you will be adding - @param completionHandler handler - */ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { if (self.updatedMenu.count == 0 || newMenuCells.count == 0) { SDLLogD(@"There are no cells to update."); From e7d5bb1e0455bc5264d0769095f6a402ce13ed95 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 26 Jan 2021 14:53:10 -0500 Subject: [PATCH 009/112] Update menu replace utility to check window capability --- .../private/SDLMenuReplaceDynamicOperation.m | 4 +-- .../private/SDLMenuReplaceStaticOperation.m | 4 +-- .../private/SDLMenuReplaceUtilities.h | 5 ++- .../private/SDLMenuReplaceUtilities.m | 33 ++++++++++--------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m index ae10c8697..526a2eddb 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m @@ -135,8 +135,8 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA return; } - NSArray *mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells fileManager:self.fileManager usingIndexesFrom:self.updatedMenu availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout];; - NSArray *subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells fileManager:self.fileManager availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + NSArray *mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells fileManager:self.fileManager usingIndexesFrom:self.updatedMenu windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout];; + NSArray *subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells fileManager:self.fileManager windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; __weak typeof(self) weakSelf = self; diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index a31dbbf6b..4cc98fe5b 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -106,8 +106,8 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti return; } - NSArray *mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells fileManager:self.fileManager usingIndexesFrom:self.updatedMenu availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; - NSArray *subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells fileManager:self.fileManager availableMenuLayouts:self.windowCapability.menuLayoutsAvailable defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + NSArray *mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells fileManager:self.fileManager usingIndexesFrom:self.updatedMenu windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + NSArray *subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells fileManager:self.fileManager windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; __weak typeof(self) weakSelf = self; diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index 61bb608df..1fa70f570 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -22,11 +22,10 @@ NS_ASSUME_NONNULL_BEGIN + (NSArray *)deleteCommandsForCells:(NSArray *)cells; -+ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingIndexesFrom:(NSArray *)menu availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; -+ (NSArray *)subMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; ++ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingIndexesFrom:(NSArray *)menu windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; ++ (NSArray *)subMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; + (NSArray *)findAllArtworksToBeUploadedFromCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability; -//+ (BOOL)shouldRPCsIncludeImages:(NSArray *)cells fileManager:(SDLFileManager *)fileManager; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index a7d8e2fb0..21e2de6fa 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -67,21 +67,22 @@ @implementation SDLMenuReplaceUtilities return [mutableArtworks allObjects]; } -+ (BOOL)sdl_shouldCellIncludeImage:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager { ++ (BOOL)sdl_shouldCellIncludeImage:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image - return ((cell.icon != nil && [fileManager hasUploadedFile:cell.icon]) || cell.icon.isStaticIcon); + BOOL supportsImage = (cell.subCells.count > 0) ? [windowCapability hasImageFieldOfName:SDLImageFieldNameSubMenuIcon] : [windowCapability hasImageFieldOfName:SDLImageFieldNameMenuIcon]; + return cell.icon != nil && supportsImage && [fileManager fileNeedsUpload:cell.icon]; } -+ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingIndexesFrom:(NSArray *)menu availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { ++ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingIndexesFrom:(NSArray *)menu windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { NSMutableArray *mutableCommands = [NSMutableArray array]; for (NSUInteger menuInteger = 0; menuInteger < menu.count; menuInteger++) { for (NSUInteger updateCellsIndex = 0; updateCellsIndex < cells.count; updateCellsIndex++) { if ([menu[menuInteger] isEqual:cells[updateCellsIndex]]) { if (cells[updateCellsIndex].subCells.count > 0) { - [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[updateCellsIndex] fileManager:fileManager position:(UInt16)menuInteger availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; + [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[updateCellsIndex] fileManager:fileManager position:(UInt16)menuInteger windowCapability:windowCapability defaultSubmenuLayout:defaultSubmenuLayout]]; } else { - [mutableCommands addObject:[self sdl_commandForMenuCell:cells[updateCellsIndex] fileManager:fileManager position:(UInt16)menuInteger]]; + [mutableCommands addObject:[self sdl_commandForMenuCell:cells[updateCellsIndex] fileManager:fileManager windowCapability:windowCapability position:(UInt16)menuInteger]]; } } } @@ -90,33 +91,33 @@ + (BOOL)sdl_shouldCellIncludeImage:(SDLMenuCell *)cell fileManager:(SDLFileManag return [mutableCommands copy]; } -+ (NSArray *)subMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { ++ (NSArray *)subMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { NSMutableArray *mutableCommands = [NSMutableArray array]; for (SDLMenuCell *cell in cells) { if (cell.subCells.count > 0) { - [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cell.subCells fileManager:fileManager availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; + [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cell.subCells fileManager:fileManager windowCapability:windowCapability defaultSubmenuLayout:defaultSubmenuLayout]]; } } return [mutableCommands copy]; } -+ (NSArray *)sdl_allCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { ++ (NSArray *)sdl_allCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { NSMutableArray *mutableCommands = [NSMutableArray array]; for (NSUInteger cellIndex = 0; cellIndex < cells.count; cellIndex++) { if (cells[cellIndex].subCells.count > 0) { - [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[cellIndex] fileManager:fileManager position:(UInt16)cellIndex availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; - [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cells[cellIndex].subCells fileManager:fileManager availableMenuLayouts:availableMenuLayouts defaultSubmenuLayout:defaultSubmenuLayout]]; + [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[cellIndex] fileManager:fileManager position:(UInt16)cellIndex windowCapability:windowCapability defaultSubmenuLayout:defaultSubmenuLayout]]; + [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cells[cellIndex].subCells fileManager:fileManager windowCapability:windowCapability defaultSubmenuLayout:defaultSubmenuLayout]]; } else { - [mutableCommands addObject:[self sdl_commandForMenuCell:cells[cellIndex] fileManager:fileManager position:(UInt16)cellIndex]]; + [mutableCommands addObject:[self sdl_commandForMenuCell:cells[cellIndex] fileManager:fileManager windowCapability:windowCapability position:(UInt16)cellIndex]]; } } return [mutableCommands copy]; } -+ (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager position:(UInt16)position { ++ (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability position:(UInt16)position { SDLAddCommand *command = [[SDLAddCommand alloc] init]; SDLMenuParams *params = [[SDLMenuParams alloc] init]; @@ -126,17 +127,17 @@ + (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFi command.menuParams = params; command.vrCommands = (cell.voiceCommands.count == 0) ? nil : cell.voiceCommands; - command.cmdIcon = [self sdl_shouldCellIncludeImage:cell fileManager:fileManager] ? cell.icon.imageRPC : nil; + command.cmdIcon = [self sdl_shouldCellIncludeImage:cell fileManager:fileManager windowCapability:windowCapability] ? cell.icon.imageRPC : nil; command.cmdID = @(cell.cellId); return command; } -+ (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager position:(UInt16)position availableMenuLayouts:(NSArray *)availableMenuLayouts defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { - SDLImage *icon = [self sdl_shouldCellIncludeImage:cell fileManager:fileManager] ? cell.icon.imageRPC : nil; ++ (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager position:(UInt16)position windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { + SDLImage *icon = [self sdl_shouldCellIncludeImage:cell fileManager:fileManager windowCapability:windowCapability] ? cell.icon.imageRPC : nil; SDLMenuLayout submenuLayout = nil; - if (cell.submenuLayout && [availableMenuLayouts containsObject:cell.submenuLayout]) { + if (cell.submenuLayout && [windowCapability.menuLayoutsAvailable containsObject:cell.submenuLayout]) { submenuLayout = cell.submenuLayout; } else { submenuLayout = defaultSubmenuLayout; From 9385fb46ba54ac299171b322674f8db41818f259 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 27 Jan 2021 10:04:21 -0500 Subject: [PATCH 010/112] Menu operation updates * In progress changes to track and pass back current menu state --- Cartfile.private | 5 +- Cartfile.resolved | 5 +- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 2 + .../SDLMenuConfigurationUpdateOperation.m | 5 +- SmartDeviceLink/private/SDLMenuManager.m | 58 ++++++++++++++++++ .../private/SDLMenuReplaceDynamicOperation.h | 5 +- .../private/SDLMenuReplaceDynamicOperation.m | 7 +-- .../private/SDLMenuReplaceStaticOperation.h | 7 ++- .../private/SDLMenuReplaceStaticOperation.m | 59 +++++++++---------- .../private/SDLMenuReplaceUtilities.h | 2 + .../private/SDLMenuShowOperation.m | 5 +- 11 files changed, 105 insertions(+), 55 deletions(-) diff --git a/Cartfile.private b/Cartfile.private index 29600768d..6eb659334 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,4 +1,3 @@ github "Quick/Quick" ~> 3.0 -github "Quick/Nimble" == 9.0.0-rc.3 -github "erikdoe/ocmock" ~> 3.7 -github "uber/ios-snapshot-test-case" ~> 6.2 +github "Quick/Nimble" ~> 9.0 +github "erikdoe/ocmock" ~> 3.7 \ No newline at end of file diff --git a/Cartfile.resolved b/Cartfile.resolved index e8652705c..00e581205 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,3 @@ -github "Quick/Nimble" "v9.0.0-rc.3" +github "Quick/Nimble" "v9.0.0" github "Quick/Quick" "v3.0.0" -github "erikdoe/ocmock" "v3.7.1" -github "uber/ios-snapshot-test-case" "6.2.0" +github "erikdoe/ocmock" "v3.8.1" diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 0d45f7f98..393e2f283 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -9223,6 +9223,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + VALIDATE_WORKSPACE = NO; }; name = Debug; }; @@ -9255,6 +9256,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.smartdevicelink.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; + VALIDATE_WORKSPACE = NO; }; name = Release; }; diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m index c09069d91..666161d03 100644 --- a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m @@ -41,10 +41,7 @@ - (instancetype)initWithConnectionManager:(id)connecti - (void)start { [super start]; - if (self.isCancelled) { - [self finishOperation]; - return; - } + if (self.isCancelled) { return; } if ([[SDLGlobals sharedGlobals].rpcVersion isLessThanVersion:[SDLVersion versionWithMajor:6 minor:0 patch:0]]) { SDLLogW(@"Menu configurations is only supported on head units with RPC spec version 6.0.0 or later. Currently connected head unit RPC spec version is %@", [SDLGlobals sharedGlobals].rpcVersion); diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index cb8a5b840..5ba4475c0 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -27,6 +27,8 @@ #import "SDLMenuConfiguration.h" #import "SDLMenuConfigurationUpdateOperation.h" #import "SDLMenuParams.h" +#import "SDLMenuReplaceDynamicOperation.h" +#import "SDLMenuReplaceStaticOperation.h" #import "SDLMenuShowOperation.h" #import "SDLOnCommand.h" #import "SDLOnHMIStatus.h" @@ -257,11 +259,67 @@ - (BOOL)openMenu:(nullable SDLMenuCell *)cell { #pragma mark - Updating System - (void)sdl_startDynamicMenuUpdate { + __weak typeof(self) weakself = self; + SDLMenuReplaceDynamicOperation *menuReplaceOperation = [[SDLMenuReplaceDynamicOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells currentMenuUpdatedBlock:^(NSArray * _Nonnull currentMenuCells) { + weakself.currentMenuCells = currentMenuCells; + [weakself sdl_updateMenuReplaceOperationsWithNewCurrentMenu]; + }]; + __weak typeof(menuReplaceOperation) weakOp = menuReplaceOperation; + menuReplaceOperation.completionBlock = ^{ + if (weakOp.error != nil) { + SDLLogE(@"Updating menu dynamically failed with error: %@", weakOp.error); + } + }; + + // Cancel previous replace menu operations + for (NSOperation *operation in self.transactionQueue.operations) { + if ([operation isMemberOfClass:[SDLMenuReplaceStaticOperation class]] + || [operation isMemberOfClass:[SDLMenuReplaceDynamicOperation class]]) { + [operation cancel]; + } + } + + [self.transactionQueue addOperation:menuReplaceOperation]; } - (void)sdl_startStaticMenuUpdate { + __weak typeof(self) weakself = self; + SDLMenuReplaceStaticOperation *menuReplaceOperation = [[SDLMenuReplaceStaticOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells currentMenuUpdatedBlock:^(NSArray * _Nonnull currentMenuCells) { + weakself.currentMenuCells = currentMenuCells; + [weakself sdl_updateMenuReplaceOperationsWithNewCurrentMenu]; + }]; + + __weak typeof(menuReplaceOperation) weakOp = menuReplaceOperation; + menuReplaceOperation.completionBlock = ^{ + if (weakOp.error != nil) { + SDLLogE(@"Updating menu statically failed with error: %@", weakOp.error); + } + }; + // TODO: Update menu config, and window capability + + // Cancel previous replace menu operations + for (NSOperation *operation in self.transactionQueue.operations) { + if ([operation isMemberOfClass:[SDLMenuReplaceStaticOperation class]] + || [operation isMemberOfClass:[SDLMenuReplaceDynamicOperation class]]) { + [operation cancel]; + } + } + + [self.transactionQueue addOperation:menuReplaceOperation]; +} + +- (void)sdl_updateMenuReplaceOperationsWithNewCurrentMenu { + for (NSOperation *operation in self.transactionQueue.operations) { + if ([operation isMemberOfClass:[SDLMenuReplaceStaticOperation class]]) { + SDLMenuReplaceStaticOperation *op = (SDLMenuReplaceStaticOperation *)operation; + op.currentMenu = self.currentMenuCells; + } else if ([operation isMemberOfClass:[SDLMenuReplaceDynamicOperation class]]) { + SDLMenuReplaceDynamicOperation *op = (SDLMenuReplaceDynamicOperation *)operation; + op.currentMenu = self.currentMenuCells; + } + } } #pragma mark - Helpers diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h index 2a37d6c24..1bb2d2680 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h +++ b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h @@ -10,6 +10,8 @@ #import "SDLAsynchronousOperation.h" +#import "SDLMenuReplaceUtilities.h" + @protocol SDLConnectionManagerType; @class SDLFileManager; @@ -23,8 +25,9 @@ NS_ASSUME_NONNULL_BEGIN @property (strong, nonatomic) SDLWindowCapability *windowCapability; @property (strong, nonatomic) SDLMenuConfiguration *menuConfiguration; +@property (strong, nonatomic) NSArray *currentMenu; -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu; +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu currentMenuUpdatedBlock:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m index 526a2eddb..9023f3b56 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m @@ -17,7 +17,6 @@ #import "SDLLogMacros.h" #import "SDLMenuCell.h" #import "SDLMenuConfiguration.h" -#import "SDLMenuReplaceUtilities.h" #import "SDLTextFieldName.h" #import "SDLWindowCapability.h" #import "SDLWindowCapability+ScreenManagerExtensions.h" @@ -37,7 +36,6 @@ @interface SDLMenuReplaceDynamicOperation () @property (weak, nonatomic) id connectionManager; @property (weak, nonatomic) SDLFileManager *fileManager; -@property (strong, nonatomic) NSArray *currentMenu; @property (strong, nonatomic) NSArray *updatedMenu; @property (copy, nonatomic, nullable) NSError *internalError; @@ -62,10 +60,7 @@ - (instancetype)initWithConnectionManager:(id)connecti - (void)start { [super start]; - if (self.isCancelled) { - [self finishOperation]; - return; - } + if (self.isCancelled) { return; } SDLDynamicMenuUpdateRunScore *runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; NSArray *deleteMenuStatus = runScore.oldStatus; diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h index 25895f7c5..3e0fb1368 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h @@ -6,10 +6,10 @@ // Copyright © 2021 smartdevicelink. All rights reserved. // -#import - #import "SDLAsynchronousOperation.h" +#import "SDLMenuReplaceUtilities.h" + @protocol SDLConnectionManagerType; @class SDLFileManager; @@ -23,8 +23,9 @@ NS_ASSUME_NONNULL_BEGIN @property (strong, nonatomic) SDLWindowCapability *windowCapability; @property (strong, nonatomic) SDLMenuConfiguration *menuConfiguration; +@property (strong, nonatomic) NSArray *currentMenu; -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu; +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu currentMenuUpdatedBlock:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index 4cc98fe5b..cc93ae474 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -15,7 +15,6 @@ #import "SDLLogMacros.h" #import "SDLMenuCell.h" #import "SDLMenuConfiguration.h" -#import "SDLMenuReplaceUtilities.h" #import "SDLTextFieldName.h" #import "SDLWindowCapability.h" #import "SDLWindowCapability+ScreenManagerExtensions.h" @@ -28,8 +27,8 @@ @interface SDLMenuReplaceStaticOperation () @property (weak, nonatomic) id connectionManager; @property (weak, nonatomic) SDLFileManager *fileManager; -@property (strong, nonatomic) NSArray *currentMenu; @property (strong, nonatomic) NSArray *updatedMenu; +@property (assign, nonatomic) SDLCurrentMenuUpdatedBlock currentMenuUpdatedBlock; @property (copy, nonatomic, nullable) NSError *internalError; @@ -37,7 +36,7 @@ @interface SDLMenuReplaceStaticOperation () @implementation SDLMenuReplaceStaticOperation -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu { +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu currentMenuUpdatedBlock:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock { self = [super init]; if (!self) { return nil; } @@ -47,16 +46,14 @@ - (instancetype)initWithConnectionManager:(id)connecti _menuConfiguration = menuConfiguration; _currentMenu = currentMenu; _updatedMenu = updatedMenu; + _currentMenuUpdatedBlock = currentMenuUpdatedBlock; return self; } - (void)start { [super start]; - if (self.isCancelled) { - [self finishOperation]; - return; - } + if (self.isCancelled) { return; } __weak typeof(self) weakself = self; NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; @@ -99,6 +96,28 @@ - (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells ce }]; } +- (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { + if (deleteMenuCells.count == 0) { + return completionHandler(nil); + } + + NSArray *deleteMenuCommands = [SDLMenuReplaceUtilities deleteCommandsForCells:deleteMenuCells]; + __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; + [self.connectionManager sendRequests:deleteMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { + if (error != nil) { + errors[request] = error; + } + } completionHandler:^(BOOL success) { + if (!success) { + SDLLogE(@"Unable to delete all old menu commands with errors: %@", errors); + completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); + } else { + SDLLogD(@"Finished deleting old menu"); + completionHandler(nil); + } + }]; +} + - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { if (self.updatedMenu.count == 0 || newMenuCells.count == 0) { SDLLogD(@"There are no cells to update."); @@ -114,6 +133,8 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti [self.connectionManager sendRequests:mainMenuCommands progressHandler:^void(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { if (error != nil) { errors[request] = error; + } else { +// [weakSelf.currentMenu ] } } completionHandler:^(BOOL success) { if (!success) { @@ -143,30 +164,6 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti }]; } -#pragma mark Delete Old Menu Items - -- (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { - if (deleteMenuCells.count == 0) { - return completionHandler(nil); - } - - NSArray *deleteMenuCommands = [SDLMenuReplaceUtilities deleteCommandsForCells:deleteMenuCells]; - __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; - [self.connectionManager sendRequests:deleteMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { - if (error != nil) { - errors[request] = error; - } - } completionHandler:^(BOOL success) { - if (!success) { - SDLLogE(@"Unable to delete all old menu commands with errors: %@", errors); - completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); - } else { - SDLLogD(@"Finished deleting old menu"); - completionHandler(nil); - } - }]; -} - #pragma mark - Operation Overrides - (void)finishOperation { diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index 1fa70f570..0c0fa84ae 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -18,6 +18,8 @@ NS_ASSUME_NONNULL_BEGIN +typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCells); + @interface SDLMenuReplaceUtilities : NSObject + (NSArray *)deleteCommandsForCells:(NSArray *)cells; diff --git a/SmartDeviceLink/private/SDLMenuShowOperation.m b/SmartDeviceLink/private/SDLMenuShowOperation.m index e1344a4e7..cc6672b47 100644 --- a/SmartDeviceLink/private/SDLMenuShowOperation.m +++ b/SmartDeviceLink/private/SDLMenuShowOperation.m @@ -49,10 +49,7 @@ - (instancetype)initWithConnectionManager:(id)connecti - (void)start { [super start]; - if (self.isCancelled) { - [self finishOperation]; - return; - } + if (self.isCancelled) { return; } SDLShowAppMenu *openMenu = nil; if (self.submenuCell != nil) { From d67ed414465ab780a4838d0cd88f8cd3caf9cb81 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 27 Jan 2021 13:02:32 -0500 Subject: [PATCH 011/112] Fix checking if image needs upload --- SmartDeviceLink/private/SDLMenuReplaceUtilities.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 21e2de6fa..877d6631e 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -70,7 +70,7 @@ @implementation SDLMenuReplaceUtilities + (BOOL)sdl_shouldCellIncludeImage:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image BOOL supportsImage = (cell.subCells.count > 0) ? [windowCapability hasImageFieldOfName:SDLImageFieldNameSubMenuIcon] : [windowCapability hasImageFieldOfName:SDLImageFieldNameMenuIcon]; - return cell.icon != nil && supportsImage && [fileManager fileNeedsUpload:cell.icon]; + return cell.icon != nil && supportsImage && ([fileManager hasUploadedFile:cell.icon] || cell.icon.isStaticIcon); } + (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingIndexesFrom:(NSArray *)menu windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { From 2cb4ccbb17d305acdcf848ee4e051f7c37f3442f Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 27 Jan 2021 14:25:53 -0500 Subject: [PATCH 012/112] Add removing deleted cells from static operation --- .../private/SDLMenuReplaceStaticOperation.m | 40 +++++++++++++++++-- .../private/SDLMenuReplaceUtilities.h | 31 +++++++++++++- .../private/SDLMenuReplaceUtilities.m | 20 ++++++++++ .../private/SDLVoiceCommandUpdateOperation.m | 5 +-- 4 files changed, 87 insertions(+), 9 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index cc93ae474..ef28f76ab 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -8,8 +8,12 @@ #import "SDLMenuReplaceStaticOperation.h" +#import "SDLAddCommand.h" +#import "SDLAddSubmenu.h" #import "SDLArtwork.h" #import "SDLConnectionManagerType.h" +#import "SDLDeleteCommand.h" +#import "SDLDeleteSubMenu.h" #import "SDLError.h" #import "SDLFileManager.h" #import "SDLLogMacros.h" @@ -23,6 +27,14 @@ typedef void(^SDLMenuUpdateCompletionHandler)(NSError *__nullable error); +@interface SDLMenuCell() + +@property (assign, nonatomic) UInt32 parentCellId; +@property (assign, nonatomic) UInt32 cellId; +@property (copy, nonatomic, readwrite, nullable) NSArray *subCells; + +@end + @interface SDLMenuReplaceStaticOperation () @property (weak, nonatomic) id connectionManager; @@ -30,6 +42,7 @@ @interface SDLMenuReplaceStaticOperation () @property (strong, nonatomic) NSArray *updatedMenu; @property (assign, nonatomic) SDLCurrentMenuUpdatedBlock currentMenuUpdatedBlock; +@property (strong, nonatomic) NSMutableArray *mutableCurrentMenu; @property (copy, nonatomic, nullable) NSError *internalError; @end @@ -44,7 +57,7 @@ - (instancetype)initWithConnectionManager:(id)connecti _fileManager = fileManager; _windowCapability = windowCapability; _menuConfiguration = menuConfiguration; - _currentMenu = currentMenu; + _mutableCurrentMenu = [currentMenu mutableCopy]; _updatedMenu = updatedMenu; _currentMenuUpdatedBlock = currentMenuUpdatedBlock; @@ -101,11 +114,21 @@ - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuC return completionHandler(nil); } + __weak typeof(self) weakself = self; NSArray *deleteMenuCommands = [SDLMenuReplaceUtilities deleteCommandsForCells:deleteMenuCells]; __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; [self.connectionManager sendRequests:deleteMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { if (error != nil) { errors[request] = error; + } else { + // Find the id of the successful request and remove it from the current menu list whereever it may have been + UInt32 commandId = 0; + if ([request isMemberOfClass:[SDLDeleteCommand class]]) { + commandId = ((SDLDeleteCommand *)request).cmdID.unsignedLongValue; + } else if ([request isMemberOfClass:[SDLDeleteSubMenu class]]) { + commandId = ((SDLDeleteSubMenu *)request).menuID.unsignedLongValue; + } + [SDLMenuReplaceUtilities removeMenuCellFromList:self.mutableCurrentMenu withCmdId:commandId]; } } completionHandler:^(BOOL success) { if (!success) { @@ -121,8 +144,7 @@ - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuC - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { if (self.updatedMenu.count == 0 || newMenuCells.count == 0) { SDLLogD(@"There are no cells to update."); - completionHandler(nil); - return; + return completionHandler(nil); } NSArray *mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells fileManager:self.fileManager usingIndexesFrom:self.updatedMenu windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; @@ -134,7 +156,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti if (error != nil) { errors[request] = error; } else { -// [weakSelf.currentMenu ] + } } completionHandler:^(BOOL success) { if (!success) { @@ -164,6 +186,16 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti }]; } +#pragma mark - Getter / Setters + +- (void)setCurrentMenu:(NSArray *)currentMenu { + _mutableCurrentMenu = [currentMenu mutableCopy]; +} + +- (NSArray *)currentMenu { + return [_mutableCurrentMenu copy]; +} + #pragma mark - Operation Overrides - (void)finishOperation { diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index 0c0fa84ae..6fdaac53d 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -22,12 +22,41 @@ typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCel @interface SDLMenuReplaceUtilities : NSObject +#pragma mark - Artworks + +/// Finds all artworks that need to be uploaded from the given list of menu cells +/// @param cells The cells to check for artwork +/// @param fileManager The file manager to check if artworks need upload +/// @param windowCapability The window capability to check available image fields ++ (NSArray *)findAllArtworksToBeUploadedFromCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability; + +#pragma mark - RPC Commands + +/// Generate SDLDeleteCommand and SDLDeleteSubMenu RPCs for the given cells +/// @param cells The cells for which to generate delete RPCs + (NSArray *)deleteCommandsForCells:(NSArray *)cells; +/// Generate SDLAddCommand and SDLAddSubMenu RPCs for given main menu cells +/// @param cells The cells to generate AddCommand / AddSubMenu RPCs for +/// @param fileManager The file manager to use to check availability of artworks +/// @param menu The menu from which we will manage indexes +/// @param windowCapability The window capability with which to check available text fields / image fields +/// @param defaultSubmenuLayout The default submenu layout to use for displaying submenus + (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingIndexesFrom:(NSArray *)menu windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; + +/// Generate SDLAddCommand and SDLAddSubMenu RPCs for the given submenu cells +/// @param cells The cells to generate AddCommand / AddSubMenu RPCs for +/// @param fileManager The file manager to use to check availability of artworks +/// @param windowCapability The window capability with which to check available text fields / image fields +/// @param defaultSubmenuLayout The default submenu layout to use for displaying submenus + (NSArray *)subMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; -+ (NSArray *)findAllArtworksToBeUploadedFromCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability; +#pragma mark - Updating Menu Cells + +/// Find the menu cell given a command id and remove it from the list, then return the new list +/// @param menuCellList The list to mutate and remove the item from +/// @param commandId The id of the cell to find and remove ++ (nullable NSMutableArray *)removeMenuCellFromList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 877d6631e..519184873 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -73,6 +73,8 @@ + (BOOL)sdl_shouldCellIncludeImage:(SDLMenuCell *)cell fileManager:(SDLFileManag return cell.icon != nil && supportsImage && ([fileManager hasUploadedFile:cell.icon] || cell.icon.isStaticIcon); } +#pragma mark - RPC Commands + + (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingIndexesFrom:(NSArray *)menu windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { NSMutableArray *mutableCommands = [NSMutableArray array]; @@ -146,4 +148,22 @@ + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager return [[SDLAddSubMenu alloc] initWithMenuID:cell.cellId menuName:cell.title position:@(position) menuIcon:icon menuLayout:submenuLayout parentID:nil]; } +#pragma mark - Updating Menu Cells + ++ (nullable NSMutableArray *)removeMenuCellFromList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId { + for (SDLMenuCell *menuCell in menuCellList) { + if (menuCell.cellId == commandId) { + [menuCellList removeObject:menuCell]; + return menuCellList; + } else if (menuCell.subCells.count > 0) { + NSMutableArray *newList = [self sdl_removeMenuCellFromList:[menuCell.subCells mutableCopy] withCmdId:commandId]; + if (newList != nil) { + menuCell.subCells = [newList copy]; + } + } + } + + return nil; +} + @end diff --git a/SmartDeviceLink/private/SDLVoiceCommandUpdateOperation.m b/SmartDeviceLink/private/SDLVoiceCommandUpdateOperation.m index 184615d63..a000e890a 100644 --- a/SmartDeviceLink/private/SDLVoiceCommandUpdateOperation.m +++ b/SmartDeviceLink/private/SDLVoiceCommandUpdateOperation.m @@ -59,10 +59,7 @@ - (void)start { __weak typeof(self) weakSelf = self; [self sdl_sendDeleteCurrentVoiceCommands:^{ // If the operation has been canceled, then don't send the new commands and finish the operation - if (self.isCancelled) { - [weakSelf finishOperation]; - return; - } + if (self.isCancelled) { return [weakSelf finishOperation]; } // Send the new commands [weakSelf sdl_sendCurrentVoiceCommands:^{ From e9a1abff3b954e8477c4391ad2b323f6a9351313 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 27 Jan 2021 15:17:08 -0500 Subject: [PATCH 013/112] Fixes --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 42 +++++++++++-------- SmartDeviceLink/private/SDLMenuManager.m | 2 +- .../private/SDLMenuManagerPrivateConstants.h | 15 +++++++ .../private/SDLMenuManagerPrivateConstants.m | 15 +++++++ .../private/SDLMenuReplaceStaticOperation.m | 8 ++-- .../private/SDLMenuReplaceUtilities.m | 6 ++- 6 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 SmartDeviceLink/private/SDLMenuManagerPrivateConstants.h create mode 100644 SmartDeviceLink/private/SDLMenuManagerPrivateConstants.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 393e2f283..75c113bdf 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -521,6 +521,8 @@ 4A93895A25B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93895825B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m */; }; 4A93896625BB361C0069F438 /* SDLMenuReplaceUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93896425BB361C0069F438 /* SDLMenuReplaceUtilities.h */; }; 4A93896725BB361C0069F438 /* SDLMenuReplaceUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */; }; + 4AAC0DBA25C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AAC0DB825C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h */; }; + 4AAC0DBB25C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAC0DB925C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m */; }; 4ABB24BA24F592620061BF55 /* NSMutableArray+Safe.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ABB24B224F592620061BF55 /* NSMutableArray+Safe.h */; }; 4ABB24BB24F592620061BF55 /* NSMutableArray+Safe.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABB24B324F592620061BF55 /* NSMutableArray+Safe.m */; }; 4ABB24BC24F592620061BF55 /* NSBundle+SDLBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABB24B424F592620061BF55 /* NSBundle+SDLBundle.m */; }; @@ -2317,6 +2319,8 @@ 4A93895825B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuConfigurationUpdateOperation.m; path = private/SDLMenuConfigurationUpdateOperation.m; sourceTree = ""; }; 4A93896425BB361C0069F438 /* SDLMenuReplaceUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuReplaceUtilities.h; path = private/SDLMenuReplaceUtilities.h; sourceTree = ""; }; 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceUtilities.m; path = private/SDLMenuReplaceUtilities.m; sourceTree = ""; }; + 4AAC0DB825C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuManagerPrivateConstants.h; path = private/SDLMenuManagerPrivateConstants.h; sourceTree = ""; }; + 4AAC0DB925C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuManagerPrivateConstants.m; path = private/SDLMenuManagerPrivateConstants.m; sourceTree = ""; }; 4ABB24B224F592620061BF55 /* NSMutableArray+Safe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSMutableArray+Safe.h"; path = "private/NSMutableArray+Safe.h"; sourceTree = ""; }; 4ABB24B324F592620061BF55 /* NSMutableArray+Safe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSMutableArray+Safe.m"; path = "private/NSMutableArray+Safe.m"; sourceTree = ""; }; 4ABB24B424F592620061BF55 /* NSBundle+SDLBundle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSBundle+SDLBundle.m"; path = "private/NSBundle+SDLBundle.m"; sourceTree = ""; }; @@ -4176,19 +4180,6 @@ name = Operations; sourceTree = ""; }; - 4A32B3F325559F37001FFA26 /* Menu */ = { - isa = PBXGroup; - children = ( - 4A93893A25B8CB480069F438 /* Operations */, - 5D76751022D907F500E8D71A /* Configuration */, - 755F175E229F14F70041B9CB /* Dynamic Menu Update Utilities */, - 5D339CEC207C08AB000CC364 /* Cells */, - 4ABB25A924F7E6E10061BF55 /* SDLMenuManager.h */, - 4ABB25A624F7E6E10061BF55 /* SDLMenuManager.m */, - ); - name = Menu; - sourceTree = ""; - }; 4A3BA4D9248E8EBB003E56B8 /* SystemRequest Handler */ = { isa = PBXGroup; children = ( @@ -4266,6 +4257,8 @@ 4A93893A25B8CB480069F438 /* Operations */ = { isa = PBXGroup; children = ( + 755F175E229F14F70041B9CB /* Dynamic Menu Update Utilities */, + 4A93896325BB35FA0069F438 /* Menu Replace Utilities */, 4A93895725B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h */, 4A93895825B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m */, 4A93893B25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.h */, @@ -4274,7 +4267,6 @@ 4A93894425B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m */, 4A93895125B9DACA0069F438 /* SDLMenuShowOperation.h */, 4A93895225B9DACA0069F438 /* SDLMenuShowOperation.m */, - 4A93896325BB35FA0069F438 /* Menu Replace Utilities */, ); name = Operations; sourceTree = ""; @@ -4307,6 +4299,16 @@ name = "Status Manager"; sourceTree = ""; }; + 4AAC0DB725C1FE9700746D33 /* Constants */ = { + isa = PBXGroup; + children = ( + 4ABB259824F7E6A60061BF55 /* SDLMenuManagerConstants.h */, + 4AAC0DB825C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h */, + 4AAC0DB925C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m */, + ); + name = Constants; + sourceTree = ""; + }; 4AD1F16A2559952D00637FE1 /* Voice Command */ = { isa = PBXGroup; children = ( @@ -4362,6 +4364,7 @@ 5D0A737F203F23D10001595D /* Soft Button */, 88A098AB2476F08F00A50005 /* Subscribe Button */, 5D0A737D203F23B30001595D /* Text and Graphic */, + 4A32B3E425559D93001FFA26 /* Voice Commands */, 5D1BF6AA2047429C00D36881 /* Utilities */, 4ABB25DA24F7E77C0061BF55 /* SDLScreenManager.h */, 4ABB25DB24F7E77C0061BF55 /* SDLScreenManager.m */, @@ -4488,8 +4491,12 @@ 5D339CE5207C0651000CC364 /* Menu */ = { isa = PBXGroup; children = ( - 4A32B3F325559F37001FFA26 /* Menu */, - 4A32B3E425559D93001FFA26 /* Voice Commands */, + 4AAC0DB725C1FE9700746D33 /* Constants */, + 5D339CEC207C08AB000CC364 /* Cells */, + 5D76751022D907F500E8D71A /* Configuration */, + 4A93893A25B8CB480069F438 /* Operations */, + 4ABB25A924F7E6E10061BF55 /* SDLMenuManager.h */, + 4ABB25A624F7E6E10061BF55 /* SDLMenuManager.m */, ); name = Menu; sourceTree = ""; @@ -5812,7 +5819,6 @@ children = ( 4ABB259B24F7E6B90061BF55 /* SDLMenuConfiguration.h */, 4ABB259A24F7E6B90061BF55 /* SDLMenuConfiguration.m */, - 4ABB259824F7E6A60061BF55 /* SDLMenuManagerConstants.h */, ); name = Configuration; sourceTree = ""; @@ -7416,6 +7422,7 @@ 4ABB250524F596450061BF55 /* SDLListFilesOperation.h in Headers */, 4ABB268F24F7F8FC0061BF55 /* SDLHexUtility.h in Headers */, 4A8BD28124F9343F000945E3 /* SDLRemoteControlCapabilities.h in Headers */, + 4AAC0DBA25C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h in Headers */, 4ABB26DD24F7FAFD0061BF55 /* SDLRPCResponse.h in Headers */, 4ABB2AAE24F847F40061BF55 /* SDLSendLocationResponse.h in Headers */, 4A8BD36B24F94636000945E3 /* SDLProtocolMessageDisassembler.h in Headers */, @@ -7815,6 +7822,7 @@ 4ABB250924F596920061BF55 /* SDLFileWrapper.m in Sources */, 4ABB24E324F5948D0061BF55 /* SDLEncryptionConfiguration.m in Sources */, 4ABB273D24F7FD1D0061BF55 /* SDLDimension.m in Sources */, + 4AAC0DBB25C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m in Sources */, 4ABB267B24F7F6840061BF55 /* SDLIconArchiveFile.m in Sources */, 4ABB2A2224F847980061BF55 /* SDLChangeRegistrationResponse.m in Sources */, 4A8BD3A524F9485B000945E3 /* SDLVideoStreamingState.m in Sources */, diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 5ba4475c0..60c806ff8 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -26,6 +26,7 @@ #import "SDLMenuCell.h" #import "SDLMenuConfiguration.h" #import "SDLMenuConfigurationUpdateOperation.h" +#import "SDLMenuManagerPrivateConstants.h" #import "SDLMenuParams.h" #import "SDLMenuReplaceDynamicOperation.h" #import "SDLMenuReplaceStaticOperation.h" @@ -76,7 +77,6 @@ @interface SDLMenuManager() @end -UInt32 const ParentIdNotFound = UINT32_MAX; UInt32 const MenuCellIdMin = 1; @implementation SDLMenuManager diff --git a/SmartDeviceLink/private/SDLMenuManagerPrivateConstants.h b/SmartDeviceLink/private/SDLMenuManagerPrivateConstants.h new file mode 100644 index 000000000..63f7333ee --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuManagerPrivateConstants.h @@ -0,0 +1,15 @@ +// +// SDLMenuManagerPrivateConstants.h +// SmartDeviceLink +// +// Created by Joel Fischer on 1/27/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +extern UInt32 const ParentIdNotFound; + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuManagerPrivateConstants.m b/SmartDeviceLink/private/SDLMenuManagerPrivateConstants.m new file mode 100644 index 000000000..03c7349e8 --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuManagerPrivateConstants.m @@ -0,0 +1,15 @@ +// +// SDLMenuManagerPrivateConstants.m +// SmartDeviceLink +// +// Created by Joel Fischer on 1/27/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import "SDLMenuManagerPrivateConstants.h" + +NS_ASSUME_NONNULL_BEGIN + +UInt32 const ParentIdNotFound = UINT32_MAX; + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index ef28f76ab..ebe355a6b 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -31,7 +31,6 @@ @interface SDLMenuCell() @property (assign, nonatomic) UInt32 parentCellId; @property (assign, nonatomic) UInt32 cellId; -@property (copy, nonatomic, readwrite, nullable) NSArray *subCells; @end @@ -114,7 +113,6 @@ - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuC return completionHandler(nil); } - __weak typeof(self) weakself = self; NSArray *deleteMenuCommands = [SDLMenuReplaceUtilities deleteCommandsForCells:deleteMenuCells]; __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; [self.connectionManager sendRequests:deleteMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { @@ -124,9 +122,9 @@ - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuC // Find the id of the successful request and remove it from the current menu list whereever it may have been UInt32 commandId = 0; if ([request isMemberOfClass:[SDLDeleteCommand class]]) { - commandId = ((SDLDeleteCommand *)request).cmdID.unsignedLongValue; + commandId = ((SDLDeleteCommand *)request).cmdID.unsignedIntValue; } else if ([request isMemberOfClass:[SDLDeleteSubMenu class]]) { - commandId = ((SDLDeleteSubMenu *)request).menuID.unsignedLongValue; + commandId = ((SDLDeleteSubMenu *)request).menuID.unsignedIntValue; } [SDLMenuReplaceUtilities removeMenuCellFromList:self.mutableCurrentMenu withCmdId:commandId]; } @@ -156,7 +154,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti if (error != nil) { errors[request] = error; } else { - + // TODO: Add to the current menu list } } completionHandler:^(BOOL success) { if (!success) { diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 519184873..5b770277a 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -18,6 +18,7 @@ #import "SDLImageFieldName.h" #import "SDLMenuCell.h" #import "SDLMenuParams.h" +#import "SDLMenuManagerPrivateConstants.h" #import "SDLRPCRequest.h" #import "SDLWindowCapability.h" #import "SDLWindowCapability+ScreenManagerExtensions.h" @@ -26,6 +27,7 @@ @interface SDLMenuCell() @property (assign, nonatomic) UInt32 parentCellId; @property (assign, nonatomic) UInt32 cellId; +@property (copy, nonatomic, readwrite, nullable) NSArray *subCells; @end @@ -124,7 +126,7 @@ + (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFi SDLMenuParams *params = [[SDLMenuParams alloc] init]; params.menuName = cell.title; - params.parentID = cell.parentCellId != UINT32_MAX ? @(cell.parentCellId) : nil; + params.parentID = (cell.parentCellId != ParentIdNotFound) ? @(cell.parentCellId) : nil; params.position = @(position); command.menuParams = params; @@ -156,7 +158,7 @@ + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager [menuCellList removeObject:menuCell]; return menuCellList; } else if (menuCell.subCells.count > 0) { - NSMutableArray *newList = [self sdl_removeMenuCellFromList:[menuCell.subCells mutableCopy] withCmdId:commandId]; + NSMutableArray *newList = [self removeMenuCellFromList:[menuCell.subCells mutableCopy] withCmdId:commandId]; if (newList != nil) { menuCell.subCells = [newList copy]; } From 8a6389dc08dcd269142755f218de2be2c4c66c21 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 27 Jan 2021 16:12:52 -0500 Subject: [PATCH 014/112] Working on adding cells to current menu list --- .../private/SDLMenuReplaceStaticOperation.m | 21 +++--- .../private/SDLMenuReplaceUtilities.h | 10 ++- .../private/SDLMenuReplaceUtilities.m | 70 +++++++++++++------ 3 files changed, 68 insertions(+), 33 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index ebe355a6b..c66d92be5 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -120,13 +120,8 @@ - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuC errors[request] = error; } else { // Find the id of the successful request and remove it from the current menu list whereever it may have been - UInt32 commandId = 0; - if ([request isMemberOfClass:[SDLDeleteCommand class]]) { - commandId = ((SDLDeleteCommand *)request).cmdID.unsignedIntValue; - } else if ([request isMemberOfClass:[SDLDeleteSubMenu class]]) { - commandId = ((SDLDeleteSubMenu *)request).menuID.unsignedIntValue; - } - [SDLMenuReplaceUtilities removeMenuCellFromList:self.mutableCurrentMenu withCmdId:commandId]; + UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; + [SDLMenuReplaceUtilities removeMenuCellFromCurrentMainMenuList:self.mutableCurrentMenu withCmdId:commandId]; } } completionHandler:^(BOOL success) { if (!success) { @@ -151,25 +146,27 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; __weak typeof(self) weakSelf = self; [self.connectionManager sendRequests:mainMenuCommands progressHandler:^void(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { + SDLLogV(@"Updating main menu commands, percent complete: %.01f", percentComplete); if (error != nil) { errors[request] = error; } else { // TODO: Add to the current menu list + // Find the id of the successful request and add it from the current menu list whereever it needs to be + UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; } } completionHandler:^(BOOL success) { if (!success) { SDLLogE(@"Failed to send main menu commands: %@", errors); - completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); - return; + return completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); } - if (self.isCancelled) { - return [weakSelf finishOperation]; - } + if (self.isCancelled) { return [weakSelf finishOperation]; } [weakSelf.connectionManager sendRequests:subMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { if (error != nil) { errors[request] = error; + } else { + // TODO: Add to the current menu list } } completionHandler:^(BOOL success) { if (!success) { diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index 6fdaac53d..1fafd0eea 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -32,6 +32,8 @@ typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCel #pragma mark - RPC Commands ++ (UInt32)commandIdForRPCRequest:(SDLRPCRequest *)request; + /// Generate SDLDeleteCommand and SDLDeleteSubMenu RPCs for the given cells /// @param cells The cells for which to generate delete RPCs + (NSArray *)deleteCommandsForCells:(NSArray *)cells; @@ -56,7 +58,13 @@ typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCel /// Find the menu cell given a command id and remove it from the list, then return the new list /// @param menuCellList The list to mutate and remove the item from /// @param commandId The id of the cell to find and remove -+ (nullable NSMutableArray *)removeMenuCellFromList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId; ++ (nullable NSMutableArray *)removeMenuCellFromCurrentMainMenuList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId; + +/// Add this menu cell to the current cells menu at the given location +/// @param cell The cell to add +/// @param menuCellList The mutable list of main menu cells. The cell to be added may be in a submenu of this list +/// @param position The position to put the item in in whichever list it needs to be ++ (NSMutableArray *)addMenuCell:(SDLMenuCell *)cell toCurrentMainMenuList:(NSMutableArray *)menuCellList atPosition:(UInt16)position; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 5b770277a..1cebaab94 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -33,23 +33,6 @@ @interface SDLMenuCell() @implementation SDLMenuReplaceUtilities -#pragma mark Delete Commands - -+ (NSArray *)deleteCommandsForCells:(NSArray *)cells { - NSMutableArray *mutableDeletes = [NSMutableArray array]; - for (SDLMenuCell *cell in cells) { - if (cell.subCells == nil) { - SDLDeleteCommand *delete = [[SDLDeleteCommand alloc] initWithId:cell.cellId]; - [mutableDeletes addObject:delete]; - } else { - SDLDeleteSubMenu *delete = [[SDLDeleteSubMenu alloc] initWithId:cell.cellId]; - [mutableDeletes addObject:delete]; - } - } - - return [mutableDeletes copy]; -} - #pragma mark Artworks + (NSArray *)findAllArtworksToBeUploadedFromCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { @@ -77,9 +60,38 @@ + (BOOL)sdl_shouldCellIncludeImage:(SDLMenuCell *)cell fileManager:(SDLFileManag #pragma mark - RPC Commands ++ (UInt32)commandIdForRPCRequest:(SDLRPCRequest *)request { + UInt32 commandId = 0; + if ([request isMemberOfClass:[SDLAddCommand class]]) { + commandId = ((SDLAddSubMenu *)request).cmdID.unsignedIntValue; + } else if ([request isMemberOfClass:[SDLAddCommand class]]) { + commandId = ((SDLAddSubMenu *)request).menuID.unsignedIntValue; + } else if ([request isMemberOfClass:[SDLDeleteCommand class]]) { + commandId = ((SDLDeleteCommand *)request).cmdID.unsignedIntValue; + } else if ([request isMemberOfClass:[SDLDeleteSubMenu class]]) { + commandId = ((SDLDeleteSubMenu *)request).menuID.unsignedIntValue; + } + + return commandId; +} + ++ (NSArray *)deleteCommandsForCells:(NSArray *)cells { + NSMutableArray *mutableDeletes = [NSMutableArray array]; + for (SDLMenuCell *cell in cells) { + if (cell.subCells.count == 0) { + SDLDeleteCommand *delete = [[SDLDeleteCommand alloc] initWithId:cell.cellId]; + [mutableDeletes addObject:delete]; + } else { + SDLDeleteSubMenu *delete = [[SDLDeleteSubMenu alloc] initWithId:cell.cellId]; + [mutableDeletes addObject:delete]; + } + } + + return [mutableDeletes copy]; +} + + (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingIndexesFrom:(NSArray *)menu windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { NSMutableArray *mutableCommands = [NSMutableArray array]; - for (NSUInteger menuInteger = 0; menuInteger < menu.count; menuInteger++) { for (NSUInteger updateCellsIndex = 0; updateCellsIndex < cells.count; updateCellsIndex++) { if ([menu[menuInteger] isEqual:cells[updateCellsIndex]]) { @@ -106,6 +118,8 @@ + (BOOL)sdl_shouldCellIncludeImage:(SDLMenuCell *)cell fileManager:(SDLFileManag return [mutableCommands copy]; } +#pragma mark Private Helpers + + (NSArray *)sdl_allCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { NSMutableArray *mutableCommands = [NSMutableArray array]; @@ -152,13 +166,14 @@ + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager #pragma mark - Updating Menu Cells -+ (nullable NSMutableArray *)removeMenuCellFromList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId { +#pragma mark Remove Cell ++ (nullable NSMutableArray *)removeMenuCellFromCurrentMainMenuList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId { for (SDLMenuCell *menuCell in menuCellList) { if (menuCell.cellId == commandId) { [menuCellList removeObject:menuCell]; return menuCellList; } else if (menuCell.subCells.count > 0) { - NSMutableArray *newList = [self removeMenuCellFromList:[menuCell.subCells mutableCopy] withCmdId:commandId]; + NSMutableArray *newList = [self removeMenuCellFromCurrentMainMenuList:[menuCell.subCells mutableCopy] withCmdId:commandId]; if (newList != nil) { menuCell.subCells = [newList copy]; } @@ -168,4 +183,19 @@ + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager return nil; } +#pragma mark Inserting Cell ++ (NSMutableArray *)addMenuCell:(SDLMenuCell *)cell toCurrentMainMenuList:(NSMutableArray *)menuCellList atPosition:(UInt16)position { + // If the cell has a parent id, it needs to go into a submenu + + // Otherwise it's in the main menu and goes at the given position +} + ++ (void)sdl_insertMenuCell:(SDLMenuCell *)cell intoList:(NSMutableArray *)cellList atPosition:(UInt16)position { + if (position > cellList.count) { + [cellList addObject:cell]; + } else { + [cellList insertObject:cell atIndex:position]; + } +} + @end From 92516dc4ce0d13589beabf5aee53ef4cd6bb2b00 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 29 Jan 2021 10:36:44 -0500 Subject: [PATCH 015/112] Finish untested setup of static operation --- .../private/SDLMenuReplaceStaticOperation.m | 32 +++++++++-- .../private/SDLMenuReplaceUtilities.h | 5 +- .../private/SDLMenuReplaceUtilities.m | 57 +++++++++++++++---- 3 files changed, 78 insertions(+), 16 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index c66d92be5..773488f07 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -121,7 +121,7 @@ - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuC } else { // Find the id of the successful request and remove it from the current menu list whereever it may have been UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; - [SDLMenuReplaceUtilities removeMenuCellFromCurrentMainMenuList:self.mutableCurrentMenu withCmdId:commandId]; + [SDLMenuReplaceUtilities removeMenuCellFromList:self.mutableCurrentMenu withCmdId:commandId]; } } completionHandler:^(BOOL success) { if (!success) { @@ -146,13 +146,24 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; __weak typeof(self) weakSelf = self; [self.connectionManager sendRequests:mainMenuCommands progressHandler:^void(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { - SDLLogV(@"Updating main menu commands, percent complete: %.01f", percentComplete); + SDLLogV(@"Updating main menu commands, percent complete: %.01f", percentComplete * 100); if (error != nil) { errors[request] = error; } else { - // TODO: Add to the current menu list // Find the id of the successful request and add it from the current menu list whereever it needs to be UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; + UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; + SDLMenuCell *addedCell = nil; + for (SDLMenuCell *cell in newMenuCells) { + if (cell.cellId == commandId) { + addedCell = cell; + break; + } + } + + if (addedCell != nil) { + [SDLMenuReplaceUtilities addMenuCell:addedCell toList:weakSelf.mutableCurrentMenu atPosition:position]; + } } } completionHandler:^(BOOL success) { if (!success) { @@ -166,7 +177,20 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti if (error != nil) { errors[request] = error; } else { - // TODO: Add to the current menu list + // Find the id of the successful request and add it from the current menu list whereever it needs to be + UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; + UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; + SDLMenuCell *addedCell = nil; + for (SDLMenuCell *cell in newMenuCells) { + if (cell.cellId == commandId) { + addedCell = cell; + break; + } + } + + if (addedCell != nil) { + [SDLMenuReplaceUtilities addMenuCell:addedCell toList:weakSelf.mutableCurrentMenu atPosition:position]; + } } } completionHandler:^(BOOL success) { if (!success) { diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index 1fafd0eea..74b5dac9f 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -33,6 +33,7 @@ typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCel #pragma mark - RPC Commands + (UInt32)commandIdForRPCRequest:(SDLRPCRequest *)request; ++ (UInt16)positionForRPCRequest:(SDLRPCRequest *)request; /// Generate SDLDeleteCommand and SDLDeleteSubMenu RPCs for the given cells /// @param cells The cells for which to generate delete RPCs @@ -58,13 +59,13 @@ typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCel /// Find the menu cell given a command id and remove it from the list, then return the new list /// @param menuCellList The list to mutate and remove the item from /// @param commandId The id of the cell to find and remove -+ (nullable NSMutableArray *)removeMenuCellFromCurrentMainMenuList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId; ++ (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId; /// Add this menu cell to the current cells menu at the given location /// @param cell The cell to add /// @param menuCellList The mutable list of main menu cells. The cell to be added may be in a submenu of this list /// @param position The position to put the item in in whichever list it needs to be -+ (NSMutableArray *)addMenuCell:(SDLMenuCell *)cell toCurrentMainMenuList:(NSMutableArray *)menuCellList atPosition:(UInt16)position; ++ (BOOL)addMenuCell:(SDLMenuCell *)cell toList:(NSMutableArray *)menuCellList atPosition:(UInt16)position; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 1cebaab94..af959f16b 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -63,8 +63,8 @@ + (BOOL)sdl_shouldCellIncludeImage:(SDLMenuCell *)cell fileManager:(SDLFileManag + (UInt32)commandIdForRPCRequest:(SDLRPCRequest *)request { UInt32 commandId = 0; if ([request isMemberOfClass:[SDLAddCommand class]]) { - commandId = ((SDLAddSubMenu *)request).cmdID.unsignedIntValue; - } else if ([request isMemberOfClass:[SDLAddCommand class]]) { + commandId = ((SDLAddCommand *)request).cmdID.unsignedIntValue; + } else if ([request isMemberOfClass:[SDLAddSubMenu class]]) { commandId = ((SDLAddSubMenu *)request).menuID.unsignedIntValue; } else if ([request isMemberOfClass:[SDLDeleteCommand class]]) { commandId = ((SDLDeleteCommand *)request).cmdID.unsignedIntValue; @@ -75,6 +75,17 @@ + (UInt32)commandIdForRPCRequest:(SDLRPCRequest *)request { return commandId; } ++ (UInt16)positionForRPCRequest:(SDLRPCRequest *)request { + UInt16 position = 0; + if ([request isMemberOfClass:[SDLAddCommand class]]) { + position = ((SDLAddCommand *)request).menuParams.position.unsignedShortValue; + } else if ([request isMemberOfClass:[SDLAddSubMenu class]]) { + position = ((SDLAddSubMenu *)request).position.unsignedShortValue; + } + + return position; +} + + (NSArray *)deleteCommandsForCells:(NSArray *)cells { NSMutableArray *mutableDeletes = [NSMutableArray array]; for (SDLMenuCell *cell in cells) { @@ -167,27 +178,53 @@ + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager #pragma mark - Updating Menu Cells #pragma mark Remove Cell -+ (nullable NSMutableArray *)removeMenuCellFromCurrentMainMenuList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId { ++ (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId { for (SDLMenuCell *menuCell in menuCellList) { if (menuCell.cellId == commandId) { + // If the cell id matches the command id, remove it from the list and return [menuCellList removeObject:menuCell]; - return menuCellList; + return YES; } else if (menuCell.subCells.count > 0) { - NSMutableArray *newList = [self removeMenuCellFromCurrentMainMenuList:[menuCell.subCells mutableCopy] withCmdId:commandId]; - if (newList != nil) { + // If the menu cell has subcells, we need to recurse and check the subcells + NSMutableArray *newList = [menuCell.subCells mutableCopy]; + BOOL foundAndRemovedItem = [self removeMenuCellFromList:newList withCmdId:commandId]; + if (foundAndRemovedItem) { menuCell.subCells = [newList copy]; + return YES; } } } - return nil; + return NO; } #pragma mark Inserting Cell -+ (NSMutableArray *)addMenuCell:(SDLMenuCell *)cell toCurrentMainMenuList:(NSMutableArray *)menuCellList atPosition:(UInt16)position { - // If the cell has a parent id, it needs to go into a submenu ++ (BOOL)addMenuCell:(SDLMenuCell *)cell toList:(NSMutableArray *)menuCellList atPosition:(UInt16)position { + if (cell.parentCellId != ParentIdNotFound) { + // If the cell has a parent id, we need to find the cell with a matching cell id and insert it into its submenu + for (SDLMenuCell *menuCell in menuCellList) { + if (menuCell.cellId == cell.parentCellId) { + // If we found the correct submenu, insert it into that submenu + NSMutableArray *newList = [menuCell.subCells mutableCopy]; + [self sdl_insertMenuCell:cell intoList:newList atPosition:position]; + return YES; + } else if (menuCell.subCells.count > 0) { + // Check the subcells of this cell to see if any of those have cell ids that match the parent cell id + NSMutableArray *newList = [menuCell.subCells mutableCopy]; + BOOL foundAndAddedItem = [self addMenuCell:cell toList:newList atPosition:position]; + if (foundAndAddedItem) { + menuCell.subCells = [newList copy]; + return YES; + } + } + } + } else { + // The cell does not have a parent id, just insert it into the main menu + [self sdl_insertMenuCell:cell intoList:menuCellList atPosition:position]; + return YES; + } - // Otherwise it's in the main menu and goes at the given position + return NO; } + (void)sdl_insertMenuCell:(SDLMenuCell *)cell intoList:(NSMutableArray *)cellList atPosition:(UInt16)position { From b05c7a71c3c6eb0970f36da7dfd6db4e5950c177 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 29 Jan 2021 10:50:30 -0500 Subject: [PATCH 016/112] Simplify static operation code duplication --- .../private/SDLMenuReplaceStaticOperation.m | 32 ++++--------------- .../private/SDLMenuReplaceUtilities.h | 6 +--- .../private/SDLMenuReplaceUtilities.m | 22 ++++++++++++- 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index 773488f07..dde9ccfcf 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -151,19 +151,9 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti errors[request] = error; } else { // Find the id of the successful request and add it from the current menu list whereever it needs to be - UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; - UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; - SDLMenuCell *addedCell = nil; - for (SDLMenuCell *cell in newMenuCells) { - if (cell.cellId == commandId) { - addedCell = cell; - break; - } - } - - if (addedCell != nil) { - [SDLMenuReplaceUtilities addMenuCell:addedCell toList:weakSelf.mutableCurrentMenu atPosition:position]; - } + UInt32 commandId = [self commandIdForRPCRequest:request]; + UInt16 position = [self positionForRPCRequest:request]; + [SDLMenuReplaceUtilities addMenuRequestWithCommandId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; } } completionHandler:^(BOOL success) { if (!success) { @@ -178,19 +168,9 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti errors[request] = error; } else { // Find the id of the successful request and add it from the current menu list whereever it needs to be - UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; - UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; - SDLMenuCell *addedCell = nil; - for (SDLMenuCell *cell in newMenuCells) { - if (cell.cellId == commandId) { - addedCell = cell; - break; - } - } - - if (addedCell != nil) { - [SDLMenuReplaceUtilities addMenuCell:addedCell toList:weakSelf.mutableCurrentMenu atPosition:position]; - } + UInt32 commandId = [self commandIdForRPCRequest:request]; + UInt16 position = [self positionForRPCRequest:request]; + [SDLMenuReplaceUtilities addMenuRequestWithCommandId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; } } completionHandler:^(BOOL success) { if (!success) { diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index 74b5dac9f..755398f11 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -61,11 +61,7 @@ typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCel /// @param commandId The id of the cell to find and remove + (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId; -/// Add this menu cell to the current cells menu at the given location -/// @param cell The cell to add -/// @param menuCellList The mutable list of main menu cells. The cell to be added may be in a submenu of this list -/// @param position The position to put the item in in whichever list it needs to be -+ (BOOL)addMenuCell:(SDLMenuCell *)cell toList:(NSMutableArray *)menuCellList atPosition:(UInt16)position; ++ (BOOL)addMenuRequestWithCommandId:(UInt32)commandId position:(UInt16)position fromNewMenuList:(NSArray *)newMenuList toMainMenuList:(NSMutableArray *)mainMenuList; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index af959f16b..d79579591 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -199,7 +199,27 @@ + (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList wit } #pragma mark Inserting Cell -+ (BOOL)addMenuCell:(SDLMenuCell *)cell toList:(NSMutableArray *)menuCellList atPosition:(UInt16)position { ++ (BOOL)addMenuRequestWithCommandId:(UInt32)commandId position:(UInt16)position fromNewMenuList:(NSArray *)newMenuList toMainMenuList:(NSMutableArray *)mainMenuList { + SDLMenuCell *addedCell = nil; + for (SDLMenuCell *cell in newMenuList) { + if (cell.cellId == commandId) { + addedCell = cell; + break; + } else if (cell.subCells.count > 0) { + BOOL didAddCell = [self addMenuRequestWithCommandId:commandId position:position fromNewMenuList:cell.subCells toMainMenuList:mainMenuList]; + if (didAddCell) { return YES; } + } + } + + if (addedCell != nil) { + [self addMenuCell:addedCell toList:mainMenuList atPosition:position]; + return YES; + } + + return NO; +} + ++ (BOOL)sdl_addMenuCell:(SDLMenuCell *)cell toList:(NSMutableArray *)menuCellList atPosition:(UInt16)position { if (cell.parentCellId != ParentIdNotFound) { // If the cell has a parent id, we need to find the cell with a matching cell id and insert it into its submenu for (SDLMenuCell *menuCell in menuCellList) { From 0387d10135580cd4d519c7c5237da62fddb08b13 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 29 Jan 2021 11:18:25 -0500 Subject: [PATCH 017/112] Add some comments, fix some comments, stub a test class --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 8 ++++++-- .../private/SDLMenuReplaceDynamicOperation.m | 10 +++++----- .../private/SDLMenuReplaceStaticOperation.m | 4 ++-- SmartDeviceLink/private/SDLMenuReplaceUtilities.h | 14 +++++++++++++- .../DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m | 12 ++++++++++++ 5 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 210dfbbf5..d9950cf7f 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -523,6 +523,7 @@ 4A93896725BB361C0069F438 /* SDLMenuReplaceUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */; }; 4AAC0DBA25C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AAC0DB825C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h */; }; 4AAC0DBB25C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAC0DB925C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m */; }; + 4AAC0DE025C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAC0DDF25C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m */; }; 4ABB24BA24F592620061BF55 /* NSMutableArray+Safe.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ABB24B224F592620061BF55 /* NSMutableArray+Safe.h */; }; 4ABB24BB24F592620061BF55 /* NSMutableArray+Safe.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABB24B324F592620061BF55 /* NSMutableArray+Safe.m */; }; 4ABB24BC24F592620061BF55 /* NSBundle+SDLBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABB24B424F592620061BF55 /* NSBundle+SDLBundle.m */; }; @@ -2329,6 +2330,7 @@ 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceUtilities.m; path = private/SDLMenuReplaceUtilities.m; sourceTree = ""; }; 4AAC0DB825C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuManagerPrivateConstants.h; path = private/SDLMenuManagerPrivateConstants.h; sourceTree = ""; }; 4AAC0DB925C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuManagerPrivateConstants.m; path = private/SDLMenuManagerPrivateConstants.m; sourceTree = ""; }; + 4AAC0DDF25C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceUtilitiesSpec.m; path = DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m; sourceTree = ""; }; 4ABB24B224F592620061BF55 /* NSMutableArray+Safe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSMutableArray+Safe.h"; path = "private/NSMutableArray+Safe.h"; sourceTree = ""; }; 4ABB24B324F592620061BF55 /* NSMutableArray+Safe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSMutableArray+Safe.m"; path = "private/NSMutableArray+Safe.m"; sourceTree = ""; }; 4ABB24B424F592620061BF55 /* NSBundle+SDLBundle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSBundle+SDLBundle.m"; path = "private/NSBundle+SDLBundle.m"; sourceTree = ""; }; @@ -6577,11 +6579,12 @@ 5DF40B24208FA7C500DD6FDA /* Menu */ = { isa = PBXGroup; children = ( - 5DF40B25208FA7DE00DD6FDA /* SDLMenuManagerSpec.m */, 5DAB5F502098994C00A020C8 /* SDLMenuCellSpec.m */, + 5D76751522D920FD00E8D71A /* SDLMenuConfigurationSpec.m */, + 5DF40B25208FA7DE00DD6FDA /* SDLMenuManagerSpec.m */, + 4AAC0DDF25C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m */, 752ECDB8228C42E100D945F4 /* SDLMenuRunScoreSpec.m */, 752ECDBA228C532600D945F4 /* SDLMenuUpdateAlgorithmSpec.m */, - 5D76751522D920FD00E8D71A /* SDLMenuConfigurationSpec.m */, ); name = Menu; sourceTree = ""; @@ -8716,6 +8719,7 @@ 162E82F91A9BDE8B00906325 /* SDLSamplingRateSpec.m in Sources */, 5DBEFA541F434B9E009EE295 /* SDLStreamingMediaConfigurationSpec.m in Sources */, 1EB59CD2202DCA9B00343A61 /* SDLMassageModeSpec.m in Sources */, + 4AAC0DE025C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m in Sources */, 9FA0D00C22DF0B65009CF344 /* SDLCreateWindowResponseSpec.m in Sources */, 162E82CB1A9BDE8A00906325 /* SDLAppHMITypeSpec.m in Sources */, EEB2537E2067D3E80069584E /* SDLSecondaryTransportManagerSpec.m in Sources */, diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m index 9023f3b56..f1e6a4981 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m @@ -73,7 +73,7 @@ - (void)start { NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:addMenuStatus]; // Since we are creating a new Menu but keeping old cells we must firt transfer the old cellIDs to the new menus kept cells. - [self transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; + [self sdl_transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; // TODO: We don't check cancellation or finish NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; @@ -179,17 +179,17 @@ - (void)sdl_startSubMenuUpdatesWithOldKeptCells:(NSArray *)oldKep NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; - [self transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; + [self sdl_transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { [weakself sdl_sendNewMenuCells:cellsToAdd oldMenu:weakself.currentMenu[startIndex].subCells withCompletionHandler:^(NSError * _Nullable error) { - // After the first set of submenu cells were added and deleted we must find the next set of subcells untll we loop through all the elemetns + // After the first set of submenu cells were added and deleted we must find the next set of subcells until we loop through all the elements [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; }]; }]; } else { - // After the first set of submenu cells were added and deleted we must find the next set of subcells untll we loop through all the elemetns + // After the first set of submenu cells were added and deleted we must find the next set of subcells until we loop through all the elements [self sdl_startSubMenuUpdatesWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; } } @@ -239,7 +239,7 @@ - (void)sdl_startSubMenuUpdatesWithOldKeptCells:(NSArray *)oldKep return [keepMenuCells copy]; } -- (void)transferCellIDFromOldCells:(NSArray *)oldCells toKeptCells:(NSArray *)newCells { +- (void)sdl_transferCellIDFromOldCells:(NSArray *)oldCells toKeptCells:(NSArray *)newCells { if (oldCells.count == 0) { return; } for (NSUInteger i = 0; i < newCells.count; i++) { newCells[i].cellId = oldCells[i].cellId; diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index dde9ccfcf..f6d472a95 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -157,7 +157,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti } } completionHandler:^(BOOL success) { if (!success) { - SDLLogE(@"Failed to send main menu commands: %@", errors); + SDLLogE(@"Failed to send one or more main menu commands: %@", errors); return completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); } @@ -174,7 +174,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti } } completionHandler:^(BOOL success) { if (!success) { - SDLLogE(@"Failed to send sub menu commands: %@", errors); + SDLLogE(@"Failed to send one or more sub menu commands: %@", errors); completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); return; } diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index 755398f11..f58ec3aa6 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -32,7 +32,12 @@ typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCel #pragma mark - RPC Commands +/// Finds and returns the command id for a given RPC request, assuming that request is an SDLDeleteSubMenu, SDLDeleteCommand, SDLAddSubMenu, or SDLAddCommand +/// @param request The request + (UInt32)commandIdForRPCRequest:(SDLRPCRequest *)request; + +/// Finds and returns the position for a given RPC request, assuming that request is an SDLAddSubMenu, or SDLAddCommand +/// @param request The request + (UInt16)positionForRPCRequest:(SDLRPCRequest *)request; /// Generate SDLDeleteCommand and SDLDeleteSubMenu RPCs for the given cells @@ -56,11 +61,18 @@ typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCel #pragma mark - Updating Menu Cells -/// Find the menu cell given a command id and remove it from the list, then return the new list +/// Find the menu cell given a command id and remove it from the list (or a cell in the list's subcell list, etc.) /// @param menuCellList The list to mutate and remove the item from /// @param commandId The id of the cell to find and remove +/// @return YES if the cell was found and removed successfully, NO if it was not + (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId; +/// Finds a menu cell from newMenuList with the given commandId and inserts it into the main menu list (or a subcell list) at the given position +/// @param commandId The command id for the cell to be found +/// @param position The position to insert the cell into the appropriate list for it to be in +/// @param newMenuList The complete requested new menu list. We will find the cell to insert from this list. +/// @param mainMenuList The mutable main menu list. The place to insert the cell will be in this list or one of its cell's subcell list (or one of it's cell's subcell's subcell's list, etc.) +/// @return YES if the cell was added successfully, NO if the cell was not + (BOOL)addMenuRequestWithCommandId:(UInt32)commandId position:(UInt16)position fromNewMenuList:(NSArray *)newMenuList toMainMenuList:(NSMutableArray *)mainMenuList; @end diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m new file mode 100644 index 000000000..e51de7374 --- /dev/null +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m @@ -0,0 +1,12 @@ +#import +#import + +#import "SDLMenuReplaceUtilities.h" + +QuickSpecBegin(SDLMenuReplaceUtilitiesSpec) + +describe(@"a menu configuration", ^{ + +}); + +QuickSpecEnd From 2c7c99b677b5675f7c32c6142aac3822f8bc6943 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 29 Jan 2021 11:19:46 -0500 Subject: [PATCH 018/112] Fix not building --- SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index f6d472a95..f8182f7d9 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -151,8 +151,8 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti errors[request] = error; } else { // Find the id of the successful request and add it from the current menu list whereever it needs to be - UInt32 commandId = [self commandIdForRPCRequest:request]; - UInt16 position = [self positionForRPCRequest:request]; + UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; + UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; [SDLMenuReplaceUtilities addMenuRequestWithCommandId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; } } completionHandler:^(BOOL success) { @@ -168,8 +168,8 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompleti errors[request] = error; } else { // Find the id of the successful request and add it from the current menu list whereever it needs to be - UInt32 commandId = [self commandIdForRPCRequest:request]; - UInt16 position = [self positionForRPCRequest:request]; + UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; + UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; [SDLMenuReplaceUtilities addMenuRequestWithCommandId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; } } completionHandler:^(BOOL success) { From 12d413ca784b5d468d14b647c6f9f5def78788db Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 29 Jan 2021 11:29:22 -0500 Subject: [PATCH 019/112] Fix still not compiling code --- SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m | 2 +- SmartDeviceLink/private/SDLMenuReplaceUtilities.m | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m index f1e6a4981..8b17e9b17 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m @@ -196,7 +196,7 @@ - (void)sdl_startSubMenuUpdatesWithOldKeptCells:(NSArray *)oldKep - (NSArray *)sdl_filterDeleteMenuItemsWithOldMenuItems:(NSArray *)oldMenuCells basedOnStatusList:(NSArray *)oldStatusList { NSMutableArray *deleteCells = [[NSMutableArray alloc] init]; - // The index of the status should corrleate 1-1 with the number of items in the menu + // The index of the status should correlate 1-1 with the number of items in the menu // [2,0,2,0] => [A,B,C,D] = [B,D] for (NSUInteger index = 0; index < oldStatusList.count; index++) { if (oldStatusList[index].integerValue == MenuCellStateDelete) { diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index d79579591..20f181d2a 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -212,7 +212,7 @@ + (BOOL)addMenuRequestWithCommandId:(UInt32)commandId position:(UInt16)position } if (addedCell != nil) { - [self addMenuCell:addedCell toList:mainMenuList atPosition:position]; + [self sdl_addMenuCell:addedCell toList:mainMenuList atPosition:position]; return YES; } @@ -231,7 +231,7 @@ + (BOOL)sdl_addMenuCell:(SDLMenuCell *)cell toList:(NSMutableArray 0) { // Check the subcells of this cell to see if any of those have cell ids that match the parent cell id NSMutableArray *newList = [menuCell.subCells mutableCopy]; - BOOL foundAndAddedItem = [self addMenuCell:cell toList:newList atPosition:position]; + BOOL foundAndAddedItem = [self sdl_addMenuCell:cell toList:newList atPosition:position]; if (foundAndAddedItem) { menuCell.subCells = [newList copy]; return YES; From a118ee5d1c561770086bb7a02dd79ff01da15070 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 29 Jan 2021 14:30:36 -0500 Subject: [PATCH 020/112] Working on menu replace utils tests --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 14 +++++ .../private/SDLMenuReplaceStaticOperation.m | 9 ++-- .../DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m | 45 +++++++++++++++- .../SDLMenuReplaceUtilitiesSpecHelpers.h | 22 ++++++++ .../SDLMenuReplaceUtilitiesSpecHelpers.m | 52 +++++++++++++++++++ 5 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h create mode 100644 SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index d9950cf7f..559cbf740 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -524,6 +524,7 @@ 4AAC0DBA25C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AAC0DB825C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h */; }; 4AAC0DBB25C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAC0DB925C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m */; }; 4AAC0DE025C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAC0DDF25C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m */; }; + 4AAC0DE825C493EE00746D33 /* SDLMenuReplaceUtilitiesSpecHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAC0DE725C493EE00746D33 /* SDLMenuReplaceUtilitiesSpecHelpers.m */; }; 4ABB24BA24F592620061BF55 /* NSMutableArray+Safe.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ABB24B224F592620061BF55 /* NSMutableArray+Safe.h */; }; 4ABB24BB24F592620061BF55 /* NSMutableArray+Safe.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABB24B324F592620061BF55 /* NSMutableArray+Safe.m */; }; 4ABB24BC24F592620061BF55 /* NSBundle+SDLBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABB24B424F592620061BF55 /* NSBundle+SDLBundle.m */; }; @@ -2331,6 +2332,8 @@ 4AAC0DB825C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuManagerPrivateConstants.h; path = private/SDLMenuManagerPrivateConstants.h; sourceTree = ""; }; 4AAC0DB925C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuManagerPrivateConstants.m; path = private/SDLMenuManagerPrivateConstants.m; sourceTree = ""; }; 4AAC0DDF25C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceUtilitiesSpec.m; path = DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m; sourceTree = ""; }; + 4AAC0DE625C493EE00746D33 /* SDLMenuReplaceUtilitiesSpecHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuReplaceUtilitiesSpecHelpers.h; path = DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h; sourceTree = ""; }; + 4AAC0DE725C493EE00746D33 /* SDLMenuReplaceUtilitiesSpecHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceUtilitiesSpecHelpers.m; path = DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m; sourceTree = ""; }; 4ABB24B224F592620061BF55 /* NSMutableArray+Safe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSMutableArray+Safe.h"; path = "private/NSMutableArray+Safe.h"; sourceTree = ""; }; 4ABB24B324F592620061BF55 /* NSMutableArray+Safe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSMutableArray+Safe.m"; path = "private/NSMutableArray+Safe.m"; sourceTree = ""; }; 4ABB24B424F592620061BF55 /* NSBundle+SDLBundle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSBundle+SDLBundle.m"; path = "private/NSBundle+SDLBundle.m"; sourceTree = ""; }; @@ -4331,6 +4334,15 @@ name = Constants; sourceTree = ""; }; + 4AAC0DE525C493D800746D33 /* Helpers */ = { + isa = PBXGroup; + children = ( + 4AAC0DE625C493EE00746D33 /* SDLMenuReplaceUtilitiesSpecHelpers.h */, + 4AAC0DE725C493EE00746D33 /* SDLMenuReplaceUtilitiesSpecHelpers.m */, + ); + name = Helpers; + sourceTree = ""; + }; 4AD1F16A2559952D00637FE1 /* Voice Command */ = { isa = PBXGroup; children = ( @@ -6579,6 +6591,7 @@ 5DF40B24208FA7C500DD6FDA /* Menu */ = { isa = PBXGroup; children = ( + 4AAC0DE525C493D800746D33 /* Helpers */, 5DAB5F502098994C00A020C8 /* SDLMenuCellSpec.m */, 5D76751522D920FD00E8D71A /* SDLMenuConfigurationSpec.m */, 5DF40B25208FA7DE00DD6FDA /* SDLMenuManagerSpec.m */, @@ -8540,6 +8553,7 @@ 162E83811A9BDE8B00906325 /* SDLImageFieldSpec.m in Sources */, 5D60DF24202B7A80001EDA01 /* SDLAsynchronousRPCRequestOperationSpec.m in Sources */, 883C22CB222EEF0900939C4C /* SDLRPCFunctionNamesSpec.m in Sources */, + 4AAC0DE825C493EE00746D33 /* SDLMenuReplaceUtilitiesSpecHelpers.m in Sources */, 162E834F1A9BDE8B00906325 /* SDLDeleteCommandResponseSpec.m in Sources */, 88B848C91F462E3600DED768 /* TestFileProgressResponse.m in Sources */, 162E83231A9BDE8B00906325 /* SDLAddSubMenuSpec.m in Sources */, diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m index f8182f7d9..2f75c7a13 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m @@ -78,9 +78,8 @@ - (void)start { return YES; } completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { - if (error != nil) { - SDLLogE(@"Error uploading menu artworks: %@", error); - } + if (weakself.isCancelled) { return; } + if (error != nil) { SDLLogE(@"Error uploading menu artworks: %@", error); } SDLLogD(@"All menu artworks uploaded"); [self sdl_updateMenuWithCellsToDelete:self.currentMenu cellsToAdd:self.updatedMenu]; @@ -98,9 +97,7 @@ - (void)start { - (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells { __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { - if (self.isCancelled) { - return [weakself finishOperation]; - } + if (self.isCancelled) { return [weakself finishOperation]; } [weakself sdl_sendNewMenuCells:addCells withCompletionHandler:^(NSError * _Nullable error) { [weakself finishOperation]; diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m index e51de7374..5b12083b0 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m @@ -1,12 +1,55 @@ #import #import +#import #import "SDLMenuReplaceUtilities.h" +#import "SDLFileManager.h" +#import "SDLMenuCell.h" +#import "SDLMenuReplaceUtilitiesSpecHelpers.h" +#import "SDLWindowCapability.h" +#import "TestConnectionManager.h" + +@interface SDLMenuCell() + +@property (assign, nonatomic) UInt32 parentCellId; +@property (assign, nonatomic) UInt32 cellId; +@property (copy, nonatomic, readwrite, nullable) NSArray *subCells; + +@end + QuickSpecBegin(SDLMenuReplaceUtilitiesSpec) -describe(@"a menu configuration", ^{ +describe(@"finding all artworks from cells", ^{ + __block NSArray *testMenuCells = nil; + __block SDLFileManager *mockFileManager = nil; + __block SDLWindowCapability *testWindowCapability = nil; + + beforeEach(^{ + mockFileManager = OCMClassMock([SDLFileManager class]); + }); +}); + +describe(@"generating RPCs", ^{ + +}); + +// updating menu cells +describe(@"updating menu cells", ^{ + __block NSArray *testNewMenuCells = nil; + __block UInt32 testCommandId = 0; + + // removeMenuCellFromList:withCmdId: + describe(@"removeMenuCellFromList:withCmdId:", ^{ + context(@"the list only has one level", ^{ + + }); + }); + describe(@"addMenuRequestWithCommandId:position:fromNewMenuList:toMainMenuList:", ^{ + __block NSMutableArray *testMenuCells = nil; + __block UInt16 testPosition = 0; + }); }); QuickSpecEnd diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h new file mode 100644 index 000000000..ebe68f865 --- /dev/null +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h @@ -0,0 +1,22 @@ +// +// SDLMenuReplaceUtilitiesSpecHelpers.h +// SmartDeviceLinkTests +// +// Created by Joel Fischer on 1/29/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import + +@class SDLMenuCell; + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLMenuReplaceUtilitiesSpecHelpers : NSObject + +@property (nonatomic, readonly) NSArray *topLevelOnlyMenu; +@property (nonatomic, readonly) NSArray *deepMenu; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m new file mode 100644 index 000000000..7537dec28 --- /dev/null +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m @@ -0,0 +1,52 @@ +// +// SDLMenuReplaceUtilitiesSpecHelpers.m +// SmartDeviceLinkTests +// +// Created by Joel Fischer on 1/29/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import "SDLMenuReplaceUtilitiesSpecHelpers.h" + +#import "SDLArtwork.h" +#import "SDLMenuCell.h" + +@implementation SDLMenuReplaceUtilitiesSpecHelpers + ++ (NSArray *)topLevelMenuOnly { + NSData *cellArtData = [@"testart" dataUsingEncoding:NSUTF8StringEncoding]; + NSData *cellArtData2 = [@"testart2" dataUsingEncoding:NSUTF8StringEncoding]; + return @[ + [[SDLMenuCell alloc] initWithTitle:@"Item 1" icon:[[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO] voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], + [[SDLMenuCell alloc] initWithTitle:@"Item 2" icon:[[SDLArtwork alloc] initWithData:cellArtData2 name:@"Test Art 1" fileExtension:@"png" persistent:NO] voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], + [[SDLMenuCell alloc] initWithTitle:@"Item 3" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] + ]; +} + ++ (NSArray *)deepMenu { + NSData *cellArtData = [@"testart" dataUsingEncoding:NSUTF8StringEncoding]; + NSData *cellArtData2 = [@"testart2" dataUsingEncoding:NSUTF8StringEncoding]; + + NSArray *subList1SubList1SubList1 = @[ + ]; + + NSArray *subList1SubList1SubList2 = @[ + ]; + + NSArray *subList1SubList1 = @[ + ]; + + NSArray *subList1 = @[ + ]; + + NSArray *subList2 = @[ + ]; + + return @[ + [[SDLMenuCell alloc] initWithTitle:@"Item 1" icon:[[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO] submenuLayout:nil subCells:subList1], + [[SDLMenuCell alloc] initWithTitle:@"Item 2" icon:[[SDLArtwork alloc] initWithData:cellArtData2 name:@"Test Art 1" fileExtension:@"png" persistent:NO] voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], + [[SDLMenuCell alloc] initWithTitle:@"Item 3" icon:nil submenuLayout:nil subCells:subList2] + ]; +} + +@end From c5e19898940e440fef0e4ea5cda45eab92d94d50 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 2 Feb 2021 16:05:06 -0500 Subject: [PATCH 021/112] Start shifting menu operation stuff all to dynamic --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 16 ++--- SmartDeviceLink/private/SDLMenuManager.m | 10 +-- ...cOperation.h => SDLMenuReplaceOperation.h} | 6 +- ...cOperation.m => SDLMenuReplaceOperation.m} | 64 ++++++++++++++----- 4 files changed, 65 insertions(+), 31 deletions(-) rename SmartDeviceLink/private/{SDLMenuReplaceDynamicOperation.h => SDLMenuReplaceOperation.h} (79%) rename SmartDeviceLink/private/{SDLMenuReplaceDynamicOperation.m => SDLMenuReplaceOperation.m} (82%) diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 559cbf740..0708118b0 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -511,8 +511,8 @@ 4A8BD3CD24F999BE000945E3 /* TestSubscribeButtonObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8BD3CC24F999BE000945E3 /* TestSubscribeButtonObserver.m */; }; 4A8BD3D024FE7CF1000945E3 /* SDLPermissionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8BD3CE24FE7CF1000945E3 /* SDLPermissionManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4A8BD3D124FE7CF1000945E3 /* SDLPermissionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8BD3CF24FE7CF1000945E3 /* SDLPermissionManager.m */; }; - 4A93893D25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93893B25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.h */; }; - 4A93893E25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93893C25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.m */; }; + 4A93893D25B8CBD40069F438 /* SDLMenuReplaceOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93893B25B8CBD40069F438 /* SDLMenuReplaceOperation.h */; }; + 4A93893E25B8CBD40069F438 /* SDLMenuReplaceOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93893C25B8CBD40069F438 /* SDLMenuReplaceOperation.m */; }; 4A93894525B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93894325B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h */; }; 4A93894625B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93894425B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m */; }; 4A93895325B9DACA0069F438 /* SDLMenuShowOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93895125B9DACA0069F438 /* SDLMenuShowOperation.h */; }; @@ -2319,8 +2319,8 @@ 4A8BD3CC24F999BE000945E3 /* TestSubscribeButtonObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestSubscribeButtonObserver.m; sourceTree = ""; }; 4A8BD3CE24FE7CF1000945E3 /* SDLPermissionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLPermissionManager.h; path = public/SDLPermissionManager.h; sourceTree = ""; }; 4A8BD3CF24FE7CF1000945E3 /* SDLPermissionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLPermissionManager.m; path = public/SDLPermissionManager.m; sourceTree = ""; }; - 4A93893B25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuReplaceDynamicOperation.h; path = private/SDLMenuReplaceDynamicOperation.h; sourceTree = ""; }; - 4A93893C25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceDynamicOperation.m; path = private/SDLMenuReplaceDynamicOperation.m; sourceTree = ""; }; + 4A93893B25B8CBD40069F438 /* SDLMenuReplaceOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuReplaceOperation.h; path = private/SDLMenuReplaceOperation.h; sourceTree = ""; }; + 4A93893C25B8CBD40069F438 /* SDLMenuReplaceOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceOperation.m; path = private/SDLMenuReplaceOperation.m; sourceTree = ""; }; 4A93894325B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuReplaceStaticOperation.h; path = private/SDLMenuReplaceStaticOperation.h; sourceTree = ""; }; 4A93894425B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceStaticOperation.m; path = private/SDLMenuReplaceStaticOperation.m; sourceTree = ""; }; 4A93895125B9DACA0069F438 /* SDLMenuShowOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuShowOperation.h; path = private/SDLMenuShowOperation.h; sourceTree = ""; }; @@ -4286,8 +4286,8 @@ 4A93896325BB35FA0069F438 /* Menu Replace Utilities */, 4A93895725B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h */, 4A93895825B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m */, - 4A93893B25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.h */, - 4A93893C25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.m */, + 4A93893B25B8CBD40069F438 /* SDLMenuReplaceOperation.h */, + 4A93893C25B8CBD40069F438 /* SDLMenuReplaceOperation.m */, 4A93894325B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h */, 4A93894425B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m */, 4A93895125B9DACA0069F438 /* SDLMenuShowOperation.h */, @@ -7116,7 +7116,7 @@ 4ABB2AA724F847F40061BF55 /* SDLShowConstantTBTResponse.h in Headers */, 4ABB28ED24F82A6A0061BF55 /* SDLOnKeyboardInput.h in Headers */, 5D9FDA991F2A7D3F00A495C8 /* emhashmap.h in Headers */, - 4A93893D25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.h in Headers */, + 4A93893D25B8CBD40069F438 /* SDLMenuReplaceOperation.h in Headers */, 4ABB255F24F7E59E0061BF55 /* SDLPermissionConstants.h in Headers */, 4ABB270324F7FB8F0061BF55 /* SDLButtonName.h in Headers */, 4ABB25B324F7E6F60061BF55 /* SDLSoftButtonTransitionOperation.h in Headers */, @@ -8175,7 +8175,7 @@ 4ABB28C924F82A6A0061BF55 /* SDLOnRCStatus.m in Sources */, 4ABB253124F7E43A0061BF55 /* SDLAsynchronousRPCOperation.m in Sources */, 4ABB2A7524F847D40061BF55 /* SDLPublishAppServiceResponse.m in Sources */, - 4A93893E25B8CBD40069F438 /* SDLMenuReplaceDynamicOperation.m in Sources */, + 4A93893E25B8CBD40069F438 /* SDLMenuReplaceOperation.m in Sources */, 4A8BD3AA24F948CF000945E3 /* SDLDeleteFileOperation.m in Sources */, 4ABB2B4524F84EF50061BF55 /* SDLBodyInformation.m in Sources */, 4ABB25BB24F7E70E0061BF55 /* SDLSoftButtonObject.m in Sources */, diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 60c806ff8..1ceb17a88 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -260,7 +260,7 @@ - (BOOL)openMenu:(nullable SDLMenuCell *)cell { - (void)sdl_startDynamicMenuUpdate { __weak typeof(self) weakself = self; - SDLMenuReplaceDynamicOperation *menuReplaceOperation = [[SDLMenuReplaceDynamicOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells currentMenuUpdatedBlock:^(NSArray * _Nonnull currentMenuCells) { + SDLMenuReplaceOperation *menuReplaceOperation = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells currentMenuUpdatedBlock:^(NSArray * _Nonnull currentMenuCells) { weakself.currentMenuCells = currentMenuCells; [weakself sdl_updateMenuReplaceOperationsWithNewCurrentMenu]; }]; @@ -275,7 +275,7 @@ - (void)sdl_startDynamicMenuUpdate { // Cancel previous replace menu operations for (NSOperation *operation in self.transactionQueue.operations) { if ([operation isMemberOfClass:[SDLMenuReplaceStaticOperation class]] - || [operation isMemberOfClass:[SDLMenuReplaceDynamicOperation class]]) { + || [operation isMemberOfClass:[SDLMenuReplaceOperation class]]) { [operation cancel]; } } @@ -302,7 +302,7 @@ - (void)sdl_startStaticMenuUpdate { // Cancel previous replace menu operations for (NSOperation *operation in self.transactionQueue.operations) { if ([operation isMemberOfClass:[SDLMenuReplaceStaticOperation class]] - || [operation isMemberOfClass:[SDLMenuReplaceDynamicOperation class]]) { + || [operation isMemberOfClass:[SDLMenuReplaceOperation class]]) { [operation cancel]; } } @@ -315,8 +315,8 @@ - (void)sdl_updateMenuReplaceOperationsWithNewCurrentMenu { if ([operation isMemberOfClass:[SDLMenuReplaceStaticOperation class]]) { SDLMenuReplaceStaticOperation *op = (SDLMenuReplaceStaticOperation *)operation; op.currentMenu = self.currentMenuCells; - } else if ([operation isMemberOfClass:[SDLMenuReplaceDynamicOperation class]]) { - SDLMenuReplaceDynamicOperation *op = (SDLMenuReplaceDynamicOperation *)operation; + } else if ([operation isMemberOfClass:[SDLMenuReplaceOperation class]]) { + SDLMenuReplaceOperation *op = (SDLMenuReplaceOperation *)operation; op.currentMenu = self.currentMenuCells; } } diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h b/SmartDeviceLink/private/SDLMenuReplaceOperation.h similarity index 79% rename from SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h rename to SmartDeviceLink/private/SDLMenuReplaceOperation.h index 1bb2d2680..bbfa07b03 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.h +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.h @@ -1,5 +1,5 @@ // -// SDLMenuReplaceDynamicOperation.h +// SDLMenuReplaceOperation.h // SmartDeviceLink // // Created by Joel Fischer on 1/20/21. @@ -21,13 +21,13 @@ NS_ASSUME_NONNULL_BEGIN -@interface SDLMenuReplaceDynamicOperation : SDLAsynchronousOperation +@interface SDLMenuReplaceOperation : SDLAsynchronousOperation @property (strong, nonatomic) SDLWindowCapability *windowCapability; @property (strong, nonatomic) SDLMenuConfiguration *menuConfiguration; @property (strong, nonatomic) NSArray *currentMenu; -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu currentMenuUpdatedBlock:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock; +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatbilityModeEnabled currentMenuUpdatedBlock:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m similarity index 82% rename from SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m rename to SmartDeviceLink/private/SDLMenuReplaceOperation.m index 8b17e9b17..67920e6b4 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceDynamicOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -6,7 +6,7 @@ // Copyright © 2021 smartdevicelink. All rights reserved. // -#import "SDLMenuReplaceDynamicOperation.h" +#import "SDLMenuReplaceOperation.h" #import "SDLArtwork.h" #import "SDLConnectionManagerType.h" @@ -32,19 +32,22 @@ @interface SDLMenuCell() @end -@interface SDLMenuReplaceDynamicOperation () +@interface SDLMenuReplaceOperation () @property (weak, nonatomic) id connectionManager; @property (weak, nonatomic) SDLFileManager *fileManager; @property (strong, nonatomic) NSArray *updatedMenu; +@property (assign, nonatomic) BOOL compatbilityModeEnabled; +@property (assign, nonatomic) SDLCurrentMenuUpdatedBlock currentMenuUpdatedBlock; +@property (strong, nonatomic) NSMutableArray *mutableCurrentMenu; @property (copy, nonatomic, nullable) NSError *internalError; @end -@implementation SDLMenuReplaceDynamicOperation +@implementation SDLMenuReplaceOperation -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu { +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatbilityModeEnabled currentMenuUpdatedBlock:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock { self = [super init]; if (!self) { return nil; } @@ -54,6 +57,8 @@ - (instancetype)initWithConnectionManager:(id)connecti _menuConfiguration = menuConfiguration; _currentMenu = currentMenu; _updatedMenu = updatedMenu; + _compatbilityModeEnabled = compatbilityModeEnabled; + _currentMenuUpdatedBlock = currentMenuUpdatedBlock; return self; } @@ -62,7 +67,14 @@ - (void)start { [super start]; if (self.isCancelled) { return; } - SDLDynamicMenuUpdateRunScore *runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; + SDLDynamicMenuUpdateRunScore *runScore = nil; + if (self.compatbilityModeEnabled) { + // TODO +// runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells: updatedMenuCells:] + } else { + runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; + } + NSArray *deleteMenuStatus = runScore.oldStatus; NSArray *addMenuStatus = runScore.updatedStatus; @@ -75,16 +87,20 @@ - (void)start { // Since we are creating a new Menu but keeping old cells we must firt transfer the old cellIDs to the new menus kept cells. [self sdl_transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; - // TODO: We don't check cancellation or finish - NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; - __weak typeof(self) weakself = self; + NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; if (artworksToBeUploaded.count > 0) { - [self.fileManager uploadArtworks:artworksToBeUploaded completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { - if (error != nil) { - SDLLogE(@"Error uploading menu artworks: %@", error); + [self.fileManager uploadArtworks:artworksToBeUploaded progressHandler:^BOOL(NSString * _Nonnull artworkName, float uploadPercentage, NSError * _Nullable error) { + if (weakself.isCancelled) { + [weakself finishOperation]; + return NO; } + return YES; + } completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { + if (weakself.isCancelled) { return; } + if (error != nil) { SDLLogE(@"Error uploading menu artworks: %@", error); } + SDLLogD(@"Menu artworks uploaded"); [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; @@ -92,16 +108,20 @@ - (void)start { }]; } else { // Cells have no artwork to load - [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { + [self sdl_updateMainMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { + if (weakself.isCancelled) { return [weakself finishOperation]; } [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; }]; } } -- (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(nullable SDLMenuUpdateCompletionHandler)completionHandler { +- (void)sdl_updateMainMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(SDLMenuUpdateCompletionHandler)handler { __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { - [weakself sdl_sendNewMenuCells:addCells oldMenu:weakself.currentMenu withCompletionHandler:^(NSError * _Nullable error) { }]; + if (weakself.isCancelled) { return handler(error); } + [weakself sdl_sendNewMenuCells:addCells oldMenu:weakself.currentMenu withCompletionHandler:^(NSError * _Nullable error) { + handler(error); + }]; }]; } @@ -183,13 +203,17 @@ - (void)sdl_startSubMenuUpdatesWithOldKeptCells:(NSArray *)oldKep __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { + if (weakself.isCancelled) { return [weakself finishOperation]; } + [weakself sdl_sendNewMenuCells:cellsToAdd oldMenu:weakself.currentMenu[startIndex].subCells withCompletionHandler:^(NSError * _Nullable error) { + if (weakself.isCancelled) { return [weakself finishOperation]; } + // After the first set of submenu cells were added and deleted we must find the next set of subcells until we loop through all the elements [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; }]; }]; } else { - // After the first set of submenu cells were added and deleted we must find the next set of subcells until we loop through all the elements + // There are no subcells, we can skip to the next index. [self sdl_startSubMenuUpdatesWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; } } @@ -246,6 +270,16 @@ - (void)sdl_transferCellIDFromOldCells:(NSArray *)oldCells toKept } } +#pragma mark - Getter / Setters + +- (void)setCurrentMenu:(NSArray *)currentMenu { + _mutableCurrentMenu = [currentMenu mutableCopy]; +} + +- (NSArray *)currentMenu { + return [_mutableCurrentMenu copy]; +} + #pragma mark - Operation Overrides - (void)finishOperation { From 6326a49fcdcbb630e8f93a26f1afdcb1bb6d8ef1 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 3 Feb 2021 16:07:31 -0500 Subject: [PATCH 022/112] Continue to work on updating menu replace operation --- .../private/SDLDynamicMenuUpdateRunScore.h | 4 +- SmartDeviceLink/private/SDLMenuManager.m | 2 +- .../private/SDLMenuReplaceOperation.m | 151 ++++++++++++------ 3 files changed, 108 insertions(+), 49 deletions(-) diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.h b/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.h index 12f251970..9e70fb39a 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.h +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.h @@ -13,12 +13,12 @@ NS_ASSUME_NONNULL_BEGIN @interface SDLDynamicMenuUpdateRunScore : NSObject /** - Will contain all the Deletes and Keeps + Will contain all the Deletes and Keeps. Contains SDLMenuState. */ @property (copy, nonatomic, readonly) NSArray *oldStatus; /** - Will contain all the Adds and Keeps + Will contain all the Adds and Keeps. Contains SDLMenuState. */ @property (copy, nonatomic, readonly) NSArray *updatedStatus; diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 1ceb17a88..d070acdb3 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -28,7 +28,7 @@ #import "SDLMenuConfigurationUpdateOperation.h" #import "SDLMenuManagerPrivateConstants.h" #import "SDLMenuParams.h" -#import "SDLMenuReplaceDynamicOperation.h" +#import "SDLMenuReplaceOperation.h" #import "SDLMenuReplaceStaticOperation.h" #import "SDLMenuShowOperation.h" #import "SDLOnCommand.h" diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 67920e6b4..95ab5a6c8 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -55,7 +55,7 @@ - (instancetype)initWithConnectionManager:(id)connecti _fileManager = fileManager; _windowCapability = windowCapability; _menuConfiguration = menuConfiguration; - _currentMenu = currentMenu; + _mutableCurrentMenu = [currentMenu mutableCopy]; _updatedMenu = updatedMenu; _compatbilityModeEnabled = compatbilityModeEnabled; _currentMenuUpdatedBlock = currentMenuUpdatedBlock; @@ -69,8 +69,7 @@ - (void)start { SDLDynamicMenuUpdateRunScore *runScore = nil; if (self.compatbilityModeEnabled) { - // TODO -// runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells: updatedMenuCells:] + runScore = [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[self sdl_buildAllDeleteStatusesForMenu:self.mutableCurrentMenu] updatedStatus:[self sdl_buildAllAddStatusesForMenu:self.updatedMenu] score:self.updatedMenu.count]; } else { runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; } @@ -87,6 +86,7 @@ - (void)start { // Since we are creating a new Menu but keeping old cells we must firt transfer the old cellIDs to the new menus kept cells. [self sdl_transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; + // Upload the artworks, then we will start updating the main menu __weak typeof(self) weakself = self; NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; if (artworksToBeUploaded.count > 0) { @@ -101,21 +101,30 @@ - (void)start { if (weakself.isCancelled) { return; } if (error != nil) { SDLLogE(@"Error uploading menu artworks: %@", error); } - SDLLogD(@"Menu artworks uploaded"); - [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { - [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; + SDLLogD(@"Menu artworks uploaded, beginning upload of main menu"); + // Start updating the main menu cells + [weakself sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { + if (weakself.isCancelled) { return [weakself finishOperation]; } + // Start uploading the submenu cells + [weakself sdl_updateSubMenuWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; }]; }]; } else { // Cells have no artwork to load - [self sdl_updateMainMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { + [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { if (weakself.isCancelled) { return [weakself finishOperation]; } - [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; + [weakself sdl_updateSubMenuWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; }]; } } -- (void)sdl_updateMainMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(SDLMenuUpdateCompletionHandler)handler { +#pragma mark - Update Main Menu / Submenu + +/// Takes the main menu cells to delete and add, and deletes the current menu cells, then adds the new menu cells in the correct locations +/// @param deleteCells The cells that need to be deleted +/// @param addCells The cells that need to be added +/// @param handler A handler called when complete +- (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(SDLMenuUpdateCompletionHandler)handler { __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { if (weakself.isCancelled) { return handler(error); } @@ -125,24 +134,79 @@ - (void)sdl_updateMainMenuWithCellsToDelete:(NSArray *)deleteCell }]; } +/// Takes the submenu cells that are old keeps and new keeps and determines which cells need to be deleted or added +/// @param oldKeptCells The old kept cells +/// @param newKeptCells The new kept cells +/// @param startIndex The index of the main menu to use +- (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells newKeptCells:(NSArray *)newKeptCells atIndex:(NSUInteger)startIndex { + if (oldKeptCells.count == 0 || startIndex >= oldKeptCells.count) { return; } + + if (oldKeptCells[startIndex].subCells.count > 0) { + SDLDynamicMenuUpdateRunScore *tempScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldKeptCells[startIndex].subCells updatedMenuCells:newKeptCells[startIndex].subCells]; + NSArray *deleteMenuStatus = tempScore.oldStatus; + NSArray *addMenuStatus = tempScore.updatedStatus; + + NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; + NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; + + NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; + NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; + + [self sdl_transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; + + __weak typeof(self) weakself = self; + [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { + if (weakself.isCancelled) { return [weakself finishOperation]; } + + [weakself sdl_sendNewMenuCells:cellsToAdd oldMenu:weakself.currentMenu[startIndex].subCells withCompletionHandler:^(NSError * _Nullable error) { + if (weakself.isCancelled) { return [weakself finishOperation]; } + + // After the first set of submenu cells were added and deleted we must find the next set of subcells until we loop through all the elements + [weakself sdl_updateSubMenuWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; + }]; + }]; + } else { + // There are no subcells, we can skip to the next index. + [self sdl_updateSubMenuWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; + } +} + +#pragma mark - Adding / Deleting cell RPCs + +/// Send Delete RPCs for given menu cells +/// @param deleteMenuCells The menu cells to be deleted +/// @param completionHandler A handler called when the RPCs are finished with an error if any failed - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { if (deleteMenuCells.count == 0) { completionHandler(nil); return; } + __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; NSArray *deleteMenuCommands = [SDLMenuReplaceUtilities deleteCommandsForCells:deleteMenuCells]; - [self.connectionManager sendRequests:deleteMenuCommands progressHandler:nil completionHandler:^(BOOL success) { + [self.connectionManager sendRequests:deleteMenuCommands progressHandler:^void(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { + if (error != nil) { + errors[request] = error; + } else { + // Find the id of the successful request and remove it from the current menu list whereever it may have been + UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; + [SDLMenuReplaceUtilities removeMenuCellFromList:self.mutableCurrentMenu withCmdId:commandId]; + } + } completionHandler:^(BOOL success) { if (!success) { - SDLLogW(@"Unable to delete all old menu commands"); + SDLLogE(@"Unable to delete all old menu commands with errors: %@", errors); + completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); } else { SDLLogD(@"Finished deleting old menu"); + completionHandler(nil); } - - completionHandler(nil); }]; } +/// Send Add RPCs for given new menu cells compared to old menu cells +/// @param newMenuCells The new menu cells we want displayed +/// @param oldMenu The old menu cells we no longer want displayed +/// @param completionHandler A handler called when the RPCs are finished with an error if any failed - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSArray *)oldMenu withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { if (self.updatedMenu.count == 0 || newMenuCells.count == 0) { SDLLogD(@"There are no cells to update."); @@ -158,6 +222,11 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA [self.connectionManager sendRequests:mainMenuCommands progressHandler:^void(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { if (error != nil) { errors[request] = error; + } else { + // Find the id of the successful request and add it from the current menu list whereever it needs to be + UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; + UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; + [SDLMenuReplaceUtilities addMenuRequestWithCommandId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; } } completionHandler:^(BOOL success) { if (!success) { @@ -169,6 +238,11 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA [weakSelf.connectionManager sendRequests:subMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { if (error != nil) { errors[request] = error; + } else { + // Find the id of the successful request and add it from the current menu list whereever it needs to be + UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; + UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; + [SDLMenuReplaceUtilities addMenuRequestWithCommandId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; } } completionHandler:^(BOOL success) { if (!success) { @@ -185,39 +259,6 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA #pragma mark Dynamic Menu Helpers -- (void)sdl_startSubMenuUpdatesWithOldKeptCells:(NSArray *)oldKeptCells newKeptCells:(NSArray *)newKeptCells atIndex:(NSUInteger)startIndex { - if (oldKeptCells.count == 0 || startIndex >= oldKeptCells.count) { return; } - - if (oldKeptCells[startIndex].subCells.count > 0) { - SDLDynamicMenuUpdateRunScore *tempScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldKeptCells[startIndex].subCells updatedMenuCells:newKeptCells[startIndex].subCells]; - NSArray *deleteMenuStatus = tempScore.oldStatus; - NSArray *addMenuStatus = tempScore.updatedStatus; - - NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; - NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; - - NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; - NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; - - [self sdl_transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; - - __weak typeof(self) weakself = self; - [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { - if (weakself.isCancelled) { return [weakself finishOperation]; } - - [weakself sdl_sendNewMenuCells:cellsToAdd oldMenu:weakself.currentMenu[startIndex].subCells withCompletionHandler:^(NSError * _Nullable error) { - if (weakself.isCancelled) { return [weakself finishOperation]; } - - // After the first set of submenu cells were added and deleted we must find the next set of subcells until we loop through all the elements - [weakself sdl_startSubMenuUpdatesWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; - }]; - }]; - } else { - // There are no subcells, we can skip to the next index. - [self sdl_startSubMenuUpdatesWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; - } -} - - (NSArray *)sdl_filterDeleteMenuItemsWithOldMenuItems:(NSArray *)oldMenuCells basedOnStatusList:(NSArray *)oldStatusList { NSMutableArray *deleteCells = [[NSMutableArray alloc] init]; // The index of the status should correlate 1-1 with the number of items in the menu @@ -270,6 +311,24 @@ - (void)sdl_transferCellIDFromOldCells:(NSArray *)oldCells toKept } } +- (NSArray *)sdl_buildAllDeleteStatusesForMenu:(NSArray *)menuCells { + NSMutableArray *mutableNumbers = [NSMutableArray arrayWithCapacity:menuCells.count]; + for (SDLMenuCell *cell in menuCells) { + [mutableNumbers addObject:@(MenuCellStateDelete)]; + } + + return [mutableNumbers copy]; +} + +- (NSArray *)sdl_buildAllAddStatusesForMenu:(NSArray *)menuCells { + NSMutableArray *mutableNumbers = [NSMutableArray arrayWithCapacity:menuCells.count]; + for (SDLMenuCell *cell in menuCells) { + [mutableNumbers addObject:@(MenuCellStateAdd)]; + } + + return [mutableNumbers copy]; +} + #pragma mark - Getter / Setters - (void)setCurrentMenu:(NSArray *)currentMenu { From 79c12a9f15070226328095561215070e0902de24 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 5 Feb 2021 15:07:36 -0500 Subject: [PATCH 023/112] Update rpc_spec --- generator/rpc_spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/rpc_spec b/generator/rpc_spec index 8b8c996c7..390394de3 160000 --- a/generator/rpc_spec +++ b/generator/rpc_spec @@ -1 +1 @@ -Subproject commit 8b8c996c738711c4235c8574c179354cbf8982e2 +Subproject commit 390394de357d3f097aef33ead9d17ef0f4c3d34c From d1662e74dd841ddb0bafc8437f95445b41f5e0ec Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 5 Feb 2021 15:43:54 -0500 Subject: [PATCH 024/112] Finish removing static operation --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 166 ++++++------- SmartDeviceLink/private/SDLMenuManager.m | 83 ++----- .../private/SDLMenuReplaceOperation.m | 6 +- .../private/SDLMenuReplaceStaticOperation.h | 32 --- .../private/SDLMenuReplaceStaticOperation.m | 220 ------------------ 5 files changed, 98 insertions(+), 409 deletions(-) delete mode 100644 SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h delete mode 100644 SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index d5068d178..084f85177 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -294,7 +294,7 @@ 4A402561250134CB0080E159 /* SDLStabilityControlsStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A40255F250134CA0080E159 /* SDLStabilityControlsStatus.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4A404C66250BBE11003AB65D /* SDLTextAndGraphicUpdateOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A404C65250BBE11003AB65D /* SDLTextAndGraphicUpdateOperationSpec.m */; }; 4A404C68250BBE2B003AB65D /* SDLTextAndGraphicStateSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A404C67250BBE2B003AB65D /* SDLTextAndGraphicStateSpec.m */; }; - 4A41430D255F0A090039C267 /* TestConnectionRequestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A41430C255F0A090039C267 /* TestConnectionRequestObject.m */; }; + 4A41430D255F0A090039C267 /* TestConnectionRequestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A41430C255F0A090039C267 /* TestConnectionRequestObject.m */; }; 4A457DC324A2933E00386CBA /* SDLLifecycleRPCAdapterSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A457DC224A2933E00386CBA /* SDLLifecycleRPCAdapterSpec.m */; }; 4A457DD324A3886700386CBA /* SDLLifecycleSyncPDataHandlerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A457DD224A3886700386CBA /* SDLLifecycleSyncPDataHandlerSpec.m */; }; 4A457DD524A3C16E00386CBA /* SDLLifecycleMobileHMIStateHandlerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A457DD424A3C16E00386CBA /* SDLLifecycleMobileHMIStateHandlerSpec.m */; }; @@ -513,8 +513,6 @@ 4A8BD3D124FE7CF1000945E3 /* SDLPermissionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8BD3CF24FE7CF1000945E3 /* SDLPermissionManager.m */; }; 4A93893D25B8CBD40069F438 /* SDLMenuReplaceOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93893B25B8CBD40069F438 /* SDLMenuReplaceOperation.h */; }; 4A93893E25B8CBD40069F438 /* SDLMenuReplaceOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93893C25B8CBD40069F438 /* SDLMenuReplaceOperation.m */; }; - 4A93894525B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93894325B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h */; }; - 4A93894625B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93894425B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m */; }; 4A93895325B9DACA0069F438 /* SDLMenuShowOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93895125B9DACA0069F438 /* SDLMenuShowOperation.h */; }; 4A93895425B9DACA0069F438 /* SDLMenuShowOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93895225B9DACA0069F438 /* SDLMenuShowOperation.m */; }; 4A93895925B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93895725B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h */; }; @@ -1398,9 +1396,9 @@ 4ABB2BA724F850AE0061BF55 /* SDLImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ABB2B9924F850AD0061BF55 /* SDLImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4ABB2BA824F850AE0061BF55 /* SDLLightState.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABB2B9A24F850AD0061BF55 /* SDLLightState.m */; }; 4ABB2BA924F850AE0061BF55 /* SDLImageResolution.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ABB2B9B24F850AD0061BF55 /* SDLImageResolution.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4ABED25B257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABED259257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.m */; }; - 4ABED25C257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ABED25A257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.h */; }; - 4AD1F1742559957100637FE1 /* SDLVoiceCommandUpdateOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AD1F1732559957100637FE1 /* SDLVoiceCommandUpdateOperationSpec.m */; }; + 4ABED25B257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABED259257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.m */; }; + 4ABED25C257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ABED25A257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.h */; }; + 4AD1F1742559957100637FE1 /* SDLVoiceCommandUpdateOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AD1F1732559957100637FE1 /* SDLVoiceCommandUpdateOperationSpec.m */; }; 4AE8A7022537796E000666C0 /* SmartDeviceLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AE8A7012537796E000666C0 /* SmartDeviceLink.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5D0A9F911F15550400CC80DD /* SDLSystemCapabilityTypeSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D0A9F901F15550400CC80DD /* SDLSystemCapabilityTypeSpec.m */; }; 5D0A9F931F15560B00CC80DD /* SDLNavigationCapabilitySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D0A9F921F15560B00CC80DD /* SDLNavigationCapabilitySpec.m */; }; @@ -1684,18 +1682,18 @@ B360F9E5255F52BA0027CA17 /* SDLSeatOccupancy.h in Headers */ = {isa = PBXBuildFile; fileRef = B360F9E3255F52B90027CA17 /* SDLSeatOccupancy.h */; settings = {ATTRIBUTES = (Public, ); }; }; B38389D6257C187500420C11 /* SDLSeatOccupancySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B38389D4257C187400420C11 /* SDLSeatOccupancySpec.m */; }; B38389D7257C187500420C11 /* SDLSeatStatusSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B38389D5257C187500420C11 /* SDLSeatStatusSpec.m */; }; - B3838A01257C47FD00420C11 /* SDLDoorStatusTypeSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A00257C47FD00420C11 /* SDLDoorStatusTypeSpec.m */; }; - B3838A09257C4EB400420C11 /* SDLDoorStatusSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A08257C4EB300420C11 /* SDLDoorStatusSpec.m */; }; - B3838A0F257C4EE100420C11 /* SDLGateStatusSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A0E257C4EE100420C11 /* SDLGateStatusSpec.m */; }; - B3838A15257C4EFD00420C11 /* SDLRoofStatusSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A14257C4EFD00420C11 /* SDLRoofStatusSpec.m */; }; - B3838A20257C5BB000420C11 /* SDLRoofStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = B3838A1E257C5BAF00420C11 /* SDLRoofStatus.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B3838A21257C5BB000420C11 /* SDLRoofStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A1F257C5BB000420C11 /* SDLRoofStatus.m */; }; - B3838A28257C5CE600420C11 /* SDLDoorStatusType.h in Headers */ = {isa = PBXBuildFile; fileRef = B3838A26257C5CE600420C11 /* SDLDoorStatusType.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B3838A29257C5CE600420C11 /* SDLDoorStatusType.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A27257C5CE600420C11 /* SDLDoorStatusType.m */; }; - B3838A30257C5D1B00420C11 /* SDLGateStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A2E257C5D1A00420C11 /* SDLGateStatus.m */; }; - B3838A31257C5D1B00420C11 /* SDLGateStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = B3838A2F257C5D1B00420C11 /* SDLGateStatus.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B3838A3A257C6AB700420C11 /* SDLDoorStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A38257C6AB600420C11 /* SDLDoorStatus.m */; }; - B3838A3B257C6AB700420C11 /* SDLDoorStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = B3838A39257C6AB600420C11 /* SDLDoorStatus.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B3838A01257C47FD00420C11 /* SDLDoorStatusTypeSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A00257C47FD00420C11 /* SDLDoorStatusTypeSpec.m */; }; + B3838A09257C4EB400420C11 /* SDLDoorStatusSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A08257C4EB300420C11 /* SDLDoorStatusSpec.m */; }; + B3838A0F257C4EE100420C11 /* SDLGateStatusSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A0E257C4EE100420C11 /* SDLGateStatusSpec.m */; }; + B3838A15257C4EFD00420C11 /* SDLRoofStatusSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A14257C4EFD00420C11 /* SDLRoofStatusSpec.m */; }; + B3838A20257C5BB000420C11 /* SDLRoofStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = B3838A1E257C5BAF00420C11 /* SDLRoofStatus.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B3838A21257C5BB000420C11 /* SDLRoofStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A1F257C5BB000420C11 /* SDLRoofStatus.m */; }; + B3838A28257C5CE600420C11 /* SDLDoorStatusType.h in Headers */ = {isa = PBXBuildFile; fileRef = B3838A26257C5CE600420C11 /* SDLDoorStatusType.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B3838A29257C5CE600420C11 /* SDLDoorStatusType.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A27257C5CE600420C11 /* SDLDoorStatusType.m */; }; + B3838A30257C5D1B00420C11 /* SDLGateStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A2E257C5D1A00420C11 /* SDLGateStatus.m */; }; + B3838A31257C5D1B00420C11 /* SDLGateStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = B3838A2F257C5D1B00420C11 /* SDLGateStatus.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B3838A3A257C6AB700420C11 /* SDLDoorStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = B3838A38257C6AB600420C11 /* SDLDoorStatus.m */; }; + B3838A3B257C6AB700420C11 /* SDLDoorStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = B3838A39257C6AB600420C11 /* SDLDoorStatus.h */; settings = {ATTRIBUTES = (Public, ); }; }; B38D8E7E24A118BD00B977D0 /* SDLGearStatusSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B38D8E7D24A118BD00B977D0 /* SDLGearStatusSpec.m */; }; B38D8E8024A1E3D000B977D0 /* SDLTransmissionTypeSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B38D8E7F24A1E3D000B977D0 /* SDLTransmissionTypeSpec.m */; }; B38D8E8224A1F53500B977D0 /* SDLCapacityUnitSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = B38D8E8124A1F53500B977D0 /* SDLCapacityUnitSpec.m */; }; @@ -2109,8 +2107,8 @@ 4A40255F250134CA0080E159 /* SDLStabilityControlsStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLStabilityControlsStatus.h; path = public/SDLStabilityControlsStatus.h; sourceTree = ""; }; 4A404C65250BBE11003AB65D /* SDLTextAndGraphicUpdateOperationSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLTextAndGraphicUpdateOperationSpec.m; path = DevAPISpecs/SDLTextAndGraphicUpdateOperationSpec.m; sourceTree = ""; }; 4A404C67250BBE2B003AB65D /* SDLTextAndGraphicStateSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLTextAndGraphicStateSpec.m; path = DevAPISpecs/SDLTextAndGraphicStateSpec.m; sourceTree = ""; }; - 4A41430B255F0A090039C267 /* TestConnectionRequestObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestConnectionRequestObject.h; sourceTree = ""; }; - 4A41430C255F0A090039C267 /* TestConnectionRequestObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestConnectionRequestObject.m; sourceTree = ""; }; + 4A41430B255F0A090039C267 /* TestConnectionRequestObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestConnectionRequestObject.h; sourceTree = ""; }; + 4A41430C255F0A090039C267 /* TestConnectionRequestObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestConnectionRequestObject.m; sourceTree = ""; }; 4A457DC224A2933E00386CBA /* SDLLifecycleRPCAdapterSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLLifecycleRPCAdapterSpec.m; path = DevAPISpecs/SDLLifecycleRPCAdapterSpec.m; sourceTree = ""; }; 4A457DD224A3886700386CBA /* SDLLifecycleSyncPDataHandlerSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLLifecycleSyncPDataHandlerSpec.m; path = DevAPISpecs/SDLLifecycleSyncPDataHandlerSpec.m; sourceTree = ""; }; 4A457DD424A3C16E00386CBA /* SDLLifecycleMobileHMIStateHandlerSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLLifecycleMobileHMIStateHandlerSpec.m; path = DevAPISpecs/SDLLifecycleMobileHMIStateHandlerSpec.m; sourceTree = ""; }; @@ -2333,8 +2331,6 @@ 4A8BD3CF24FE7CF1000945E3 /* SDLPermissionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLPermissionManager.m; path = public/SDLPermissionManager.m; sourceTree = ""; }; 4A93893B25B8CBD40069F438 /* SDLMenuReplaceOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuReplaceOperation.h; path = private/SDLMenuReplaceOperation.h; sourceTree = ""; }; 4A93893C25B8CBD40069F438 /* SDLMenuReplaceOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceOperation.m; path = private/SDLMenuReplaceOperation.m; sourceTree = ""; }; - 4A93894325B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuReplaceStaticOperation.h; path = private/SDLMenuReplaceStaticOperation.h; sourceTree = ""; }; - 4A93894425B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceStaticOperation.m; path = private/SDLMenuReplaceStaticOperation.m; sourceTree = ""; }; 4A93895125B9DACA0069F438 /* SDLMenuShowOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuShowOperation.h; path = private/SDLMenuShowOperation.h; sourceTree = ""; }; 4A93895225B9DACA0069F438 /* SDLMenuShowOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuShowOperation.m; path = private/SDLMenuShowOperation.m; sourceTree = ""; }; 4A93895725B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuConfigurationUpdateOperation.h; path = private/SDLMenuConfigurationUpdateOperation.h; sourceTree = ""; }; @@ -3221,9 +3217,9 @@ 4ABB2B9924F850AD0061BF55 /* SDLImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLImage.h; path = public/SDLImage.h; sourceTree = ""; }; 4ABB2B9A24F850AD0061BF55 /* SDLLightState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLLightState.m; path = public/SDLLightState.m; sourceTree = ""; }; 4ABB2B9B24F850AD0061BF55 /* SDLImageResolution.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLImageResolution.h; path = public/SDLImageResolution.h; sourceTree = ""; }; - 4ABED259257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLVoiceCommandUpdateOperation.m; path = private/SDLVoiceCommandUpdateOperation.m; sourceTree = ""; }; - 4ABED25A257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLVoiceCommandUpdateOperation.h; path = private/SDLVoiceCommandUpdateOperation.h; sourceTree = ""; }; - 4AD1F1732559957100637FE1 /* SDLVoiceCommandUpdateOperationSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLVoiceCommandUpdateOperationSpec.m; path = DevAPISpecs/SDLVoiceCommandUpdateOperationSpec.m; sourceTree = ""; }; + 4ABED259257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLVoiceCommandUpdateOperation.m; path = private/SDLVoiceCommandUpdateOperation.m; sourceTree = ""; }; + 4ABED25A257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLVoiceCommandUpdateOperation.h; path = private/SDLVoiceCommandUpdateOperation.h; sourceTree = ""; }; + 4AD1F1732559957100637FE1 /* SDLVoiceCommandUpdateOperationSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLVoiceCommandUpdateOperationSpec.m; path = DevAPISpecs/SDLVoiceCommandUpdateOperationSpec.m; sourceTree = ""; }; 4AE8A7012537796E000666C0 /* SmartDeviceLink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SmartDeviceLink.h; path = public/SmartDeviceLink.h; sourceTree = ""; }; 4AE8A707253779F9000666C0 /* EAAccessory+OCMock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "EAAccessory+OCMock.h"; sourceTree = ""; }; 5D0A9F901F15550400CC80DD /* SDLSystemCapabilityTypeSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLSystemCapabilityTypeSpec.m; sourceTree = ""; }; @@ -3548,18 +3544,18 @@ B360F9E3255F52B90027CA17 /* SDLSeatOccupancy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLSeatOccupancy.h; path = public/SDLSeatOccupancy.h; sourceTree = ""; }; B38389D4257C187400420C11 /* SDLSeatOccupancySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLSeatOccupancySpec.m; path = SmartDeviceLinkTests/RPCSpecs/ResponseSpecs/SDLSeatOccupancySpec.m; sourceTree = SOURCE_ROOT; }; B38389D5257C187500420C11 /* SDLSeatStatusSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLSeatStatusSpec.m; path = SmartDeviceLinkTests/RPCSpecs/ResponseSpecs/SDLSeatStatusSpec.m; sourceTree = SOURCE_ROOT; }; - B3838A00257C47FD00420C11 /* SDLDoorStatusTypeSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLDoorStatusTypeSpec.m; sourceTree = ""; }; - B3838A08257C4EB300420C11 /* SDLDoorStatusSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLDoorStatusSpec.m; path = SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLDoorStatusSpec.m; sourceTree = SOURCE_ROOT; }; - B3838A0E257C4EE100420C11 /* SDLGateStatusSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLGateStatusSpec.m; path = SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLGateStatusSpec.m; sourceTree = SOURCE_ROOT; }; - B3838A14257C4EFD00420C11 /* SDLRoofStatusSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLRoofStatusSpec.m; path = SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLRoofStatusSpec.m; sourceTree = SOURCE_ROOT; }; - B3838A1E257C5BAF00420C11 /* SDLRoofStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLRoofStatus.h; path = public/SDLRoofStatus.h; sourceTree = ""; }; - B3838A1F257C5BB000420C11 /* SDLRoofStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLRoofStatus.m; path = public/SDLRoofStatus.m; sourceTree = ""; }; - B3838A26257C5CE600420C11 /* SDLDoorStatusType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLDoorStatusType.h; path = public/SDLDoorStatusType.h; sourceTree = ""; }; - B3838A27257C5CE600420C11 /* SDLDoorStatusType.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLDoorStatusType.m; path = public/SDLDoorStatusType.m; sourceTree = ""; }; - B3838A2E257C5D1A00420C11 /* SDLGateStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLGateStatus.m; path = public/SDLGateStatus.m; sourceTree = ""; }; - B3838A2F257C5D1B00420C11 /* SDLGateStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLGateStatus.h; path = public/SDLGateStatus.h; sourceTree = ""; }; - B3838A38257C6AB600420C11 /* SDLDoorStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLDoorStatus.m; path = public/SDLDoorStatus.m; sourceTree = ""; }; - B3838A39257C6AB600420C11 /* SDLDoorStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLDoorStatus.h; path = public/SDLDoorStatus.h; sourceTree = ""; }; + B3838A00257C47FD00420C11 /* SDLDoorStatusTypeSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLDoorStatusTypeSpec.m; sourceTree = ""; }; + B3838A08257C4EB300420C11 /* SDLDoorStatusSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLDoorStatusSpec.m; path = SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLDoorStatusSpec.m; sourceTree = SOURCE_ROOT; }; + B3838A0E257C4EE100420C11 /* SDLGateStatusSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLGateStatusSpec.m; path = SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLGateStatusSpec.m; sourceTree = SOURCE_ROOT; }; + B3838A14257C4EFD00420C11 /* SDLRoofStatusSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLRoofStatusSpec.m; path = SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLRoofStatusSpec.m; sourceTree = SOURCE_ROOT; }; + B3838A1E257C5BAF00420C11 /* SDLRoofStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLRoofStatus.h; path = public/SDLRoofStatus.h; sourceTree = ""; }; + B3838A1F257C5BB000420C11 /* SDLRoofStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLRoofStatus.m; path = public/SDLRoofStatus.m; sourceTree = ""; }; + B3838A26257C5CE600420C11 /* SDLDoorStatusType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLDoorStatusType.h; path = public/SDLDoorStatusType.h; sourceTree = ""; }; + B3838A27257C5CE600420C11 /* SDLDoorStatusType.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLDoorStatusType.m; path = public/SDLDoorStatusType.m; sourceTree = ""; }; + B3838A2E257C5D1A00420C11 /* SDLGateStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLGateStatus.m; path = public/SDLGateStatus.m; sourceTree = ""; }; + B3838A2F257C5D1B00420C11 /* SDLGateStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLGateStatus.h; path = public/SDLGateStatus.h; sourceTree = ""; }; + B3838A38257C6AB600420C11 /* SDLDoorStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLDoorStatus.m; path = public/SDLDoorStatus.m; sourceTree = ""; }; + B3838A39257C6AB600420C11 /* SDLDoorStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLDoorStatus.h; path = public/SDLDoorStatus.h; sourceTree = ""; }; B38D8E7D24A118BD00B977D0 /* SDLGearStatusSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLGearStatusSpec.m; sourceTree = ""; }; B38D8E7F24A1E3D000B977D0 /* SDLTransmissionTypeSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLTransmissionTypeSpec.m; sourceTree = ""; }; B38D8E8124A1F53500B977D0 /* SDLCapacityUnitSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLCapacityUnitSpec.m; sourceTree = ""; }; @@ -3760,7 +3756,7 @@ 162E81F11A9BDE8A00906325 /* SDLDisplayTypeSpec.m */, 1EAA475D2035B177000FE74B /* SDLDisplayModeSpec.m */, 1EAA47612035B1AE000FE74B /* SDLDistanceUnitSpec.m */, - B3838A00257C47FD00420C11 /* SDLDoorStatusTypeSpec.m */, + B3838A00257C47FD00420C11 /* SDLDoorStatusTypeSpec.m */, 162E81F21A9BDE8A00906325 /* SDLDriverDistractionStateSpec.m */, 162E81F31A9BDE8A00906325 /* SDLECallConfirmationStatusSpec.m */, 5DD8406420FCE21A0082CE04 /* SDLElectronicParkBrakeStatusSpec.m */, @@ -4055,13 +4051,13 @@ 162E82981A9BDE8A00906325 /* SDLDIDResult.m */, 9FA0D00522DF06D3009CF344 /* SDLDisplayCapabilitySpec.m */, 162E82991A9BDE8A00906325 /* SDLDisplayCapabilitiesSpec.m */, - B3838A08257C4EB300420C11 /* SDLDoorStatusSpec.m */, + B3838A08257C4EB300420C11 /* SDLDoorStatusSpec.m */, 4A1B036E24CF484E008C6B13 /* SDLDriverDistractionCapabilitySpec.m */, 162E829A1A9BDE8A00906325 /* SDLECallInfoSpec.m */, 162E829B1A9BDE8A00906325 /* SDLEmergencyEventSpec.m */, 88B3BFA120DA911E00943565 /* SDLFuelRangeSpec.m */, 1EAA47752036B847000FE74B /* SDLEqualizerSettingsSpec.m */, - B3838A0E257C4EE100420C11 /* SDLGateStatusSpec.m */, + B3838A0E257C4EE100420C11 /* SDLGateStatusSpec.m */, B38D8E7D24A118BD00B977D0 /* SDLGearStatusSpec.m */, 162E829C1A9BDE8A00906325 /* SDLGPSDataSpec.m */, 88EED83A1F33BECB00E6C42E /* SDLHapticRectSpec.m */, @@ -4103,14 +4099,12 @@ 1EE8C44F1F38629200FDC2CF /* SDLRemoteControlCapabilitiesSpec.m */, 5DADA7771F4E059E0084D17D /* SDLRectangleSpec.m */, 5D92934F20AF526200FCC775 /* SDLRGBColorSpec.m */, - B3838A14257C4EFD00420C11 /* SDLRoofStatusSpec.m */, + B3838A14257C4EFD00420C11 /* SDLRoofStatusSpec.m */, 162E82A71A9BDE8A00906325 /* SDLScreenParamsSpec.m */, B38389D4257C187400420C11 /* SDLSeatOccupancySpec.m */, B38389D5257C187500420C11 /* SDLSeatStatusSpec.m */, 1E89B0E1203196B800A47992 /* SDLSeatControlCapabilitiesSpec.m */, 1E89B0DD2031636000A47992 /* SDLSeatControlDataSpec.m */, - 000DD56B22EEF8E4005AB7A7 /* SDLSeatLocationCapabilitySpec.m */, - 000DD56D22EF01FC005AB7A7 /* SDLSeatLocationSpec.m */, 1EB59CD9202DCEEC00343A61 /* SDLSeatMemoryActionSpec.m */, C9758784257F4C570066F271 /* SDLSeekStreamingIndicatorSpec.m */, 162E82A81A9BDE8A00906325 /* SDLSingleTireStatusSpec.m */, @@ -4139,8 +4133,8 @@ 8855F9DF220C93B700A5C897 /* SDLWeatherDataSpec.m */, 880D2679220DDD1000B3F496 /* SDLWeatherServiceDataSpec.m */, 880D267F220E038800B3F496 /* SDLWeatherServiceManifestSpec.m */, - 000DD56B22EEF8E4005AB7A7 /* SDLSeatLocationCapabilitySpec.m */, - 000DD56D22EF01FC005AB7A7 /* SDLSeatLocationSpec.m */, + 000DD56B22EEF8E4005AB7A7 /* SDLSeatLocationCapabilitySpec.m */, + 000DD56D22EF01FC005AB7A7 /* SDLSeatLocationSpec.m */, 9FA0CFFF22DF06A0009CF344 /* SDLWindowCapabilitySpec.m */, 9FA0D00222DF06B9009CF344 /* SDLWindowTypeCapabilitiesSpec.m */, ); @@ -4312,14 +4306,11 @@ 4A93893A25B8CB480069F438 /* Operations */ = { isa = PBXGroup; children = ( - 755F175E229F14F70041B9CB /* Dynamic Menu Update Utilities */, 4A93896325BB35FA0069F438 /* Menu Replace Utilities */, 4A93895725B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.h */, 4A93895825B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m */, 4A93893B25B8CBD40069F438 /* SDLMenuReplaceOperation.h */, 4A93893C25B8CBD40069F438 /* SDLMenuReplaceOperation.m */, - 4A93894325B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h */, - 4A93894425B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m */, 4A93895125B9DACA0069F438 /* SDLMenuShowOperation.h */, 4A93895225B9DACA0069F438 /* SDLMenuShowOperation.m */, ); @@ -4329,6 +4320,10 @@ 4A93896325BB35FA0069F438 /* Menu Replace Utilities */ = { isa = PBXGroup; children = ( + 4ABB25A124F7E6CE0061BF55 /* SDLDynamicMenuUpdateAlgorithm.h */, + 4ABB259F24F7E6CE0061BF55 /* SDLDynamicMenuUpdateAlgorithm.m */, + 4ABB259E24F7E6CE0061BF55 /* SDLDynamicMenuUpdateRunScore.h */, + 4ABB25A024F7E6CE0061BF55 /* SDLDynamicMenuUpdateRunScore.m */, 4A93896425BB361C0069F438 /* SDLMenuReplaceUtilities.h */, 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */, ); @@ -4610,7 +4605,7 @@ children = ( 5D4019B11A76EC350006B0C2 /* Examples */, 5D61FA1D1A84237100846EE7 /* SmartDeviceLink */, - 5D4346621E6F38E600B639C6 /* SmartDeviceLinkSwift */, + 5D4346621E6F38E600B639C6 /* SmartDeviceLinkSwift */, 5D61FA2C1A84237100846EE7 /* SmartDeviceLinkTests */, 5D4019B01A76EC350006B0C2 /* Products */, ); @@ -5145,8 +5140,8 @@ 4ABB2B2524F84EF10061BF55 /* SDLDisplayCapabilities.m */, 4ABB2B3324F84EF30061BF55 /* SDLDisplayCapability.h */, 4ABB2B3824F84EF40061BF55 /* SDLDisplayCapability.m */, - B3838A39257C6AB600420C11 /* SDLDoorStatus.h */, - B3838A38257C6AB600420C11 /* SDLDoorStatus.m */, + B3838A39257C6AB600420C11 /* SDLDoorStatus.h */, + B3838A38257C6AB600420C11 /* SDLDoorStatus.m */, 4ABB2B3724F84EF40061BF55 /* SDLDriverDistractionCapability.h */, 4ABB2B2B24F84EF20061BF55 /* SDLDriverDistractionCapability.m */, 4A8BD3B924F98F89000945E3 /* SDLDynamicUpdateCapabilities.h */, @@ -5159,8 +5154,8 @@ 4ABB2B5D24F84FE30061BF55 /* SDLEqualizerSettings.m */, 4ABB2B6424F84FE50061BF55 /* SDLFuelRange.h */, 4ABB2B6524F84FE50061BF55 /* SDLFuelRange.m */, - B3838A2F257C5D1B00420C11 /* SDLGateStatus.h */, - B3838A2E257C5D1A00420C11 /* SDLGateStatus.m */, + B3838A2F257C5D1B00420C11 /* SDLGateStatus.h */, + B3838A2E257C5D1A00420C11 /* SDLGateStatus.m */, 4ABB2B5C24F84FE30061BF55 /* SDLGearStatus.h */, 4ABB2B5F24F84FE40061BF55 /* SDLGearStatus.m */, 4ABB2B5A24F84FE30061BF55 /* SDLGPSData.h */, @@ -5249,8 +5244,8 @@ 4A8BD26C24F9343D000945E3 /* SDLRemoteControlCapabilities.m */, 4A8BD28724F934F2000945E3 /* SDLRGBColor.h */, 4A8BD28A24F934F3000945E3 /* SDLRGBColor.m */, - B3838A1E257C5BAF00420C11 /* SDLRoofStatus.h */, - B3838A1F257C5BB000420C11 /* SDLRoofStatus.m */, + B3838A1E257C5BAF00420C11 /* SDLRoofStatus.h */, + B3838A1F257C5BB000420C11 /* SDLRoofStatus.m */, 4A8BD29124F93533000945E3 /* SDLScreenParams.h */, 4A8BD28F24F93533000945E3 /* SDLScreenParams.m */, 4A8BD29024F93533000945E3 /* SDLSeatControlCapabilities.h */, @@ -5388,8 +5383,8 @@ 4ABB272A24F7FD1B0061BF55 /* SDLDistanceUnit.m */, 4ABB272D24F7FD1C0061BF55 /* SDLDisplayType.h */, 4ABB272B24F7FD1B0061BF55 /* SDLDisplayType.m */, - B3838A26257C5CE600420C11 /* SDLDoorStatusType.h */, - B3838A27257C5CE600420C11 /* SDLDoorStatusType.m */, + B3838A26257C5CE600420C11 /* SDLDoorStatusType.h */, + B3838A27257C5CE600420C11 /* SDLDoorStatusType.m */, 4ABB274424F7FD9A0061BF55 /* SDLDriverDistractionState.h */, 4ABB274924F7FD9B0061BF55 /* SDLDriverDistractionState.m */, 4ABB274724F7FD9B0061BF55 /* SDLECallConfirmationStatus.h */, @@ -6268,7 +6263,7 @@ 5DAD5F8220507DED0025624C /* Soft Button */, 88D0E5D42478656B009469AB /* Subscribe Button */, 5DAD5F8320507DF30025624C /* Text and Graphic */, - 4AD1F16A2559952D00637FE1 /* Voice Command */, + 4AD1F16A2559952D00637FE1 /* Voice Command */, 5DAD5F8420507E1F0025624C /* SDLScreenManagerSpec.m */, ); name = Screen; @@ -6339,8 +6334,8 @@ 8850DB5F1F4475D30053A48D /* TestMultipleFilesConnectionManager.m */, 5D6035D3202CE4A500A429C9 /* TestMultipleRequestsConnectionManager.h */, 5D6035D4202CE4A500A429C9 /* TestMultipleRequestsConnectionManager.m */, - 4A41430B255F0A090039C267 /* TestConnectionRequestObject.h */, - 4A41430C255F0A090039C267 /* TestConnectionRequestObject.m */, + 4A41430B255F0A090039C267 /* TestConnectionRequestObject.h */, + 4A41430C255F0A090039C267 /* TestConnectionRequestObject.m */, ); name = "Connection Manager"; sourceTree = ""; @@ -6640,17 +6635,6 @@ name = Menu; sourceTree = ""; }; - 755F175E229F14F70041B9CB /* Dynamic Menu Update Utilities */ = { - isa = PBXGroup; - children = ( - 4ABB25A124F7E6CE0061BF55 /* SDLDynamicMenuUpdateAlgorithm.h */, - 4ABB259F24F7E6CE0061BF55 /* SDLDynamicMenuUpdateAlgorithm.m */, - 4ABB259E24F7E6CE0061BF55 /* SDLDynamicMenuUpdateRunScore.h */, - 4ABB25A024F7E6CE0061BF55 /* SDLDynamicMenuUpdateRunScore.m */, - ); - name = "Dynamic Menu Update Utilities"; - sourceTree = ""; - }; 880245A120F79BDA00ED195B /* Configuration */ = { isa = PBXGroup; children = ( @@ -7041,7 +7025,6 @@ 4ABB2A3624F847980061BF55 /* SDLDialNumberResponse.h in Headers */, 4ABB297724F844D30061BF55 /* SDLPerformAppServiceInteraction.h in Headers */, 4ABB27E124F800CA0061BF55 /* SDLPowerModeQualificationStatus.h in Headers */, - 4A93894525B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.h in Headers */, 4A8BD2D124F93803000945E3 /* SDLTemperature.h in Headers */, 4ABB282624F824E70061BF55 /* SDLTemperatureUnit.h in Headers */, C9DFFE7E257AD07E00F7D57A /* SDLSeekIndicatorType.h in Headers */, @@ -7055,7 +7038,7 @@ 4ABB269624F7F9400061BF55 /* SDLRPCFunctionNames.h in Headers */, 4ABB2B4024F84EF50061BF55 /* SDLCloudAppProperties.h in Headers */, 4ABB2A5824F847B10061BF55 /* SDLGetInteriorVehicleDataConsentResponse.h in Headers */, - B3838A20257C5BB000420C11 /* SDLRoofStatus.h in Headers */, + B3838A20257C5BB000420C11 /* SDLRoofStatus.h in Headers */, 4ABB271824F7FC4E0061BF55 /* SDLCompassDirection.h in Headers */, 4ABB2B5924F84EF50061BF55 /* SDLDeviceStatus.h in Headers */, 4A8BD2CF24F93803000945E3 /* SDLTouchEvent.h in Headers */, @@ -7184,7 +7167,7 @@ 4ABB280124F823F20061BF55 /* SDLResult.h in Headers */, 4ABB2B8624F8504A0061BF55 /* SDLHMISettingsControlCapabilities.h in Headers */, 4ABB276924F7FE480061BF55 /* SDLHMIZoneCapabilities.h in Headers */, - B3838A3B257C6AB700420C11 /* SDLDoorStatus.h in Headers */, + B3838A3B257C6AB700420C11 /* SDLDoorStatus.h in Headers */, 4ABB29DF24F846880061BF55 /* SDLSystemRequest.h in Headers */, 4ABB290224F82BE90061BF55 /* SDLAddCommand.h in Headers */, 4ABB260624F7E9650061BF55 /* SDLStreamingMediaManager.h in Headers */, @@ -7359,7 +7342,7 @@ 4ABB24C924F593090061BF55 /* SDLStreamingProtocolDelegate.h in Headers */, 4ABB275F24F7FE1F0061BF55 /* SDLFuelType.h in Headers */, 4A8BD3A024F9474B000945E3 /* SDLIAPDataSessionDelegate.h in Headers */, - B3838A28257C5CE600420C11 /* SDLDoorStatusType.h in Headers */, + B3838A28257C5CE600420C11 /* SDLDoorStatusType.h in Headers */, 4A8BD2F724F93872000945E3 /* SDLVrHelpItem.h in Headers */, 4ABB2A0024F8477F0061BF55 /* SDLAddCommandResponse.h in Headers */, 4ABB291124F842160061BF55 /* SDLChangeRegistration.h in Headers */, @@ -7535,11 +7518,11 @@ 4ABB296524F844020061BF55 /* SDLListFiles.h in Headers */, 4ABB281824F824A50061BF55 /* SDLStaticIconName.h in Headers */, 4ABB290524F82BE90061BF55 /* SDLAlert.h in Headers */, - B3838A31257C5D1B00420C11 /* SDLGateStatus.h in Headers */, + B3838A31257C5D1B00420C11 /* SDLGateStatus.h in Headers */, 4ABB26BA24F7FA1C0061BF55 /* SDLLogConstants.h in Headers */, 4ABB28DB24F82A6A0061BF55 /* SDLOnSystemCapabilityUpdated.h in Headers */, 4ABB269324F7F9060061BF55 /* SDLTimer.h in Headers */, - 4ABED25C257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.h in Headers */, + 4ABED25C257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.h in Headers */, 4A8BD2F924F93872000945E3 /* SDLVehicleDataType.h in Headers */, 4ABB279924F7FF0B0061BF55 /* SDLLanguage.h in Headers */, 4ABB285F24F828E00061BF55 /* SDLVehicleDataActiveStatus.h in Headers */, @@ -7745,7 +7728,7 @@ }; 5D61FA1B1A84237100846EE7 = { CreatedOnToolsVersion = 6.1.1; - LastSwiftMigration = 1210; + LastSwiftMigration = 1210; }; 5D61FA251A84237100846EE7 = { CreatedOnToolsVersion = 6.1.1; @@ -7902,7 +7885,7 @@ 4ABB264524F7F5340061BF55 /* SDLSystemCapabilityManager.m in Sources */, 4A8BD2B124F935BC000945E3 /* SDLSoftButtonCapabilities.m in Sources */, 4ABB2A5524F847B10061BF55 /* SDLGetInteriorVehicleDataConsentResponse.m in Sources */, - 4ABED25B257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.m in Sources */, + 4ABED25B257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.m in Sources */, 4ABB2AA924F847F40061BF55 /* SDLSetAppIconResponse.m in Sources */, 4A8BD2D224F93803000945E3 /* SDLTemplateColorScheme.m in Sources */, 4A8BD3C124F994D5000945E3 /* SDLFileManager.m in Sources */, @@ -7996,7 +7979,6 @@ 4ABB2B9E24F850AE0061BF55 /* SDLImage.m in Sources */, 4A40254324FFDA660080E159 /* SDLTextAndGraphicUpdateOperation.m in Sources */, 4ABB29C124F845DB0061BF55 /* SDLShow.m in Sources */, - 4A93894625B8CC5F0069F438 /* SDLMenuReplaceStaticOperation.m in Sources */, 4ABB2BA524F850AE0061BF55 /* SDLLightControlData.m in Sources */, 4ABB272724F7FCAE0061BF55 /* SDLDeliveryMode.m in Sources */, 4ABB295F24F844020061BF55 /* SDLGetSystemCapability.m in Sources */, @@ -8017,7 +7999,7 @@ 4ABB28D624F82A6A0061BF55 /* SDLOnAudioPassThru.m in Sources */, 4A8BD25224F93135000945E3 /* SDLKeyboardProperties.m in Sources */, 4ABB2B1324F84D950061BF55 /* SDLAppServiceRecord.m in Sources */, - B3838A29257C5CE600420C11 /* SDLDoorStatusType.m in Sources */, + B3838A29257C5CE600420C11 /* SDLDoorStatusType.m in Sources */, 4ABB290424F82BE90061BF55 /* SDLAlertManeuver.m in Sources */, 4ABB27B224F7FFDA0061BF55 /* SDLMassageMode.m in Sources */, 4ABB274F24F7FD9C0061BF55 /* SDLECallConfirmationStatus.m in Sources */, @@ -8165,7 +8147,7 @@ 4A8BD24C24F93135000945E3 /* SDLLocationDetails.m in Sources */, 4ABB275C24F7FE1F0061BF55 /* SDLFuelType.m in Sources */, 4ABB2B4324F84EF50061BF55 /* SDLDisplayCapabilities.m in Sources */, - B3838A3A257C6AB700420C11 /* SDLDoorStatus.m in Sources */, + B3838A3A257C6AB700420C11 /* SDLDoorStatus.m in Sources */, 4ABB267724F7F6720061BF55 /* SDLObjectWithPriority.m in Sources */, 4ABB29FE24F8477F0061BF55 /* SDLAddCommandResponse.m in Sources */, 4ABB251324F7E3A30061BF55 /* SDLLifecycleConfiguration.m in Sources */, @@ -8324,7 +8306,7 @@ 4ABB2A5B24F847B10061BF55 /* SDLGetCloudAppPropertiesResponse.m in Sources */, 4ABB25F424F7E7EF0061BF55 /* SDLTouch.m in Sources */, 4ABB2B3E24F84EF50061BF55 /* SDLDeviceStatus.m in Sources */, - B3838A21257C5BB000420C11 /* SDLRoofStatus.m in Sources */, + B3838A21257C5BB000420C11 /* SDLRoofStatus.m in Sources */, 4ABB28BE24F82A6A0061BF55 /* SDLOnSyncPData.m in Sources */, 4ABB275724F7FD9C0061BF55 /* SDLEmergencyEventType.m in Sources */, 4ABB29B824F845DB0061BF55 /* SDLSetMediaClockTimer.m in Sources */, @@ -8390,7 +8372,7 @@ 4ABB2AA224F847F40061BF55 /* SDLSetCloudAppPropertiesResponse.m in Sources */, 4A8BD3A224F9474B000945E3 /* SDLIAPDataSession.m in Sources */, 4A8BD3B624F98F64000945E3 /* SDLOnUpdateSubMenu.m in Sources */, - B3838A30257C5D1B00420C11 /* SDLGateStatus.m in Sources */, + B3838A30257C5D1B00420C11 /* SDLGateStatus.m in Sources */, 4ABB256624F7E5B80061BF55 /* SDLRPCPermissionStatus.m in Sources */, 4ABB299824F845440061BF55 /* SDLReleaseInteriorVehicleDataModule.m in Sources */, 4ABB277724F7FE910061BF55 /* SDLIgnitionStatus.m in Sources */, @@ -8508,7 +8490,7 @@ 162E82E31A9BDE8B00906325 /* SDLIgnitionStatusSpec.m in Sources */, 162E83511A9BDE8B00906325 /* SDLDeleteInteractionChoiceSetResponseSpec.m in Sources */, DA9F7EB41DCC086400ACAE48 /* SDLDateTimeSpec.m in Sources */, - B3838A0F257C4EE100420C11 /* SDLGateStatusSpec.m in Sources */, + B3838A0F257C4EE100420C11 /* SDLGateStatusSpec.m in Sources */, 162E82E41A9BDE8B00906325 /* SDLImageFieldNameSpec.m in Sources */, 162E82ED1A9BDE8B00906325 /* SDLMaintenanceModeStatusSpec.m in Sources */, 8B9376DB1F33656C009605C4 /* SDLMetadataTagsSpec.m in Sources */, @@ -8521,7 +8503,7 @@ 162E83181A9BDE8B00906325 /* SDLOnKeyboardInputSpec.m in Sources */, 1EE8C4441F34A1B900FDC2CF /* SDLClimateControlDataSpec.m in Sources */, 162E83701A9BDE8B00906325 /* SDLUpdateTurnListResponseSpec.m in Sources */, - 4A41430D255F0A090039C267 /* TestConnectionRequestObject.m in Sources */, + 4A41430D255F0A090039C267 /* TestConnectionRequestObject.m in Sources */, 88C23E8822297C6000EA171F /* SDLRPCResponseNotificationSpec.m in Sources */, 162E833B1A9BDE8B00906325 /* SDLSetGlobalPropertiesSpec.m in Sources */, 884AF94C220B3FCC00E22928 /* SDLGetSystemCapabilitySpec.m in Sources */, @@ -8632,7 +8614,7 @@ 162E82F71A9BDE8B00906325 /* SDLResultSpec.m in Sources */, 88DDD0F9229ECA57002F9623 /* SDLIAPConstantsSpec.m in Sources */, 1680B1141A9CD7AD00DBD79E /* SDLV1ProtocolHeaderSpec.m in Sources */, - B3838A15257C4EFD00420C11 /* SDLRoofStatusSpec.m in Sources */, + B3838A15257C4EFD00420C11 /* SDLRoofStatusSpec.m in Sources */, 880D2680220E038800B3F496 /* SDLWeatherServiceManifestSpec.m in Sources */, 88EEC5BE220A3B8B005AA2F9 /* SDLPublishAppServiceResponseSpec.m in Sources */, 1680B1161A9CD7AD00DBD79E /* SDLProtocolMessageSpec.m in Sources */, @@ -8698,7 +8680,7 @@ 1EAA47762036B847000FE74B /* SDLEqualizerSettingsSpec.m in Sources */, 752ECDB9228C42E100D945F4 /* SDLMenuRunScoreSpec.m in Sources */, 162E83141A9BDE8B00906325 /* SDLOnDriverDistractionSpec.m in Sources */, - B3838A09257C4EB400420C11 /* SDLDoorStatusSpec.m in Sources */, + B3838A09257C4EB400420C11 /* SDLDoorStatusSpec.m in Sources */, 162E83371A9BDE8B00906325 /* SDLResetGlobalPropertiesSpec.m in Sources */, 162E82DF1A9BDE8B00906325 /* SDLGlobalProperySpec.m in Sources */, 88DF998F22035D1700477AC1 /* SDLIAPSessionSpec.m in Sources */, @@ -8902,7 +8884,7 @@ 1EAA47782036BA74000FE74B /* SDLAudioControlCapabilitiesSpec.m in Sources */, 5DB1BCD51D243A8E002FFC37 /* SDLUploadFileOperationSpec.m in Sources */, 162E83401A9BDE8B00906325 /* SDLSpeakSpec.m in Sources */, - B3838A01257C47FD00420C11 /* SDLDoorStatusTypeSpec.m in Sources */, + B3838A01257C47FD00420C11 /* SDLDoorStatusTypeSpec.m in Sources */, 88A5E7F4220B57F900495E8A /* SDLOnSystemCapabilityUpdatedSpec.m in Sources */, 5DCF76FC1ACDDB4200BB647B /* SDLSendLocationSpec.m in Sources */, 5DB1BCD81D243AA6002FFC37 /* SDLPermissionFilterSpec.m in Sources */, @@ -8923,7 +8905,7 @@ 162E831E1A9BDE8B00906325 /* SDLOnTBTClientStateSpec.m in Sources */, 162E83351A9BDE8B00906325 /* SDLReadDIDSpec.m in Sources */, 5DF40B28208FDA9700DD6FDA /* SDLVoiceCommandManagerSpec.m in Sources */, - 4AD1F1742559957100637FE1 /* SDLVoiceCommandUpdateOperationSpec.m in Sources */, + 4AD1F1742559957100637FE1 /* SDLVoiceCommandUpdateOperationSpec.m in Sources */, 88B3BFA020DA8FD000943565 /* SDLFuelTypeSpec.m in Sources */, 162E836F1A9BDE8B00906325 /* SDLUnsubscribeVehicleDataResponseSpec.m in Sources */, 162E82DB1A9BDE8B00906325 /* SDLECallConfirmationStatusSpec.m in Sources */, @@ -9337,7 +9319,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - VALIDATE_WORKSPACE = NO; + VALIDATE_WORKSPACE = NO; }; name = Debug; }; @@ -9370,7 +9352,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.smartdevicelink.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - VALIDATE_WORKSPACE = NO; + VALIDATE_WORKSPACE = NO; }; name = Release; }; diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index d070acdb3..0ffc99293 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -29,7 +29,6 @@ #import "SDLMenuManagerPrivateConstants.h" #import "SDLMenuParams.h" #import "SDLMenuReplaceOperation.h" -#import "SDLMenuReplaceStaticOperation.h" #import "SDLMenuShowOperation.h" #import "SDLOnCommand.h" #import "SDLOnHMIStatus.h" @@ -210,14 +209,29 @@ - (void)setMenuCells:(NSArray *)menuCells { } [self sdl_updateIdsOnMenuCells:menuCells parentId:ParentIdNotFound]; - _menuCells = menuCells; - if ([self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) { - [self sdl_startDynamicMenuUpdate]; - } else { - [self sdl_startStaticMenuUpdate]; + __weak typeof(self) weakself = self; + SDLMenuReplaceOperation *menuReplaceOperation = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells compatibilityModeEnabled:(![self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) currentMenuUpdatedBlock:^(NSArray * _Nonnull currentMenuCells) { + weakself.currentMenuCells = currentMenuCells; + [weakself sdl_updateMenuReplaceOperationsWithNewCurrentMenu]; + }]; + + __weak typeof(menuReplaceOperation) weakOp = menuReplaceOperation; + menuReplaceOperation.completionBlock = ^{ + if (weakOp.error != nil) { + SDLLogE(@"Updating menu dynamically failed with error: %@", weakOp.error); + } + }; + + // Cancel previous replace menu operations + for (NSOperation *operation in self.transactionQueue.operations) { + if ([operation isMemberOfClass:[SDLMenuReplaceOperation class]]) { + [operation cancel]; + } } + + [self.transactionQueue addOperation:menuReplaceOperation]; } #pragma mark - Open Menu @@ -258,64 +272,9 @@ - (BOOL)openMenu:(nullable SDLMenuCell *)cell { #pragma mark - Updating System -- (void)sdl_startDynamicMenuUpdate { - __weak typeof(self) weakself = self; - SDLMenuReplaceOperation *menuReplaceOperation = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells currentMenuUpdatedBlock:^(NSArray * _Nonnull currentMenuCells) { - weakself.currentMenuCells = currentMenuCells; - [weakself sdl_updateMenuReplaceOperationsWithNewCurrentMenu]; - }]; - - __weak typeof(menuReplaceOperation) weakOp = menuReplaceOperation; - menuReplaceOperation.completionBlock = ^{ - if (weakOp.error != nil) { - SDLLogE(@"Updating menu dynamically failed with error: %@", weakOp.error); - } - }; - - // Cancel previous replace menu operations - for (NSOperation *operation in self.transactionQueue.operations) { - if ([operation isMemberOfClass:[SDLMenuReplaceStaticOperation class]] - || [operation isMemberOfClass:[SDLMenuReplaceOperation class]]) { - [operation cancel]; - } - } - - [self.transactionQueue addOperation:menuReplaceOperation]; -} - -- (void)sdl_startStaticMenuUpdate { - __weak typeof(self) weakself = self; - SDLMenuReplaceStaticOperation *menuReplaceOperation = [[SDLMenuReplaceStaticOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells currentMenuUpdatedBlock:^(NSArray * _Nonnull currentMenuCells) { - weakself.currentMenuCells = currentMenuCells; - [weakself sdl_updateMenuReplaceOperationsWithNewCurrentMenu]; - }]; - - __weak typeof(menuReplaceOperation) weakOp = menuReplaceOperation; - menuReplaceOperation.completionBlock = ^{ - if (weakOp.error != nil) { - SDLLogE(@"Updating menu statically failed with error: %@", weakOp.error); - } - }; - - // TODO: Update menu config, and window capability - - // Cancel previous replace menu operations - for (NSOperation *operation in self.transactionQueue.operations) { - if ([operation isMemberOfClass:[SDLMenuReplaceStaticOperation class]] - || [operation isMemberOfClass:[SDLMenuReplaceOperation class]]) { - [operation cancel]; - } - } - - [self.transactionQueue addOperation:menuReplaceOperation]; -} - - (void)sdl_updateMenuReplaceOperationsWithNewCurrentMenu { for (NSOperation *operation in self.transactionQueue.operations) { - if ([operation isMemberOfClass:[SDLMenuReplaceStaticOperation class]]) { - SDLMenuReplaceStaticOperation *op = (SDLMenuReplaceStaticOperation *)operation; - op.currentMenu = self.currentMenuCells; - } else if ([operation isMemberOfClass:[SDLMenuReplaceOperation class]]) { + if ([operation isMemberOfClass:[SDLMenuReplaceOperation class]]) { SDLMenuReplaceOperation *op = (SDLMenuReplaceOperation *)operation; op.currentMenu = self.currentMenuCells; } diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 95ab5a6c8..3b48be2a8 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -313,7 +313,7 @@ - (void)sdl_transferCellIDFromOldCells:(NSArray *)oldCells toKept - (NSArray *)sdl_buildAllDeleteStatusesForMenu:(NSArray *)menuCells { NSMutableArray *mutableNumbers = [NSMutableArray arrayWithCapacity:menuCells.count]; - for (SDLMenuCell *cell in menuCells) { + for (int i = 0; i < menuCells.count; i++) { [mutableNumbers addObject:@(MenuCellStateDelete)]; } @@ -322,8 +322,8 @@ - (void)sdl_transferCellIDFromOldCells:(NSArray *)oldCells toKept - (NSArray *)sdl_buildAllAddStatusesForMenu:(NSArray *)menuCells { NSMutableArray *mutableNumbers = [NSMutableArray arrayWithCapacity:menuCells.count]; - for (SDLMenuCell *cell in menuCells) { - [mutableNumbers addObject:@(MenuCellStateAdd)]; + for (int i = 0; i < menuCells.count; i++) { + [mutableNumbers addObject:@(MenuCellStateDelete)]; } return [mutableNumbers copy]; diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h deleted file mode 100644 index 3e0fb1368..000000000 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// SDLMenuReplaceStaticOperation.h -// SmartDeviceLink -// -// Created by Joel Fischer on 1/20/21. -// Copyright © 2021 smartdevicelink. All rights reserved. -// - -#import "SDLAsynchronousOperation.h" - -#import "SDLMenuReplaceUtilities.h" - -@protocol SDLConnectionManagerType; - -@class SDLFileManager; -@class SDLMenuCell; -@class SDLMenuConfiguration; -@class SDLWindowCapability; - -NS_ASSUME_NONNULL_BEGIN - -@interface SDLMenuReplaceStaticOperation : SDLAsynchronousOperation - -@property (strong, nonatomic) SDLWindowCapability *windowCapability; -@property (strong, nonatomic) SDLMenuConfiguration *menuConfiguration; -@property (strong, nonatomic) NSArray *currentMenu; - -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu currentMenuUpdatedBlock:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m b/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m deleted file mode 100644 index 2f75c7a13..000000000 --- a/SmartDeviceLink/private/SDLMenuReplaceStaticOperation.m +++ /dev/null @@ -1,220 +0,0 @@ -// -// SDLMenuReplaceStaticOperation.m -// SmartDeviceLink -// -// Created by Joel Fischer on 1/20/21. -// Copyright © 2021 smartdevicelink. All rights reserved. -// - -#import "SDLMenuReplaceStaticOperation.h" - -#import "SDLAddCommand.h" -#import "SDLAddSubmenu.h" -#import "SDLArtwork.h" -#import "SDLConnectionManagerType.h" -#import "SDLDeleteCommand.h" -#import "SDLDeleteSubMenu.h" -#import "SDLError.h" -#import "SDLFileManager.h" -#import "SDLLogMacros.h" -#import "SDLMenuCell.h" -#import "SDLMenuConfiguration.h" -#import "SDLTextFieldName.h" -#import "SDLWindowCapability.h" -#import "SDLWindowCapability+ScreenManagerExtensions.h" - -NS_ASSUME_NONNULL_BEGIN - -typedef void(^SDLMenuUpdateCompletionHandler)(NSError *__nullable error); - -@interface SDLMenuCell() - -@property (assign, nonatomic) UInt32 parentCellId; -@property (assign, nonatomic) UInt32 cellId; - -@end - -@interface SDLMenuReplaceStaticOperation () - -@property (weak, nonatomic) id connectionManager; -@property (weak, nonatomic) SDLFileManager *fileManager; -@property (strong, nonatomic) NSArray *updatedMenu; -@property (assign, nonatomic) SDLCurrentMenuUpdatedBlock currentMenuUpdatedBlock; - -@property (strong, nonatomic) NSMutableArray *mutableCurrentMenu; -@property (copy, nonatomic, nullable) NSError *internalError; - -@end - -@implementation SDLMenuReplaceStaticOperation - -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu currentMenuUpdatedBlock:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock { - self = [super init]; - if (!self) { return nil; } - - _connectionManager = connectionManager; - _fileManager = fileManager; - _windowCapability = windowCapability; - _menuConfiguration = menuConfiguration; - _mutableCurrentMenu = [currentMenu mutableCopy]; - _updatedMenu = updatedMenu; - _currentMenuUpdatedBlock = currentMenuUpdatedBlock; - - return self; -} - -- (void)start { - [super start]; - if (self.isCancelled) { return; } - - __weak typeof(self) weakself = self; - NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; - if (artworksToBeUploaded.count > 0) { - [self.fileManager uploadArtworks:artworksToBeUploaded progressHandler:^BOOL(NSString * _Nonnull artworkName, float uploadPercentage, NSError * _Nullable error) { - if (weakself.isCancelled) { - [weakself finishOperation]; - return NO; - } - - return YES; - } completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { - if (weakself.isCancelled) { return; } - if (error != nil) { SDLLogE(@"Error uploading menu artworks: %@", error); } - - SDLLogD(@"All menu artworks uploaded"); - [self sdl_updateMenuWithCellsToDelete:self.currentMenu cellsToAdd:self.updatedMenu]; - }]; - } else { - // Cells have no artwork to load - [self sdl_updateMenuWithCellsToDelete:self.currentMenu cellsToAdd:self.updatedMenu]; - } -} - -#pragma mark - Private Helpers - -#pragma mark Sending Items - -- (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells { - __weak typeof(self) weakself = self; - [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { - if (self.isCancelled) { return [weakself finishOperation]; } - - [weakself sdl_sendNewMenuCells:addCells withCompletionHandler:^(NSError * _Nullable error) { - [weakself finishOperation]; - }]; - }]; -} - -- (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { - if (deleteMenuCells.count == 0) { - return completionHandler(nil); - } - - NSArray *deleteMenuCommands = [SDLMenuReplaceUtilities deleteCommandsForCells:deleteMenuCells]; - __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; - [self.connectionManager sendRequests:deleteMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { - if (error != nil) { - errors[request] = error; - } else { - // Find the id of the successful request and remove it from the current menu list whereever it may have been - UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; - [SDLMenuReplaceUtilities removeMenuCellFromList:self.mutableCurrentMenu withCmdId:commandId]; - } - } completionHandler:^(BOOL success) { - if (!success) { - SDLLogE(@"Unable to delete all old menu commands with errors: %@", errors); - completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); - } else { - SDLLogD(@"Finished deleting old menu"); - completionHandler(nil); - } - }]; -} - -- (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { - if (self.updatedMenu.count == 0 || newMenuCells.count == 0) { - SDLLogD(@"There are no cells to update."); - return completionHandler(nil); - } - - NSArray *mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells fileManager:self.fileManager usingIndexesFrom:self.updatedMenu windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; - NSArray *subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells fileManager:self.fileManager windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; - - __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; - __weak typeof(self) weakSelf = self; - [self.connectionManager sendRequests:mainMenuCommands progressHandler:^void(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { - SDLLogV(@"Updating main menu commands, percent complete: %.01f", percentComplete * 100); - if (error != nil) { - errors[request] = error; - } else { - // Find the id of the successful request and add it from the current menu list whereever it needs to be - UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; - UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; - [SDLMenuReplaceUtilities addMenuRequestWithCommandId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; - } - } completionHandler:^(BOOL success) { - if (!success) { - SDLLogE(@"Failed to send one or more main menu commands: %@", errors); - return completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); - } - - if (self.isCancelled) { return [weakSelf finishOperation]; } - - [weakSelf.connectionManager sendRequests:subMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { - if (error != nil) { - errors[request] = error; - } else { - // Find the id of the successful request and add it from the current menu list whereever it needs to be - UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; - UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; - [SDLMenuReplaceUtilities addMenuRequestWithCommandId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; - } - } completionHandler:^(BOOL success) { - if (!success) { - SDLLogE(@"Failed to send one or more sub menu commands: %@", errors); - completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); - return; - } - - SDLLogD(@"Finished updating menu"); - completionHandler(nil); - }]; - }]; -} - -#pragma mark - Getter / Setters - -- (void)setCurrentMenu:(NSArray *)currentMenu { - _mutableCurrentMenu = [currentMenu mutableCopy]; -} - -- (NSArray *)currentMenu { - return [_mutableCurrentMenu copy]; -} - -#pragma mark - Operation Overrides - -- (void)finishOperation { - SDLLogV(@"Finishing menu manager static replace operation"); - if (self.isCancelled) { - self.internalError = [NSError sdl_menuManager_replaceOperationCancelled]; - } - - [super finishOperation]; -} - -- (nullable NSString *)name { - return @"com.sdl.menuManager.replaceMenu.static"; -} - -- (NSOperationQueuePriority)queuePriority { - return NSOperationQueuePriorityNormal; -} - -- (nullable NSError *)error { - return self.internalError; -} - -@end - -NS_ASSUME_NONNULL_END From 0960312c0d0fd03e40a75286d7d3e3013152f8e8 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 8 Feb 2021 10:05:24 -0500 Subject: [PATCH 025/112] Update window capability properly --- SmartDeviceLink/private/SDLMenuManager.m | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 0ffc99293..3f0c8cb4a 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -281,6 +281,15 @@ - (void)sdl_updateMenuReplaceOperationsWithNewCurrentMenu { } } +- (void)sdl_updateMenuReplaceOperationsWithNewWindowCapability { + for (NSOperation *operation in self.transactionQueue.operations) { + if ([operation isMemberOfClass:[SDLMenuReplaceOperation class]]) { + SDLMenuReplaceOperation *op = (SDLMenuReplaceOperation *)operation; + op.windowCapability = self.windowCapability; + } + } +} + #pragma mark - Helpers - (BOOL)sdl_isDynamicMenuUpdateActive:(SDLDynamicMenuUpdatesMode)dynamicMenuUpdatesMode { @@ -333,13 +342,13 @@ - (BOOL)sdl_callHandlerForCells:(NSArray *)cells command:(SDLOnCo - (void)sdl_commandNotification:(SDLRPCNotificationNotification *)notification { SDLOnCommand *onCommand = (SDLOnCommand *)notification.notification; - [self sdl_callHandlerForCells:self.menuCells command:onCommand]; } - (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_updateMenuReplaceOperationsWithNewWindowCapability]; } - (void)sdl_hmiStatusNotification:(SDLRPCNotificationNotification *)notification { From 3ae329f09de6bb25dc2bd8434b61de855623533b Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 8 Feb 2021 11:02:11 -0500 Subject: [PATCH 026/112] Update replace ops with new menu configuration --- .../SDLMenuConfigurationUpdateOperation.h | 5 ++- .../SDLMenuConfigurationUpdateOperation.m | 8 ++++- SmartDeviceLink/private/SDLMenuManager.m | 31 ++++++++++--------- .../private/SDLMenuReplaceOperation.h | 2 +- .../private/SDLMenuReplaceOperation.m | 2 +- .../private/SDLMenuReplaceUtilities.h | 1 + 6 files changed, 31 insertions(+), 18 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h index 21fd78d70..27a220149 100644 --- a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h @@ -10,13 +10,16 @@ #import "SDLConnectionManagerType.h" #import "SDLMenuConfiguration.h" +#import "SDLMenuReplaceUtilities.h" #import "SDLWindowCapability.h" NS_ASSUME_NONNULL_BEGIN +typedef void(^SDLMenuConfigurationUpdatedBlock)(SDLMenuConfiguration *newMenuConfiguration); + @interface SDLMenuConfigurationUpdateOperation : SDLAsynchronousOperation -- (instancetype)initWithConnectionManager:(id)connectionManager windowCapability:(SDLWindowCapability *)windowCapability newMenuConfiguration:(SDLMenuConfiguration *)newConfiguration; +- (instancetype)initWithConnectionManager:(id)connectionManager windowCapability:(SDLWindowCapability *)windowCapability newMenuConfiguration:(SDLMenuConfiguration *)newConfiguration configurationUpdatedHandler:(SDLMenuConfigurationUpdatedBlock)configurationUpdatedBlock; @end diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m index 666161d03..178446bf7 100644 --- a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m @@ -21,6 +21,7 @@ @interface SDLMenuConfigurationUpdateOperation () @property (weak, nonatomic) id connectionManager; @property (strong, nonatomic) NSArray *availableMenuLayouts; @property (strong, nonatomic) SDLMenuConfiguration *updatedMenuConfiguration; +@property (assign, nonatomic) SDLMenuConfigurationUpdatedBlock menuConfigurationUpdatedBlock; @property (copy, nonatomic, nullable) NSError *internalError; @@ -28,13 +29,14 @@ @interface SDLMenuConfigurationUpdateOperation () @implementation SDLMenuConfigurationUpdateOperation -- (instancetype)initWithConnectionManager:(id)connectionManager windowCapability:(SDLWindowCapability *)windowCapability newMenuConfiguration:(SDLMenuConfiguration *)newConfiguration { +- (instancetype)initWithConnectionManager:(id)connectionManager windowCapability:(SDLWindowCapability *)windowCapability newMenuConfiguration:(SDLMenuConfiguration *)newConfiguration configurationUpdatedHandler:(SDLMenuConfigurationUpdatedBlock)configurationUpdatedBlock { self = [super init]; if (!self) { return nil; } _connectionManager = connectionManager; _availableMenuLayouts = windowCapability.menuLayoutsAvailable; _updatedMenuConfiguration = newConfiguration; + _menuConfigurationUpdatedBlock = configurationUpdatedBlock; return self; } @@ -77,6 +79,10 @@ - (void)finishOperation { self.internalError = [NSError sdl_menuManager_configurationOperationCancelled]; } + if (self.internalError == nil) { + self.menuConfigurationUpdatedBlock(self.updatedMenuConfiguration); + } + [super finishOperation]; } diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 3f0c8cb4a..4e4a453e9 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -68,11 +68,10 @@ @interface SDLMenuManager() @property (copy, nonatomic, nullable) SDLHMILevel currentHMILevel; @property (copy, nonatomic, nullable) SDLSystemContext currentSystemContext; - -@property (assign, nonatomic) UInt32 lastMenuId; @property (copy, nonatomic) NSArray *currentMenuCells; +@property (strong, nonatomic, nullable) SDLMenuConfiguration *currentMenuConfiguration; -@property (strong, nonatomic, nullable) SDLMenuConfiguration *oldMenuConfiguration; +@property (assign, nonatomic) UInt32 lastMenuId; @end @@ -154,24 +153,19 @@ - (void)setMenuConfiguration:(SDLMenuConfiguration *)menuConfiguration { return; } - // Keep the old configuration so that we can reset it if necessary - self.oldMenuConfiguration = self.menuConfiguration; - // Create the operation - SDLMenuConfigurationUpdateOperation *configurationUpdateOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:self.connectionManager windowCapability:self.windowCapability newMenuConfiguration:menuConfiguration]; - __weak typeof(self) weakself = self; + SDLMenuConfigurationUpdateOperation *configurationUpdateOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:self.connectionManager windowCapability:self.windowCapability newMenuConfiguration:menuConfiguration configurationUpdatedHandler:^(SDLMenuConfiguration * _Nonnull newMenuConfiguration) { + weakself.currentMenuConfiguration = newMenuConfiguration; + [weakself sdl_updateMenuReplaceOperationsWithNewMenuConfiguration]; + }]; + __weak typeof(configurationUpdateOp) weakOp = configurationUpdateOp; configurationUpdateOp.completionBlock = ^{ __strong typeof(weakself) strongself = weakself; if (weakOp.error != nil) { SDLLogE(@"Error setting new menu configuration with error: %@, info: %@. Will revert to old menu configuration: %@", weakOp.error, weakOp.error.userInfo, weakself.oldMenuConfiguration); - strongself->_menuConfiguration = strongself.oldMenuConfiguration; - } else { - strongself.oldMenuConfiguration = nil; } - - // TODO: Use custom completion block and update the replace operations with new menu configuration }; // Cancel previous menu configuration operations @@ -212,7 +206,7 @@ - (void)setMenuCells:(NSArray *)menuCells { _menuCells = menuCells; __weak typeof(self) weakself = self; - SDLMenuReplaceOperation *menuReplaceOperation = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells compatibilityModeEnabled:(![self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) currentMenuUpdatedBlock:^(NSArray * _Nonnull currentMenuCells) { + SDLMenuReplaceOperation *menuReplaceOperation = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells compatibilityModeEnabled:(![self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) currentMenuUpdatedHandler:^(NSArray * _Nonnull currentMenuCells) { weakself.currentMenuCells = currentMenuCells; [weakself sdl_updateMenuReplaceOperationsWithNewCurrentMenu]; }]; @@ -290,6 +284,15 @@ - (void)sdl_updateMenuReplaceOperationsWithNewWindowCapability { } } +- (void)sdl_updateMenuReplaceOperationsWithNewMenuConfiguration { + for (NSOperation *operation in self.transactionQueue.operations) { + if ([operation isMemberOfClass:[SDLMenuReplaceOperation class]]) { + SDLMenuReplaceOperation *op = (SDLMenuReplaceOperation *)operation; + op.menuConfiguration = self.currentMenuConfiguration; + } + } +} + #pragma mark - Helpers - (BOOL)sdl_isDynamicMenuUpdateActive:(SDLDynamicMenuUpdatesMode)dynamicMenuUpdatesMode { diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.h b/SmartDeviceLink/private/SDLMenuReplaceOperation.h index bbfa07b03..3e9c261e3 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.h +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.h @@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN @property (strong, nonatomic) SDLMenuConfiguration *menuConfiguration; @property (strong, nonatomic) NSArray *currentMenu; -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatbilityModeEnabled currentMenuUpdatedBlock:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock; +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatbilityModeEnabled currentMenuUpdatedHandler:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 3b48be2a8..cef9f6724 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -47,7 +47,7 @@ @interface SDLMenuReplaceOperation () @implementation SDLMenuReplaceOperation -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatbilityModeEnabled currentMenuUpdatedBlock:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock { +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatbilityModeEnabled currentMenuUpdatedHandler:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock { self = [super init]; if (!self) { return nil; } diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index f58ec3aa6..816cb3dd6 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -13,6 +13,7 @@ @class SDLArtwork; @class SDLFileManager; @class SDLMenuCell; +@class SDLMenuConfiguration; @class SDLRPCRequest; @class SDLWindowCapability; From 7074a359fc73d4ddab95f457e9cff035be461339 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 8 Feb 2021 16:10:49 -0500 Subject: [PATCH 027/112] In progress alignment --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 8 +++++ .../private/SDLDynamicMenuUpdateAlgorithm.m | 7 ++-- SmartDeviceLink/private/SDLMacros.h | 16 +++++++++ SmartDeviceLink/private/SDLMacros.m | 14 ++++++++ SmartDeviceLink/private/SDLMenuManager.m | 8 ----- .../private/SDLMenuReplaceOperation.m | 34 +++++++++---------- .../private/SDLMenuReplaceUtilities.m | 12 ++++--- SmartDeviceLink/public/SDLMenuCell.m | 16 ++++----- SmartDeviceLink/public/SDLMenuConfiguration.m | 20 +++++++++++ 9 files changed, 91 insertions(+), 44 deletions(-) create mode 100644 SmartDeviceLink/private/SDLMacros.h create mode 100644 SmartDeviceLink/private/SDLMacros.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 084f85177..6856ac6ed 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -519,6 +519,8 @@ 4A93895A25B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93895825B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m */; }; 4A93896625BB361C0069F438 /* SDLMenuReplaceUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93896425BB361C0069F438 /* SDLMenuReplaceUtilities.h */; }; 4A93896725BB361C0069F438 /* SDLMenuReplaceUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */; }; + 4A96113B25D1A0C600D787DA /* SDLMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A96113925D1A0C600D787DA /* SDLMacros.h */; }; + 4A96114825D1B5DA00D787DA /* SDLMacros.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A96114725D1B5DA00D787DA /* SDLMacros.m */; }; 4AAC0DBA25C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AAC0DB825C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h */; }; 4AAC0DBB25C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAC0DB925C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m */; }; 4AAC0DE025C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAC0DDF25C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m */; }; @@ -2337,6 +2339,8 @@ 4A93895825B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuConfigurationUpdateOperation.m; path = private/SDLMenuConfigurationUpdateOperation.m; sourceTree = ""; }; 4A93896425BB361C0069F438 /* SDLMenuReplaceUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuReplaceUtilities.h; path = private/SDLMenuReplaceUtilities.h; sourceTree = ""; }; 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceUtilities.m; path = private/SDLMenuReplaceUtilities.m; sourceTree = ""; }; + 4A96113925D1A0C600D787DA /* SDLMacros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMacros.h; path = private/SDLMacros.h; sourceTree = ""; }; + 4A96114725D1B5DA00D787DA /* SDLMacros.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMacros.m; path = private/SDLMacros.m; sourceTree = ""; }; 4AAC0DB825C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuManagerPrivateConstants.h; path = private/SDLMenuManagerPrivateConstants.h; sourceTree = ""; }; 4AAC0DB925C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuManagerPrivateConstants.m; path = private/SDLMenuManagerPrivateConstants.m; sourceTree = ""; }; 4AAC0DDF25C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceUtilitiesSpec.m; path = DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m; sourceTree = ""; }; @@ -6150,6 +6154,8 @@ 5DA3F35C1BC4484B0026F2D0 /* Notifications */, 5D6008871BE3ED470094A505 /* State Machine */, 4ABB24D824F594190061BF55 /* SDLConnectionManagerType.h */, + 4A96113925D1A0C600D787DA /* SDLMacros.h */, + 4A96114725D1B5DA00D787DA /* SDLMacros.m */, 4ABB24C824F593090061BF55 /* SDLStreamingProtocolDelegate.h */, 4ABB24D524F593ED0061BF55 /* SDLVersion.h */, 4ABB24D424F593EC0061BF55 /* SDLVersion.m */, @@ -7100,6 +7106,7 @@ 4ABB2B5024F84EF50061BF55 /* SDLClusterModeStatus.h in Headers */, 4ABB26CA24F7FAAF0061BF55 /* SDLEnum.h in Headers */, 4ABB251E24F7E3EC0061BF55 /* SDLLifecycleMobileHMIStateHandler.h in Headers */, + 4A96113B25D1A0C600D787DA /* SDLMacros.h in Headers */, 4ABB260724F7E9650061BF55 /* SDLStreamingMediaManagerDataSource.h in Headers */, 4ABB265224F7F58D0061BF55 /* SDLRPCRequestNotification.h in Headers */, 4ABB2A6424F847BB0061BF55 /* SDLListFilesResponse.h in Headers */, @@ -8234,6 +8241,7 @@ 4A8BD31124F938D6000945E3 /* SDLWeatherServiceManifest.m in Sources */, 4ABB271524F7FC4E0061BF55 /* SDLCompassDirection.m in Sources */, 4ABB254424F7E48D0061BF55 /* SDLLockScreenRootViewController.m in Sources */, + 4A96114825D1B5DA00D787DA /* SDLMacros.m in Sources */, 4A93895A25B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m in Sources */, 4ABB265F24F7F5F20061BF55 /* SDLNotificationDispatcher.m in Sources */, 4A8BD31624F938D6000945E3 /* SDLWindowTypeCapabilities.m in Sources */, diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m index 160c4ddda..4a17eb727 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m @@ -60,8 +60,7 @@ + (nullable SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)sta // As soon as we a run that requires 0 Adds we will use it since we cant do better then 0 if (numberOfAdds == 0) { - bestScore = [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:oldMenuStatus updatedStatus:newMenuStatus score:numberOfAdds]; - return bestScore; + return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:oldMenuStatus updatedStatus:newMenuStatus score:numberOfAdds]; } // if we havent create the bestScore object or if the current score beats the old score then we will create a new bestScore if (bestScore == nil || numberOfAdds < bestScore.score) { @@ -78,7 +77,7 @@ + (nullable SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)sta @param oldMenu The old menu array */ + (NSMutableArray *)sdl_buildAllDeleteStatusesforMenu:(NSArray *)oldMenu { - NSMutableArray *oldMenuStatus = [[NSMutableArray alloc] init]; + NSMutableArray *oldMenuStatus = [[NSMutableArray alloc] initWithCapacity:oldMenu.count]; for (NSUInteger index = 0; index < oldMenu.count; index++) { [oldMenuStatus addObject:@(MenuCellStateDelete)]; } @@ -91,7 +90,7 @@ + (nullable SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)sta @param newMenu The new menu array */ + (NSMutableArray *)sdl_buildAllAddStatusesForMenu:(NSArray *)newMenu { - NSMutableArray *newMenuStatus = [[NSMutableArray alloc] init]; + NSMutableArray *newMenuStatus = [[NSMutableArray alloc] initWithCapacity:newMenu.count]; for (NSUInteger index = 0; index < newMenu.count; index++) { [newMenuStatus addObject:@(MenuCellStateAdd)]; } diff --git a/SmartDeviceLink/private/SDLMacros.h b/SmartDeviceLink/private/SDLMacros.h new file mode 100644 index 000000000..fff4ccbdf --- /dev/null +++ b/SmartDeviceLink/private/SDLMacros.h @@ -0,0 +1,16 @@ +// +// SDLMacros.h +// SmartDeviceLink +// +// Created by Joel Fischer on 2/8/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +extern NSUInteger const NSUIntBitCell; +extern NSUInteger NSUIntRotateCell(NSUInteger val, NSUInteger howMuch); + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMacros.m b/SmartDeviceLink/private/SDLMacros.m new file mode 100644 index 000000000..ed92ea8e5 --- /dev/null +++ b/SmartDeviceLink/private/SDLMacros.m @@ -0,0 +1,14 @@ +// +// SDLMacros.m +// SmartDeviceLink +// +// Created by Joel Fischer on 2/8/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import "SDLMacros.h" + +NSUInteger const NSUIntBitCell = (CHAR_BIT * sizeof(NSUInteger)); +NSUInteger NSUIntRotateCell(NSUInteger val, NSUInteger howMuch) { + return ((((NSUInteger)val) << howMuch) | (((NSUInteger)val) >> (NSUIntBitCell - howMuch))); +} diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 4e4a453e9..50c39070b 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -160,14 +160,6 @@ - (void)setMenuConfiguration:(SDLMenuConfiguration *)menuConfiguration { [weakself sdl_updateMenuReplaceOperationsWithNewMenuConfiguration]; }]; - __weak typeof(configurationUpdateOp) weakOp = configurationUpdateOp; - configurationUpdateOp.completionBlock = ^{ - __strong typeof(weakself) strongself = weakself; - if (weakOp.error != nil) { - SDLLogE(@"Error setting new menu configuration with error: %@, info: %@. Will revert to old menu configuration: %@", weakOp.error, weakOp.error.userInfo, weakself.oldMenuConfiguration); - } - }; - // Cancel previous menu configuration operations for (NSOperation *operation in self.transactionQueue.operations) { if ([operation isMemberOfClass:[SDLMenuConfigurationUpdateOperation class]]) { diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index cef9f6724..5d7a04d34 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -69,19 +69,23 @@ - (void)start { SDLDynamicMenuUpdateRunScore *runScore = nil; if (self.compatbilityModeEnabled) { + SDLLogV(@"Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); runScore = [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[self sdl_buildAllDeleteStatusesForMenu:self.mutableCurrentMenu] updatedStatus:[self sdl_buildAllAddStatusesForMenu:self.updatedMenu] score:self.updatedMenu.count]; } else { + SDLLogV(@"Dynamic menu update active. Running the algorithm to find the best way to delete / add cells."); runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; } - NSArray *deleteMenuStatus = runScore.oldStatus; - NSArray *addMenuStatus = runScore.updatedStatus; + // If both old and new cells are empty, nothing needs to happen + if ((runScore.oldStatus.count == 0) && (runScore.updatedStatus.count == 0)) { + return [self finishOperation]; + } - NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:deleteMenuStatus]; - NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:addMenuStatus]; + NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:runScore.oldStatus]; + NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares - NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:deleteMenuStatus]; - NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:addMenuStatus]; + NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:runScore.oldStatus]; + NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; // Since we are creating a new Menu but keeping old cells we must firt transfer the old cellIDs to the new menus kept cells. [self sdl_transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; @@ -177,17 +181,14 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells /// @param deleteMenuCells The menu cells to be deleted /// @param completionHandler A handler called when the RPCs are finished with an error if any failed - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { - if (deleteMenuCells.count == 0) { - completionHandler(nil); - return; - } + if (deleteMenuCells.count == 0) { return completionHandler(nil); } __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; NSArray *deleteMenuCommands = [SDLMenuReplaceUtilities deleteCommandsForCells:deleteMenuCells]; [self.connectionManager sendRequests:deleteMenuCommands progressHandler:^void(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { if (error != nil) { errors[request] = error; - } else { + } else if (response.success.boolValue) { // Find the id of the successful request and remove it from the current menu list whereever it may have been UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; [SDLMenuReplaceUtilities removeMenuCellFromList:self.mutableCurrentMenu withCmdId:commandId]; @@ -209,9 +210,8 @@ - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuC /// @param completionHandler A handler called when the RPCs are finished with an error if any failed - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSArray *)oldMenu withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { if (self.updatedMenu.count == 0 || newMenuCells.count == 0) { - SDLLogD(@"There are no cells to update."); - completionHandler(nil); - return; + SDLLogV(@"There are no cells to update."); + return completionHandler(nil); } NSArray *mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells fileManager:self.fileManager usingIndexesFrom:self.updatedMenu windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout];; @@ -231,8 +231,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA } completionHandler:^(BOOL success) { if (!success) { SDLLogE(@"Failed to send main menu commands: %@", errors); - completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); - return; + return completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); } [weakSelf.connectionManager sendRequests:subMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { @@ -247,8 +246,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA } completionHandler:^(BOOL success) { if (!success) { SDLLogE(@"Failed to send sub menu commands: %@", errors); - completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); - return; + return completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); } SDLLogD(@"Finished updating menu"); diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 20f181d2a..9021fa56f 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -111,6 +111,7 @@ + (UInt16)positionForRPCRequest:(SDLRPCRequest *)request { } else { [mutableCommands addObject:[self sdl_commandForMenuCell:cells[updateCellsIndex] fileManager:fileManager windowCapability:windowCapability position:(UInt16)menuInteger]]; } + break; } } } @@ -206,8 +207,7 @@ + (BOOL)addMenuRequestWithCommandId:(UInt32)commandId position:(UInt16)position addedCell = cell; break; } else if (cell.subCells.count > 0) { - BOOL didAddCell = [self addMenuRequestWithCommandId:commandId position:position fromNewMenuList:cell.subCells toMainMenuList:mainMenuList]; - if (didAddCell) { return YES; } + return [self addMenuRequestWithCommandId:commandId position:position fromNewMenuList:cell.subCells toMainMenuList:mainMenuList]; } } @@ -243,11 +243,15 @@ + (BOOL)sdl_addMenuCell:(SDLMenuCell *)cell toList:(NSMutableArray *)cellList atPosition:(UInt16)position { + SDLMenuCell *cellToInsert = cell; + if (cellToInsert.subCells.count > 0) { + cellToInsert = [cell copy]; + cellToInsert.subCells = @[]; + } + if (position > cellList.count) { [cellList addObject:cell]; } else { diff --git a/SmartDeviceLink/public/SDLMenuCell.m b/SmartDeviceLink/public/SDLMenuCell.m index 9a06411bc..4655aee15 100644 --- a/SmartDeviceLink/public/SDLMenuCell.m +++ b/SmartDeviceLink/public/SDLMenuCell.m @@ -9,6 +9,7 @@ #import "SDLMenuCell.h" #import "SDLArtwork.h" +#import "SDLMacros.h" NS_ASSUME_NONNULL_BEGIN @@ -57,16 +58,11 @@ - (NSString *)description { #pragma mark - Object Equality -NSUInteger const NSUIntBitCell = (CHAR_BIT * sizeof(NSUInteger)); -NSUInteger NSUIntRotateCell(NSUInteger val, NSUInteger howMuch) { - return ((((NSUInteger)val) << howMuch) | (((NSUInteger)val) >> (NSUIntBitCell - howMuch))); -} - - (NSUInteger)hash { return NSUIntRotateCell(self.title.hash, NSUIntBitCell / 2) ^ NSUIntRotateCell(self.icon.name.hash, NSUIntBitCell / 3) ^ NSUIntRotateCell(self.voiceCommands.hash, NSUIntBitCell / 4) - ^ NSUIntRotateCell(self.subCells.count !=0, NSUIntBitCell / 5) + ^ NSUIntRotateCell((self.subCells.count != 0), NSUIntBitCell / 5) ^ NSUIntRotateCell(self.submenuLayout.hash, NSUIntBitCell / 6); } @@ -74,13 +70,13 @@ - (BOOL)isEqual:(id)object { if (self == object) { return YES; } if (![object isMemberOfClass:[self class]]) { return NO; } - return [self isEqualToChoice:(SDLMenuCell *)object]; + return [self isEqualToCell:(SDLMenuCell *)object]; } -- (BOOL)isEqualToChoice:(SDLMenuCell *)choice { - if (choice == nil) { return NO; } +- (BOOL)isEqualToCell:(SDLMenuCell *)cell { + if (cell == nil) { return NO; } - return (self.hash == choice.hash); + return (self.hash == cell.hash); } @end diff --git a/SmartDeviceLink/public/SDLMenuConfiguration.m b/SmartDeviceLink/public/SDLMenuConfiguration.m index 2049c83bf..b957d72ca 100644 --- a/SmartDeviceLink/public/SDLMenuConfiguration.m +++ b/SmartDeviceLink/public/SDLMenuConfiguration.m @@ -8,6 +8,8 @@ #import "SDLMenuConfiguration.h" +#import "SDLMacros.h" + @implementation SDLMenuConfiguration - (instancetype)init { @@ -24,6 +26,24 @@ - (instancetype)initWithMainMenuLayout:(SDLMenuLayout)mainMenuLayout defaultSubm return self; } +- (NSUInteger)hash { + return NSUIntRotateCell(self.mainMenuLayout.hash, NSUIntBitCell / 2) + ^ NSUIntRotateCell(self.defaultSubmenuLayout.hash, NSUIntBitCell / 3); +} + +- (BOOL)isEqual:(id)object { + if (self == object) { return YES; } + if (![object isMemberOfClass:[self class]]) { return NO; } + + return [self isEqualToConfiguration:(SDLMenuConfiguration *)object]; +} + +- (BOOL)isEqualToConfiguration:(SDLMenuConfiguration *)configuration { + if (configuration == nil) { return NO; } + + return (self.hash == configuration.hash); +} + - (NSString *)description { return [NSString stringWithFormat:@"Menu configuration, main menu layout: %@, submenu default layout: %@", _mainMenuLayout, _defaultSubmenuLayout]; } From 09cb1c9bce55c5891679a5491e28dc1e600fc675 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 10 Feb 2021 11:34:21 -0500 Subject: [PATCH 028/112] Update RPC spec --- generator/rpc_spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/rpc_spec b/generator/rpc_spec index 390394de3..1a8d4a28e 160000 --- a/generator/rpc_spec +++ b/generator/rpc_spec @@ -1 +1 @@ -Subproject commit 390394de357d3f097aef33ead9d17ef0f4c3d34c +Subproject commit 1a8d4a28ebdf4410a63e22ce7a1792d9e5cd7e45 From 7b41774429d7a48ccf4ae8dd61561e4b619338f2 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 10 Feb 2021 14:52:39 -0500 Subject: [PATCH 029/112] Alignment and fixes --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 2 +- .../xcschemes/SmartDeviceLink-Example-ObjC.xcscheme | 2 +- .../xcshareddata/xcschemes/SmartDeviceLink.xcscheme | 2 +- .../xcshareddata/xcschemes/SmartDeviceLinkSwift.xcscheme | 2 +- SmartDeviceLink/private/SDLMenuManager.m | 2 +- SmartDeviceLink/private/SDLMenuReplaceUtilities.m | 5 +++-- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index bf04d9f50..e883205ab 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -7729,7 +7729,7 @@ attributes = { CLASSPREFIX = SDL; LastSwiftUpdateCheck = 0710; - LastUpgradeCheck = 1210; + LastUpgradeCheck = 1240; ORGANIZATIONNAME = smartdevicelink; TargetAttributes = { 5D4019AE1A76EC350006B0C2 = { diff --git a/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme b/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme index 144ab0c4b..bc2631407 100644 --- a/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme +++ b/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme @@ -1,6 +1,6 @@ *)menuCells { __weak typeof(menuReplaceOperation) weakOp = menuReplaceOperation; menuReplaceOperation.completionBlock = ^{ if (weakOp.error != nil) { - SDLLogE(@"Updating menu dynamically failed with error: %@", weakOp.error); + SDLLogE(@"Updating menu failed with error: %@", weakOp.error); } }; diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 9021fa56f..7ef31fcfb 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -212,8 +212,7 @@ + (BOOL)addMenuRequestWithCommandId:(UInt32)commandId position:(UInt16)position } if (addedCell != nil) { - [self sdl_addMenuCell:addedCell toList:mainMenuList atPosition:position]; - return YES; + return [self sdl_addMenuCell:addedCell toList:mainMenuList atPosition:position]; } return NO; @@ -238,6 +237,8 @@ + (BOOL)sdl_addMenuCell:(SDLMenuCell *)cell toList:(NSMutableArray Date: Wed, 10 Feb 2021 15:46:56 -0500 Subject: [PATCH 030/112] Remove unneeded private property --- SmartDeviceLink/private/SDLMenuManager.m | 1 - .../DevAPISpecs/SDLMenuManagerSpec.m | 38 +++++++++---------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index a1440fc75..c5b377fbc 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -63,7 +63,6 @@ @interface SDLMenuManager() @property (weak, nonatomic) SDLSystemCapabilityManager *systemCapabilityManager; @property (strong, nonatomic) NSOperationQueue *transactionQueue; -@property (copy, nonatomic) NSArray *waitingUpdateMenuCells; @property (strong, nonatomic, nullable) SDLWindowCapability *windowCapability; @property (copy, nonatomic, nullable) SDLHMILevel currentHMILevel; diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index 98ea5d5bf..68629febf 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -22,20 +22,15 @@ @interface SDLMenuManager() @property (weak, nonatomic) SDLFileManager *fileManager; @property (weak, nonatomic) SDLSystemCapabilityManager *systemCapabilityManager; -@property (copy, nonatomic, nullable) SDLHMILevel currentHMILevel; -@property (copy, nonatomic, nullable) SDLSystemContext currentSystemContext; - - -@property (strong, nonatomic, nullable) NSArray *inProgressUpdate; -@property (assign, nonatomic) BOOL hasQueuedUpdate; -@property (assign, nonatomic) BOOL waitingOnHMIUpdate; -@property (copy, nonatomic) NSArray *waitingUpdateMenuCells; +@property (strong, nonatomic) NSOperationQueue *transactionQueue; @property (strong, nonatomic, nullable) SDLWindowCapability *windowCapability; -@property (assign, nonatomic) UInt32 lastMenuId; +@property (copy, nonatomic, nullable) SDLHMILevel currentHMILevel; +@property (copy, nonatomic, nullable) SDLSystemContext currentSystemContext; @property (copy, nonatomic) NSArray *currentMenuCells; +@property (strong, nonatomic, nullable) SDLMenuConfiguration *currentMenuConfiguration; -- (BOOL)sdl_shouldRPCsIncludeImages:(NSArray *)cells; +@property (assign, nonatomic) UInt32 lastMenuId; @end @@ -90,17 +85,18 @@ - (BOOL)sdl_shouldRPCsIncludeImages:(NSArray *)cells; it(@"should instantiate correctly", ^{ expect(testManager.menuCells).to(beEmpty()); + + expect(@(testManager.dynamicMenuUpdatesMode)).to(equal(@(SDLDynamicMenuUpdatesModeOnWithCompatibility))); expect(testManager.connectionManager).to(equal(mockConnectionManager)); expect(testManager.fileManager).to(equal(mockFileManager)); expect(testManager.systemCapabilityManager).to(equal(mockSystemCapabilityManager)); + expect(testManager.transactionQueue).toNot(beNil()); + expect(testManager.windowCapability).to(beNil()); expect(testManager.currentHMILevel).to(beNil()); - expect(testManager.inProgressUpdate).to(beNil()); - expect(testManager.hasQueuedUpdate).to(beFalse()); - expect(testManager.waitingOnHMIUpdate).to(beFalse()); - expect(testManager.lastMenuId).to(equal(1)); + expect(testManager.currentSystemContext).to(beNil()); expect(testManager.currentMenuCells).to(beEmpty()); - expect(testManager.waitingUpdateMenuCells).to(beNil()); - expect(testManager.menuConfiguration).toNot(beNil()); + expect(testManager.currentMenuConfiguration).to(beNil()); + expect(testManager.lastMenuId).to(equal(1)); }); describe(@"updating menu cells before HMI is ready", ^{ @@ -659,7 +655,7 @@ - (BOOL)sdl_shouldRPCsIncludeImages:(NSArray *)cells; }); it(@"should send showAppMenu RPC", ^{ - BOOL canSendRPC = [testManager openMenu]; + BOOL canSendRPC = [testManager openMenu:nil]; NSPredicate *showMenu = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]]; NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:showMenu]; @@ -674,7 +670,7 @@ - (BOOL)sdl_shouldRPCsIncludeImages:(NSArray *)cells; [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - BOOL canSendRPC = [testManager openSubmenu:submenuCell]; + BOOL canSendRPC = [testManager openMenu:submenuCell]; NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]]; NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; @@ -687,7 +683,7 @@ - (BOOL)sdl_shouldRPCsIncludeImages:(NSArray *)cells; context(@"when open menu RPC can not be sent", ^{ it(@"should not send a showAppMenu RPC when cell has no subcells", ^ { - BOOL canSendRPC = [testManager openSubmenu:textOnlyCell]; + BOOL canSendRPC = [testManager openMenu:textOnlyCell]; NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]]; NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; @@ -702,7 +698,7 @@ - (BOOL)sdl_shouldRPCsIncludeImages:(NSArray *)cells; id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]); OCMStub([globalMock rpcVersion]).andReturn(oldVersion); - BOOL canSendRPC = [testManager openSubmenu:submenuCell]; + BOOL canSendRPC = [testManager openMenu:submenuCell]; NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]]; NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; @@ -717,7 +713,7 @@ - (BOOL)sdl_shouldRPCsIncludeImages:(NSArray *)cells; id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]); OCMStub([globalMock rpcVersion]).andReturn(oldVersion); - BOOL canSendRPC = [testManager openSubmenu:submenuCell]; + BOOL canSendRPC = [testManager openMenu:submenuCell]; NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]]; NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; From 5ce749ebb6e97916119ca007995ce792ab5d797a Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 10 Feb 2021 16:05:48 -0500 Subject: [PATCH 031/112] Fixing crashers in menu tests --- .../DevAPISpecs/SDLMenuManagerSpec.m | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index 68629febf..c279e462c 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -107,7 +107,7 @@ @interface SDLMenuManager() }); it(@"should not update", ^{ - expect(mockConnectionManager.receivedRequests).to(beEmpty()); + expect(testManager.transactionQueue.operationCount).to(equal(0)); }); describe(@"when entering the foreground", ^{ @@ -121,7 +121,7 @@ @interface SDLMenuManager() }); it(@"should update", ^{ - expect(mockConnectionManager.receivedRequests).toNot(beEmpty()); + expect(testManager.transactionQueue.operationCount).to(equal(1)); }); }); }); @@ -626,18 +626,19 @@ @interface SDLMenuManager() }); it(@"should reset correctly", ^{ + expect(testManager.menuCells).to(beEmpty()); + + expect(@(testManager.dynamicMenuUpdatesMode)).to(equal(@(SDLDynamicMenuUpdatesModeOnWithCompatibility))); expect(testManager.connectionManager).to(equal(mockConnectionManager)); expect(testManager.fileManager).to(equal(mockFileManager)); - - expect(testManager.menuCells).to(beEmpty()); + expect(testManager.systemCapabilityManager).to(equal(mockSystemCapabilityManager)); + expect(testManager.transactionQueue).toNot(beNil()); + expect(testManager.windowCapability).to(beNil()); expect(testManager.currentHMILevel).to(beNil()); - expect(testManager.inProgressUpdate).to(beNil()); - expect(testManager.hasQueuedUpdate).to(beFalse()); - expect(testManager.waitingOnHMIUpdate).to(beFalse()); - expect(testManager.lastMenuId).to(equal(1)); + expect(testManager.currentSystemContext).to(beNil()); expect(testManager.currentMenuCells).to(beEmpty()); - expect(testManager.waitingUpdateMenuCells).to(beEmpty()); - expect(testManager.menuConfiguration).toNot(beNil()); + expect(testManager.currentMenuConfiguration).to(beNil()); + expect(testManager.lastMenuId).to(equal(1)); }); }); From c0c734f46b3c1d772f4bb4adeec64626eae7e10a Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 16 Feb 2021 15:51:34 -0500 Subject: [PATCH 032/112] Working on unit test refactor --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 20 + SmartDeviceLink/private/SDLMenuManager.m | 11 +- .../SDLMenuConfigurationUpdateOperationSpec.m | 32 ++ .../DevAPISpecs/SDLMenuManagerSpec.m | 426 ++---------------- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 369 +++++++++++++++ .../DevAPISpecs/SDLMenuShowOperationSpec.m | 19 + 6 files changed, 488 insertions(+), 389 deletions(-) create mode 100644 SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m create mode 100644 SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m create mode 100644 SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index e883205ab..52eafb2a9 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -1398,6 +1398,9 @@ 4ABB2BA724F850AE0061BF55 /* SDLImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ABB2B9924F850AD0061BF55 /* SDLImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4ABB2BA824F850AE0061BF55 /* SDLLightState.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABB2B9A24F850AD0061BF55 /* SDLLightState.m */; }; 4ABB2BA924F850AE0061BF55 /* SDLImageResolution.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ABB2B9B24F850AD0061BF55 /* SDLImageResolution.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4ABC1CA725DC4E1C00545AC6 /* SDLMenuReplaceOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABC1CA625DC4E1C00545AC6 /* SDLMenuReplaceOperationSpec.m */; }; + 4ABC1CAD25DC51A800545AC6 /* SDLMenuConfigurationUpdateOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABC1CAC25DC51A800545AC6 /* SDLMenuConfigurationUpdateOperationSpec.m */; }; + 4ABC1CB125DC520300545AC6 /* SDLMenuShowOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABC1CB025DC520300545AC6 /* SDLMenuShowOperationSpec.m */; }; 4ABED25B257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ABED259257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.m */; }; 4ABED25C257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ABED25A257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.h */; }; 4AD1F1742559957100637FE1 /* SDLVoiceCommandUpdateOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AD1F1732559957100637FE1 /* SDLVoiceCommandUpdateOperationSpec.m */; }; @@ -3224,6 +3227,9 @@ 4ABB2B9924F850AD0061BF55 /* SDLImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLImage.h; path = public/SDLImage.h; sourceTree = ""; }; 4ABB2B9A24F850AD0061BF55 /* SDLLightState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLLightState.m; path = public/SDLLightState.m; sourceTree = ""; }; 4ABB2B9B24F850AD0061BF55 /* SDLImageResolution.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLImageResolution.h; path = public/SDLImageResolution.h; sourceTree = ""; }; + 4ABC1CA625DC4E1C00545AC6 /* SDLMenuReplaceOperationSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceOperationSpec.m; path = DevAPISpecs/SDLMenuReplaceOperationSpec.m; sourceTree = ""; }; + 4ABC1CAC25DC51A800545AC6 /* SDLMenuConfigurationUpdateOperationSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuConfigurationUpdateOperationSpec.m; path = DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m; sourceTree = ""; }; + 4ABC1CB025DC520300545AC6 /* SDLMenuShowOperationSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuShowOperationSpec.m; path = DevAPISpecs/SDLMenuShowOperationSpec.m; sourceTree = ""; }; 4ABED259257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLVoiceCommandUpdateOperation.m; path = private/SDLVoiceCommandUpdateOperation.m; sourceTree = ""; }; 4ABED25A257681ED005BDF61 /* SDLVoiceCommandUpdateOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLVoiceCommandUpdateOperation.h; path = private/SDLVoiceCommandUpdateOperation.h; sourceTree = ""; }; 4AD1F1732559957100637FE1 /* SDLVoiceCommandUpdateOperationSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLVoiceCommandUpdateOperationSpec.m; path = DevAPISpecs/SDLVoiceCommandUpdateOperationSpec.m; sourceTree = ""; }; @@ -4379,6 +4385,16 @@ name = Helpers; sourceTree = ""; }; + 4ABC1CA525DC4DC200545AC6 /* Operations */ = { + isa = PBXGroup; + children = ( + 4ABC1CAC25DC51A800545AC6 /* SDLMenuConfigurationUpdateOperationSpec.m */, + 4ABC1CA625DC4E1C00545AC6 /* SDLMenuReplaceOperationSpec.m */, + 4ABC1CB025DC520300545AC6 /* SDLMenuShowOperationSpec.m */, + ); + name = Operations; + sourceTree = ""; + }; 4AD1F16A2559952D00637FE1 /* Voice Command */ = { isa = PBXGroup; children = ( @@ -6639,6 +6655,7 @@ 5DF40B24208FA7C500DD6FDA /* Menu */ = { isa = PBXGroup; children = ( + 4ABC1CA525DC4DC200545AC6 /* Operations */, 4AAC0DE525C493D800746D33 /* Helpers */, 5DAB5F502098994C00A020C8 /* SDLMenuCellSpec.m */, 5D76751522D920FD00E8D71A /* SDLMenuConfigurationSpec.m */, @@ -8503,6 +8520,7 @@ 5DBEFA581F436132009EE295 /* SDLFakeSecurityManager.m in Sources */, 9FA0D00022DF06A0009CF344 /* SDLWindowCapabilitySpec.m in Sources */, 162E82D91A9BDE8A00906325 /* SDLDisplayTypeSpec.m in Sources */, + 4ABC1CB125DC520300545AC6 /* SDLMenuShowOperationSpec.m in Sources */, 00EADD3522DFE5670088B608 /* SDLEncryptionConfigurationSpec.m in Sources */, 162E83871A9BDE8B00906325 /* SDLPermissionItemSpec.m in Sources */, 5DAB5F5320989A8300A020C8 /* SDLVoiceCommandSpec.m in Sources */, @@ -8848,6 +8866,7 @@ 162E836B1A9BDE8B00906325 /* SDLSyncPDataResponseSpec.m in Sources */, 8B7B31AF1F2FBA0200BDC38D /* SDLVideoStreamingCapabilitySpec.m in Sources */, 88FBF7C2250132C1005EA0A4 /* SDLOnLockScreenStatusSpec.m in Sources */, + 4ABC1CAD25DC51A800545AC6 /* SDLMenuConfigurationUpdateOperationSpec.m in Sources */, 8B05F88922DD011300666CD8 /* SDLUnpublishAppServiceSpec.m in Sources */, 162E839B1A9BDE8B00906325 /* SDLRPCNotificationSpec.m in Sources */, 162E83581A9BDE8B00906325 /* SDLGetVehicleDataResponseSpec.m in Sources */, @@ -8933,6 +8952,7 @@ 162E83731A9BDE8B00906325 /* SDLBeltStatusSpec.m in Sources */, 162E83551A9BDE8B00906325 /* SDLEndAudioPassThruResponseSpec.m in Sources */, 8881AFC12225EB9300EA870B /* SDLGetCloudAppPropertiesResponseSpec.m in Sources */, + 4ABC1CA725DC4E1C00545AC6 /* SDLMenuReplaceOperationSpec.m in Sources */, 000DD56E22EF01FC005AB7A7 /* SDLSeatLocationSpec.m in Sources */, 162E83251A9BDE8B00906325 /* SDLAlertSpec.m in Sources */, 2BF2F85220ED068200A26EF2 /* SDLAudioStreamingIndicatorSpec.m in Sources */, diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index c5b377fbc..de552b0b3 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -117,7 +117,9 @@ - (void)stop { _transactionQueue = [self sdl_newTransactionQueue]; _currentHMILevel = nil; - _currentSystemContext = SDLSystemContextMain; + _currentSystemContext = nil; + _currentMenuConfiguration = nil; + _windowCapability = nil; } #pragma mark Transaction Queue @@ -150,8 +152,13 @@ - (void)setMenuConfiguration:(SDLMenuConfiguration *)menuConfiguration { if (menuConfiguration == self.menuConfiguration) { SDLLogD(@"New menu configuration is equal to existing one, will not set new configuration"); return; + } else if ([[SDLGlobals sharedGlobals].rpcVersion isLessThanVersion:[SDLVersion versionWithMajor:6 minor:0 patch:0]]) { + SDLLogE(@"Setting a menu configuration is not supported on this head unit. Only supported on RPC 6.0+, this version: %@", [SDLGlobals sharedGlobals].rpcVersion); + return; } + _menuConfiguration = menuConfiguration; + // Create the operation __weak typeof(self) weakself = self; SDLMenuConfigurationUpdateOperation *configurationUpdateOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:self.connectionManager windowCapability:self.windowCapability newMenuConfiguration:menuConfiguration configurationUpdatedHandler:^(SDLMenuConfiguration * _Nonnull newMenuConfiguration) { @@ -229,7 +236,7 @@ - (BOOL)openMenu:(nullable SDLMenuCell *)cell { SDLLogE(@"This cell has not been sent to the head unit, so no submenu can be opened. Make sure that the cell exists in the SDLManager.menu array"); return NO; } else if ([SDLGlobals.sharedGlobals.rpcVersion isLessThanVersion:[[SDLVersion alloc] initWithMajor:6 minor:0 patch:0]]) { - SDLLogE(@"The openSubmenu method is not supported on this head unit."); + SDLLogE(@"The openMenu / openSubmenu is not supported on this head unit."); return NO; } diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m new file mode 100644 index 000000000..2c9192151 --- /dev/null +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m @@ -0,0 +1,32 @@ +// +// SDLMenuConfigurationUpdateOperationSpec.m +// SmartDeviceLinkTests +// +// Created by Joel Fischer on 2/16/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import +#import +#import + +#import +#import "SDLMenuConfigurationUpdateOperation.h" +#import "TestConnectionManager.h" + +QuickSpecBegin(SDLMenuConfigurationUpdateOperationSpec) + +describe(@"a menu configuration update operation", ^{ + __block SDLMenuConfigurationUpdateOperation *testOp = nil; + + __block TestConnectionManager *testConnectionManager = nil; + __block SDLFileManager *testFileManager = nil; + __block SDLWindowCapability *testWindowCapability = nil; + __block SDLMenuConfiguration *testMenuConfiguration = nil; + __block NSArray *testCurrentMenu = nil; + __block NSArray *testNewMenu = nil; + + +}); + +QuickSpecEnd diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index c279e462c..2df2bba2e 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -91,7 +91,7 @@ @interface SDLMenuManager() expect(testManager.fileManager).to(equal(mockFileManager)); expect(testManager.systemCapabilityManager).to(equal(mockSystemCapabilityManager)); expect(testManager.transactionQueue).toNot(beNil()); - expect(testManager.windowCapability).to(beNil()); + expect(testManager.windowCapability).toNot(beNil()); expect(testManager.currentHMILevel).to(beNil()); expect(testManager.currentSystemContext).to(beNil()); expect(testManager.currentMenuCells).to(beEmpty()); @@ -107,7 +107,8 @@ @interface SDLMenuManager() }); it(@"should not update", ^{ - expect(testManager.transactionQueue.operationCount).to(equal(0)); + expect(testManager.transactionQueue.isSuspended).to(beTrue()); + expect(testManager.transactionQueue.operationCount).to(equal(1)); }); describe(@"when entering the foreground", ^{ @@ -137,23 +138,13 @@ @interface SDLMenuManager() expect(testManager.menuConfiguration).toNot(equal(testMenuConfiguration)); }); - it(@"should not update the menu cells", ^{ - testManager.menuCells = @[textOnlyCell]; - expect(mockConnectionManager.receivedRequests).to(beEmpty()); - }); - }); + it(@"should not open the menu", ^{ - context(@"when in the menu", ^{ - beforeEach(^{ - [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"6.0.0"]; - testManager.currentHMILevel = SDLHMILevelFull; - testManager.currentSystemContext = SDLSystemContextMenu; }); - it(@"should update the menu configuration", ^{ - testManager.menuConfiguration = testMenuConfiguration; - expect(mockConnectionManager.receivedRequests).toNot(beEmpty()); - expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); + it(@"should not update the menu cells", ^{ + testManager.menuCells = @[textOnlyCell]; + expect(mockConnectionManager.receivedRequests).to(beEmpty()); }); }); }); @@ -180,343 +171,6 @@ @interface SDLMenuManager() expect(testManager.menuCells).to(beEmpty()); }); }); - - it(@"should check if all artworks are uploaded and return NO", ^{ - textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - testManager.menuCells = @[textAndImageCell, textOnlyCell]; - OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); - expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beFalse()); - }); - - it(@"should properly update a text cell", ^{ - testManager.menuCells = @[textOnlyCell]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - expect(deletes).to(beEmpty()); - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; - NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - expect(add).toNot(beEmpty()); - }); - - it(@"should properly update with subcells", ^{ - OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); - testManager.menuCells = @[submenuCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - NSPredicate *submenuCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenus = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:submenuCommandPredicate]; - - expect(adds).to(haveCount(2)); - expect(submenus).to(haveCount(1)); - }); - - describe(@"updating with an image", ^{ - context(@"when the image is already on the head unit", ^{ - beforeEach(^{ - OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES); - }); - - it(@"should check if all artworks are uploaded", ^{ - textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - testManager.menuCells = @[textAndImageCell, textOnlyCell]; - OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); - expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beTrue()); - }); - - it(@"should properly update an image cell", ^{ - testManager.menuCells = @[textAndImageCell, submenuImageCell]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; - NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - SDLAddCommand *sentCommand = add.firstObject; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - SDLAddSubMenu *sentSubmenu = submenu.firstObject; - - expect(add).to(haveCount(1)); - expect(submenu).to(haveCount(1)); - expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name)); - expect(sentSubmenu.menuIcon.value).to(equal(testArtwork2.name)); - OCMReject([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); - }); - - it(@"should properly overwrite an image cell", ^{ - OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); - textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - testManager.menuCells = @[textAndImageCell, submenuImageCell]; - OCMVerify([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); - }); - }); - - // No longer a valid unit test - context(@"when the image is not on the head unit", ^{ - beforeEach(^{ - testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; - OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); - }); - - it(@"should wait till image is on head unit and attempt to update without the image", ^{ - testManager.menuCells = @[textAndImageCell, submenuImageCell]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; - NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - SDLAddCommand *sentCommand = add.firstObject; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - SDLAddSubMenu *sentSubmenu = submenu.firstObject; - - expect(add).to(haveCount(1)); - expect(submenu).to(haveCount(1)); - expect(sentCommand.cmdIcon.value).to(beNil()); - expect(sentSubmenu.menuIcon.value).to(beNil()); - }); - }); - }); - - describe(@"updating when a menu already exists with dynamic updates on", ^{ - beforeEach(^{ - testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOn; - OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); - }); - - it(@"should send deletes first", ^{ - testManager.menuCells = @[textOnlyCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - expect(deletes).to(haveCount(1)); - expect(adds).to(haveCount(2)); - }); - - it(@"should send dynamic deletes first then dynamic adds case with 2 submenu cells", ^{ - testManager.menuCells = @[textOnlyCell, submenuCell, submenuImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[submenuCell, submenuImageCell, textOnlyCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - expect(deletes).to(haveCount(1)); - expect(adds).to(haveCount(5)); - expect(submenu).to(haveCount(2)); - }); - - it(@"should send dynamic deletes first then dynamic adds when removing one submenu cell", ^{ - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; - NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - expect(deletes).to(haveCount(0)); - expect(subDeletes).to(haveCount(1)); - expect(adds).to(haveCount(5)); - expect(submenu).to(haveCount(2)); - }); - - it(@"should send dynamic deletes first then dynamic adds when adding one new cell", ^{ - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell, textOnlyCell2]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - expect(deletes).to(haveCount(0)); - expect(adds).to(haveCount(6)); - expect(submenu).to(haveCount(2)); - }); - - it(@"should send dynamic deletes first then dynamic adds when cells stay the same", ^{ - testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - expect(deletes).to(haveCount(0)); - expect(adds).to(haveCount(3)); - }); - }); - - describe(@"updating when a menu already exists with dynamic updates off", ^{ - beforeEach(^{ - testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; - OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); - }); - - it(@"should send deletes first", ^{ - testManager.menuCells = @[textOnlyCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - expect(deletes).to(haveCount(1)); - expect(adds).to(haveCount(2)); - }); - - it(@"should deletes first case 2", ^{ - testManager.menuCells = @[textOnlyCell, textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textAndImageCell, textOnlyCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - expect(deletes).to(haveCount(2)); - expect(adds).to(haveCount(4)); - }); - - it(@"should send deletes first case 3", ^{ - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; - NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - expect(deletes).to(haveCount(2)); - expect(subDeletes).to(haveCount(2)); - expect(adds).to(haveCount(9)); - expect(submenu).to(haveCount(3)); - }); - - it(@"should send deletes first case 4", ^{ - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, textOnlyCell2]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; - NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; - - expect(deletes).to(haveCount(2)); - expect(adds).to(haveCount(9)); - expect(submenu).to(haveCount(2)); - expect(subDeletes).to(haveCount(1)); - }); - - it(@"should deletes first case 5", ^{ - testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - expect(deletes).to(haveCount(3)); - expect(adds).to(haveCount(6)); - }); - }); }); describe(@"running menu cell handlers", ^{ @@ -596,7 +250,7 @@ @interface SDLMenuManager() testManager.menuConfiguration = testMenuConfiguration; expect(testManager.menuConfiguration).toNot(equal(testMenuConfiguration)); - expect(mockConnectionManager.receivedRequests).to(haveCount(0)); + expect(testManager.transactionQueue.operationCount).to(equal(0)); }); }); @@ -609,13 +263,34 @@ @interface SDLMenuManager() testManager.menuConfiguration = testMenuConfiguration; expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); - expect(mockConnectionManager.receivedRequests).to(haveCount(1)); + expect(testManager.transactionQueue.operationCount).to(equal(1)); + }); + }); + + context(@"when no HMI level has been received", ^{ + beforeEach(^{ + testManager.currentHMILevel = nil; + }); - SDLSetGlobalPropertiesResponse *response = [[SDLSetGlobalPropertiesResponse alloc] init]; - response.success = @YES; - [mockConnectionManager respondToLastRequestWithResponse:response]; + it(@"should queue the update to the menu configuration", ^{ + testManager.menuConfiguration = testMenuConfiguration; expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); + expect(testManager.transactionQueue.operationCount).to(equal(1)); + }); + }); + + context(@"when in the menu", ^{ + beforeEach(^{ + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"6.0.0"]; + testManager.currentHMILevel = SDLHMILevelFull; + testManager.currentSystemContext = SDLSystemContextMenu; + }); + + it(@"should update the menu configuration", ^{ + testManager.menuConfiguration = testMenuConfiguration; + expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); + expect(testManager.transactionQueue.operationCount).to(equal(1)); }); }); }); @@ -642,7 +317,7 @@ @interface SDLMenuManager() }); }); - describe(@"ShowMenu RPC", ^{ + describe(@"opening the menu", ^{ beforeEach(^{ testManager.currentHMILevel = SDLHMILevelFull; testManager.currentSystemContext = SDLSystemContextMain; @@ -658,26 +333,15 @@ @interface SDLMenuManager() it(@"should send showAppMenu RPC", ^{ BOOL canSendRPC = [testManager openMenu:nil]; - NSPredicate *showMenu = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]]; - NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:showMenu]; - - expect(mockConnectionManager.receivedRequests).toNot(beEmpty()); - expect(openMenu).to(haveCount(1)); + expect(testManager.transactionQueue.operationCount).to(equal(1)); expect(canSendRPC).to(equal(YES)); }); it(@"should send showAppMenu RPC with cellID", ^ { testManager.menuCells = @[submenuCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - BOOL canSendRPC = [testManager openMenu:submenuCell]; - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]]; - NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - expect(mockConnectionManager.receivedRequests).toNot(beEmpty()); - expect(openMenu).to(haveCount(1)); + expect(testManager.transactionQueue.operationCount).to(equal(2)); expect(canSendRPC).to(equal(YES)); }); }); @@ -686,11 +350,7 @@ @interface SDLMenuManager() it(@"should not send a showAppMenu RPC when cell has no subcells", ^ { BOOL canSendRPC = [testManager openMenu:textOnlyCell]; - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]]; - NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - expect(mockConnectionManager.receivedRequests).to(beEmpty()); - expect(openMenu).to(haveCount(0)); + expect(testManager.transactionQueue.operationCount).to(equal(0)); expect(canSendRPC).to(equal(NO)); }); @@ -701,11 +361,7 @@ @interface SDLMenuManager() BOOL canSendRPC = [testManager openMenu:submenuCell]; - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]]; - NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - expect(mockConnectionManager.receivedRequests).to(beEmpty()); - expect(openMenu).to(haveCount(0)); + expect(testManager.transactionQueue.operationCount).to(equal(0)); expect(canSendRPC).to(equal(NO)); }); @@ -716,11 +372,7 @@ @interface SDLMenuManager() BOOL canSendRPC = [testManager openMenu:submenuCell]; - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]]; - NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - expect(mockConnectionManager.receivedRequests).to(beEmpty()); - expect(openMenu).to(haveCount(0)); + expect(testManager.transactionQueue.operationCount).to(equal(0)); expect(canSendRPC).to(equal(NO)); }); }); diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m new file mode 100644 index 000000000..17bfc795e --- /dev/null +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -0,0 +1,369 @@ +// +// SDLMenuReplaceOperationSpec.m +// SmartDeviceLinkTests +// +// Created by Joel Fischer on 2/16/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import +#import +#import + +#import +#import "SDLMenuReplaceOperation.h" +#import "TestConnectionManager.h" + +QuickSpecBegin(SDLMenuReplaceOperationSpec) + +describe(@"a menu replace operation", ^{ + __block SDLMenuReplaceOperation *testOp = nil; + + __block TestConnectionManager *testConnectionManager = nil; + __block SDLFileManager *testFileManager = nil; + __block SDLWindowCapability *testWindowCapability = nil; + __block SDLMenuConfiguration *testMenuConfiguration = nil; + __block NSArray *testCurrentMenu = nil; + __block NSArray *testNewMenu = nil; + +// describe(@"sending menu cells", ^{ +// it(@"should check if all artworks are uploaded and return NO", ^{ +// textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; +// testManager.menuCells = @[textAndImageCell, textOnlyCell]; +// OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); +// expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beFalse()); +// }); +// +// it(@"should properly update a text cell", ^{ +// testManager.menuCells = @[textOnlyCell]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// expect(deletes).to(beEmpty()); +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; +// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// expect(add).toNot(beEmpty()); +// }); +// +// it(@"should properly update with subcells", ^{ +// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); +// testManager.menuCells = @[submenuCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// NSPredicate *submenuCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenus = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:submenuCommandPredicate]; +// +// expect(adds).to(haveCount(2)); +// expect(submenus).to(haveCount(1)); +// }); +// }); +// +// describe(@"updating with an image", ^{ +// context(@"when the image is already on the head unit", ^{ +// beforeEach(^{ +// OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES); +// }); +// +// it(@"should check if all artworks are uploaded", ^{ +// textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; +// testManager.menuCells = @[textAndImageCell, textOnlyCell]; +// OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); +// expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beTrue()); +// }); +// +// it(@"should properly update an image cell", ^{ +// testManager.menuCells = @[textAndImageCell, submenuImageCell]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; +// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// SDLAddCommand *sentCommand = add.firstObject; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// SDLAddSubMenu *sentSubmenu = submenu.firstObject; +// +// expect(add).to(haveCount(1)); +// expect(submenu).to(haveCount(1)); +// expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name)); +// expect(sentSubmenu.menuIcon.value).to(equal(testArtwork2.name)); +// OCMReject([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); +// }); +// +// it(@"should properly overwrite an image cell", ^{ +// OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); +// textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; +// testManager.menuCells = @[textAndImageCell, submenuImageCell]; +// OCMVerify([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); +// }); +// }); +// +// // No longer a valid unit test +// context(@"when the image is not on the head unit", ^{ +// beforeEach(^{ +// testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; +// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); +// }); +// +// it(@"should wait till image is on head unit and attempt to update without the image", ^{ +// testManager.menuCells = @[textAndImageCell, submenuImageCell]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; +// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// SDLAddCommand *sentCommand = add.firstObject; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// SDLAddSubMenu *sentSubmenu = submenu.firstObject; +// +// expect(add).to(haveCount(1)); +// expect(submenu).to(haveCount(1)); +// expect(sentCommand.cmdIcon.value).to(beNil()); +// expect(sentSubmenu.menuIcon.value).to(beNil()); +// }); +// }); +// }); +// +// describe(@"updating when a menu already exists with dynamic updates on", ^{ +// beforeEach(^{ +// testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOn; +// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); +// }); +// +// it(@"should send deletes first", ^{ +// testManager.menuCells = @[textOnlyCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// expect(deletes).to(haveCount(1)); +// expect(adds).to(haveCount(2)); +// }); +// +// it(@"should send dynamic deletes first then dynamic adds case with 2 submenu cells", ^{ +// testManager.menuCells = @[textOnlyCell, submenuCell, submenuImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[submenuCell, submenuImageCell, textOnlyCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// +// expect(deletes).to(haveCount(1)); +// expect(adds).to(haveCount(5)); +// expect(submenu).to(haveCount(2)); +// }); +// +// it(@"should send dynamic deletes first then dynamic adds when removing one submenu cell", ^{ +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; +// NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// +// expect(deletes).to(haveCount(0)); +// expect(subDeletes).to(haveCount(1)); +// expect(adds).to(haveCount(5)); +// expect(submenu).to(haveCount(2)); +// }); +// +// it(@"should send dynamic deletes first then dynamic adds when adding one new cell", ^{ +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell, textOnlyCell2]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// +// expect(deletes).to(haveCount(0)); +// expect(adds).to(haveCount(6)); +// expect(submenu).to(haveCount(2)); +// }); +// +// it(@"should send dynamic deletes first then dynamic adds when cells stay the same", ^{ +// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// expect(deletes).to(haveCount(0)); +// expect(adds).to(haveCount(3)); +// }); +// }); +// +// describe(@"updating when a menu already exists with dynamic updates off", ^{ +// beforeEach(^{ +// testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; +// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); +// }); +// +// it(@"should send deletes first", ^{ +// testManager.menuCells = @[textOnlyCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// expect(deletes).to(haveCount(1)); +// expect(adds).to(haveCount(2)); +// }); +// +// it(@"should deletes first case 2", ^{ +// testManager.menuCells = @[textOnlyCell, textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textAndImageCell, textOnlyCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// expect(deletes).to(haveCount(2)); +// expect(adds).to(haveCount(4)); +// }); +// +// it(@"should send deletes first case 3", ^{ +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; +// NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// +// expect(deletes).to(haveCount(2)); +// expect(subDeletes).to(haveCount(2)); +// expect(adds).to(haveCount(9)); +// expect(submenu).to(haveCount(3)); +// }); +// +// it(@"should send deletes first case 4", ^{ +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, textOnlyCell2]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// +// NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; +// NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; +// +// expect(deletes).to(haveCount(2)); +// expect(adds).to(haveCount(9)); +// expect(submenu).to(haveCount(2)); +// expect(subDeletes).to(haveCount(1)); +// }); +// +// it(@"should deletes first case 5", ^{ +// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// expect(deletes).to(haveCount(3)); +// expect(adds).to(haveCount(6)); +// }); +// }); +}); + +QuickSpecEnd diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m new file mode 100644 index 000000000..e341d4099 --- /dev/null +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m @@ -0,0 +1,19 @@ +// +// SDLMenuShowOperationSpec.m +// SmartDeviceLinkTests +// +// Created by Joel Fischer on 2/16/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import +#import +#import + +#import +#import "SDLMenuReplaceOperation.h" +#import "TestConnectionManager.h" + +QuickSpecBegin(SDLMenuShowOperationSpec) + +QuickSpecEnd From 974e30882cf70f50ce5ad3b5ff34df90d98bee29 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 16 Feb 2021 17:11:16 -0500 Subject: [PATCH 033/112] Fix not properly checking setting equality in menu manager --- SmartDeviceLink/private/SDLMenuManager.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index de552b0b3..af21b5b10 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -149,7 +149,7 @@ - (void)sdl_updateTransactionQueueSuspended { #pragma mark - Setters - (void)setMenuConfiguration:(SDLMenuConfiguration *)menuConfiguration { - if (menuConfiguration == self.menuConfiguration) { + if ([menuConfiguration isEqual:self.menuConfiguration]) { SDLLogD(@"New menu configuration is equal to existing one, will not set new configuration"); return; } else if ([[SDLGlobals sharedGlobals].rpcVersion isLessThanVersion:[SDLVersion versionWithMajor:6 minor:0 patch:0]]) { @@ -178,6 +178,11 @@ - (void)setMenuConfiguration:(SDLMenuConfiguration *)menuConfiguration { } - (void)setMenuCells:(NSArray *)menuCells { + if ([self.menuCells isEqualToArray:menuCells]) { + SDLLogD(@"The set menu cells are identical to previously set menu cells. Skipping..."); + return; + } + NSMutableSet *titleCheckSet = [NSMutableSet set]; NSMutableSet *allMenuVoiceCommands = [NSMutableSet set]; NSUInteger voiceCommandCount = 0; From 3514057bdd0c3dc869c9b001fb1b092bcdfaae75 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 17 Feb 2021 10:58:13 -0500 Subject: [PATCH 034/112] Fix menu configuration update op to return an error in more situations --- .../private/SDLMenuConfigurationUpdateOperation.m | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m index 178446bf7..d0df7a8ad 100644 --- a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m @@ -45,15 +45,14 @@ - (void)start { [super start]; if (self.isCancelled) { return; } - if ([[SDLGlobals sharedGlobals].rpcVersion isLessThanVersion:[SDLVersion versionWithMajor:6 minor:0 patch:0]]) { - SDLLogW(@"Menu configurations is only supported on head units with RPC spec version 6.0.0 or later. Currently connected head unit RPC spec version is %@", [SDLGlobals sharedGlobals].rpcVersion); - return [self finishOperation]; - } else if (self.availableMenuLayouts == nil) { + if (self.availableMenuLayouts.count == 0) { SDLLogW(@"Could not set the main menu configuration. Which menu layouts can be used is not available"); + self.internalError = [NSError sdl_menuManager_configurationOperationLayoutsNotSupported]; return [self finishOperation]; } else if (![self.availableMenuLayouts containsObject:self.updatedMenuConfiguration.mainMenuLayout] || ![self.availableMenuLayouts containsObject:self.updatedMenuConfiguration.defaultSubmenuLayout]) { SDLLogE(@"One or more of the set menu layouts are not available on this system. The menu configuration will not be set. Available menu layouts: %@, set menu layouts: %@", self.availableMenuLayouts, self.updatedMenuConfiguration); + self.internalError = [NSError sdl_menuManager_configurationOperationLayoutsNotSupported]; return [self finishOperation]; } From cedee386d2626f6cddd3f08e2079eb60f3d91cd4 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 17 Feb 2021 10:58:31 -0500 Subject: [PATCH 035/112] Fix add menu item with id returning early --- SmartDeviceLink/private/SDLMenuReplaceUtilities.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 7ef31fcfb..b8e095b05 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -207,7 +207,8 @@ + (BOOL)addMenuRequestWithCommandId:(UInt32)commandId position:(UInt16)position addedCell = cell; break; } else if (cell.subCells.count > 0) { - return [self addMenuRequestWithCommandId:commandId position:position fromNewMenuList:cell.subCells toMainMenuList:mainMenuList]; + BOOL success = [self addMenuRequestWithCommandId:commandId position:position fromNewMenuList:cell.subCells toMainMenuList:mainMenuList]; + if (success) { return YES; } } } From 150dc204dcf00ab16c0e558ab107d31c65d4cb9f Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 17 Feb 2021 10:58:38 -0500 Subject: [PATCH 036/112] Add additional tests --- SmartDeviceLink/private/SDLError.h | 1 + SmartDeviceLink/private/SDLError.m | 8 ++ SmartDeviceLink/public/SDLErrorConstants.h | 5 +- .../SDLMenuConfigurationUpdateOperationSpec.m | 92 +++++++++++- .../DevAPISpecs/SDLMenuShowOperationSpec.m | 131 +++++++++++++++++- 5 files changed, 230 insertions(+), 7 deletions(-) diff --git a/SmartDeviceLink/private/SDLError.h b/SmartDeviceLink/private/SDLError.h index 5a5d95d73..1b491a558 100644 --- a/SmartDeviceLink/private/SDLError.h +++ b/SmartDeviceLink/private/SDLError.h @@ -58,6 +58,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark Menu Manager + (NSError *)sdl_menuManager_configurationOperationCancelled; ++ (NSError *)sdl_menuManager_configurationOperationLayoutsNotSupported; + (NSError *)sdl_menuManager_configurationOperationFailed:(SDLMenuConfiguration *)failedConfiguration; + (NSError *)sdl_menuManager_openMenuOperationCancelled; + (NSError *)sdl_menuManager_openMenuOperationFailed:(nullable SDLMenuCell *)menuCell; diff --git a/SmartDeviceLink/private/SDLError.m b/SmartDeviceLink/private/SDLError.m index 3ace9e39a..7d4f1f15f 100644 --- a/SmartDeviceLink/private/SDLError.m +++ b/SmartDeviceLink/private/SDLError.m @@ -267,6 +267,14 @@ + (NSError *)sdl_menuManager_configurationOperationCancelled { }]; } ++ (NSError *)sdl_menuManager_configurationOperationLayoutsNotSupported { + return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorConfigurationUpdateLayoutNotSupported userInfo:@{ + NSLocalizedDescriptionKey: @"Menu Manager - Configuration Update Failed", + NSLocalizedFailureReasonErrorKey: @"One or more of the configuration layouts is not supported by the module", + NSLocalizedRecoverySuggestionErrorKey: @"Compare SDLManager.systemCapabilityManager.defaultWindowCapability.menuLayoutsAvailable to what you attempted to set" + }]; +} + + (NSError *)sdl_menuManager_configurationOperationFailed:(SDLMenuConfiguration *)failedConfiguration { return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorConfigurationUpdateFailed userInfo:@{ @"Failed Configuration": failedConfiguration, diff --git a/SmartDeviceLink/public/SDLErrorConstants.h b/SmartDeviceLink/public/SDLErrorConstants.h index 9bf68ef38..f34b5a6fe 100644 --- a/SmartDeviceLink/public/SDLErrorConstants.h +++ b/SmartDeviceLink/public/SDLErrorConstants.h @@ -189,8 +189,9 @@ typedef NS_ENUM(NSInteger, SDLMenuManagerError) { SDLMenuManagerErrorRPCsFailed = -1, SDLMenuManagerErrorPendingUpdateSuperseded = -2, SDLMenuManagerErrorOperationCancelled = -3, - SDLMenuManagerErrorConfigurationUpdateFailed = -4, - SDLMenuManagerErrorOpenMenuFailed = -5 + SDLMenuManagerErrorConfigurationUpdateLayoutNotSupported = -4, + SDLMenuManagerErrorConfigurationUpdateFailed = -5, + SDLMenuManagerErrorOpenMenuFailed = -6 }; /// Errors associated with Choice Set class diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m index 2c9192151..a676e5622 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m @@ -22,11 +22,95 @@ __block TestConnectionManager *testConnectionManager = nil; __block SDLFileManager *testFileManager = nil; __block SDLWindowCapability *testWindowCapability = nil; - __block SDLMenuConfiguration *testMenuConfiguration = nil; - __block NSArray *testCurrentMenu = nil; - __block NSArray *testNewMenu = nil; + SDLMenuConfiguration *testMenuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutList defaultSubmenuLayout:SDLMenuLayoutTiles]; - + __block SDLMenuConfigurationUpdatedBlock testUpdatedBlock = nil; + __block SDLMenuConfiguration *updatedBlockMenuConfiguration = nil; + + beforeEach(^{ + testConnectionManager = [[TestConnectionManager alloc] init]; + testFileManager = OCMClassMock([SDLFileManager class]); + testWindowCapability = nil; + + updatedBlockMenuConfiguration = nil; + testUpdatedBlock = ^(SDLMenuConfiguration *newConfiguration) { + updatedBlockMenuConfiguration = newConfiguration; + }; + }); + + describe(@"when the layout check fails", ^{ + context(@"when there are no known menu layouts", ^{ + beforeEach(^{ + testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[] dynamicUpdateCapabilities:nil]; + }); + + it(@"should return an error and finish", ^{ + testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock]; + [testOp start]; + + expect(testOp.error).toNot(beNil()); + expect(testConnectionManager.receivedRequests).to(beEmpty()); + expect(testOp.isFinished).to(beTrue()); + }); + }); + + context(@"when the set main menu layout is not available", ^{ + beforeEach(^{ + testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[SDLMenuLayoutTiles] dynamicUpdateCapabilities:nil]; + }); + + it(@"should return an error and finish", ^{ + testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock]; + [testOp start]; + + expect(testOp.error).toNot(beNil()); + expect(testConnectionManager.receivedRequests).to(beEmpty()); + expect(testOp.isFinished).to(beTrue()); + }); + }); + + context(@"when the set default submenu layout is not available", ^{ + beforeEach(^{ + testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[SDLMenuLayoutList] dynamicUpdateCapabilities:nil]; + }); + + it(@"should return an error and finish", ^{ + testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock]; + [testOp start]; + + expect(testOp.error).toNot(beNil()); + expect(testConnectionManager.receivedRequests).to(beEmpty()); + expect(testOp.isFinished).to(beTrue()); + }); + }); + }); + + describe(@"when the set layouts are available", ^{ + beforeEach(^{ + testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[SDLMenuLayoutList, SDLMenuLayoutTiles] dynamicUpdateCapabilities:nil]; + }); + + it(@"should send the RPC", ^{ + testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock]; + [testOp start]; + + expect(testOp.error).to(beNil()); + expect(testConnectionManager.receivedRequests).toNot(beEmpty()); + expect(testOp.isFinished).to(beFalse()); + }); + + context(@"if an error returned", ^{ + it(@"should return an error and finish", ^{ + + }); + }); + + context(@"if it succeeded", ^{ + it(@"should not return an error and finish", ^{ + + }); + }); + }); }); QuickSpecEnd diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m index e341d4099..ba0f86ebe 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m @@ -11,9 +11,138 @@ #import #import -#import "SDLMenuReplaceOperation.h" +#import "SDLMenuShowOperation.h" #import "TestConnectionManager.h" QuickSpecBegin(SDLMenuShowOperationSpec) +describe(@"the show menu operation", ^{ + __block SDLMenuShowOperation *testOp = nil; + __block TestConnectionManager *testConnectionManager = nil; + + beforeEach(^{ + testConnectionManager = [[TestConnectionManager alloc] init]; + }); + + afterEach(^{ + testOp = nil; + }); + + context(@"opening to the main menu", ^{ + beforeEach(^{ + testOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:testConnectionManager toMenuCell:nil]; + [testOp start]; + }); + + it(@"should send the RPC request", ^{ + expect(testConnectionManager.receivedRequests).to(haveCount(1)); + }); + + context(@"when the response is not SUCCESS or WARNINGS", ^{ + beforeEach(^{ + SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init]; + response.success = @NO; + response.resultCode = SDLResultRejected; + + [testConnectionManager respondToLastRequestWithResponse:response]; + }); + + it(@"should set the error and finish", ^{ + expect(testOp.error).toNot((beNil())); + expect(testOp.isFinished).to(beTrue()); + }); + }); + + context(@"when the response is SUCCESS", ^{ + beforeEach(^{ + SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init]; + response.success = @YES; + response.resultCode = SDLResultSuccess; + + [testConnectionManager respondToLastRequestWithResponse:response]; + }); + + it(@"should not set the error and finish", ^{ + expect(testOp.error).to((beNil())); + expect(testOp.isFinished).to(beTrue()); + }); + }); + + context(@"when the response is WARNINGS", ^{ + beforeEach(^{ + SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init]; + response.success = @YES; + response.resultCode = SDLResultWarnings; + + [testConnectionManager respondToLastRequestWithResponse:response]; + }); + + it(@"should not set the error and finish", ^{ + expect(testOp.error).to((beNil())); + expect(testOp.isFinished).to(beTrue()); + }); + }); + }); + + context(@"opening to an inner menu", ^{ + __block SDLMenuCell *openToCell = nil; + __block SDLMenuCell *subcell = nil; + beforeEach(^{ + subcell = [[SDLMenuCell alloc] initWithTitle:@"Subcell" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { }]; + openToCell = [[SDLMenuCell alloc] initWithTitle:@"Test submenu" icon:nil submenuLayout:nil subCells:@[subcell]]; + testOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:testConnectionManager toMenuCell:openToCell]; + [testOp start]; + }); + + it(@"should send the RPC request", ^{ + expect(testConnectionManager.receivedRequests).to(haveCount(1)); + }); + + context(@"when the response is not SUCCESS or WARNINGS", ^{ + beforeEach(^{ + SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init]; + response.success = @NO; + response.resultCode = SDLResultRejected; + + [testConnectionManager respondToLastRequestWithResponse:response]; + }); + + it(@"should set the error and finish", ^{ + expect(testOp.error).toNot((beNil())); + expect(testOp.isFinished).to(beTrue()); + }); + }); + + context(@"when the response is SUCCESS", ^{ + beforeEach(^{ + SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init]; + response.success = @YES; + response.resultCode = SDLResultSuccess; + + [testConnectionManager respondToLastRequestWithResponse:response]; + }); + + it(@"should not set the error and finish", ^{ + expect(testOp.error).to((beNil())); + expect(testOp.isFinished).to(beTrue()); + }); + }); + + context(@"when the response is WARNINGS", ^{ + beforeEach(^{ + SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init]; + response.success = @YES; + response.resultCode = SDLResultWarnings; + + [testConnectionManager respondToLastRequestWithResponse:response]; + }); + + it(@"should not set the error and finish", ^{ + expect(testOp.error).to((beNil())); + expect(testOp.isFinished).to(beTrue()); + }); + }); + }); +}); + QuickSpecEnd From 7398e3f401430c117401aff1d7a0bdc2c7c2084d Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 17 Feb 2021 11:39:24 -0500 Subject: [PATCH 037/112] Add tests for menu show operation --- .../SDLMenuConfigurationUpdateOperation.m | 3 +- .../SDLMenuConfigurationUpdateOperationSpec.m | 42 ++++++++++++++++++- .../DevAPISpecs/SDLMenuShowOperationSpec.m | 8 ++++ 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m index d0df7a8ad..8ede72988 100644 --- a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m @@ -56,10 +56,9 @@ - (void)start { return [self finishOperation]; } + __weak typeof(self) weakself = self; SDLSetGlobalProperties *setGlobalsRPC = [[SDLSetGlobalProperties alloc] init]; setGlobalsRPC.menuLayout = self.updatedMenuConfiguration.mainMenuLayout; - - __weak typeof(self) weakself = self; [self.connectionManager sendConnectionRequest:setGlobalsRPC withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { __strong typeof(weakself) strongself = weakself; if (error != nil) { diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m index a676e5622..1f199b58e 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m @@ -38,7 +38,10 @@ }; }); + // when the layout check fails describe(@"when the layout check fails", ^{ + + // when there are no known menu layouts context(@"when there are no known menu layouts", ^{ beforeEach(^{ testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[] dynamicUpdateCapabilities:nil]; @@ -51,9 +54,11 @@ expect(testOp.error).toNot(beNil()); expect(testConnectionManager.receivedRequests).to(beEmpty()); expect(testOp.isFinished).to(beTrue()); + expect(updatedBlockMenuConfiguration).to(beNil()); }); }); + // when the set main menu layout is not available context(@"when the set main menu layout is not available", ^{ beforeEach(^{ testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[SDLMenuLayoutTiles] dynamicUpdateCapabilities:nil]; @@ -66,9 +71,11 @@ expect(testOp.error).toNot(beNil()); expect(testConnectionManager.receivedRequests).to(beEmpty()); expect(testOp.isFinished).to(beTrue()); + expect(updatedBlockMenuConfiguration).to(beNil()); }); }); + // when the set default submenu layout is not available context(@"when the set default submenu layout is not available", ^{ beforeEach(^{ testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[SDLMenuLayoutList] dynamicUpdateCapabilities:nil]; @@ -81,33 +88,64 @@ expect(testOp.error).toNot(beNil()); expect(testConnectionManager.receivedRequests).to(beEmpty()); expect(testOp.isFinished).to(beTrue()); + expect(updatedBlockMenuConfiguration).to(beNil()); }); }); }); + // when the set layouts are available describe(@"when the set layouts are available", ^{ + __block SDLSetGlobalPropertiesResponse *response = [[SDLSetGlobalPropertiesResponse alloc] init]; + beforeEach(^{ testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[SDLMenuLayoutList, SDLMenuLayoutTiles] dynamicUpdateCapabilities:nil]; - }); - it(@"should send the RPC", ^{ testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock]; [testOp start]; + }); + // should send the RPC + it(@"should send the RPC", ^{ expect(testOp.error).to(beNil()); expect(testConnectionManager.receivedRequests).toNot(beEmpty()); expect(testOp.isFinished).to(beFalse()); + expect(updatedBlockMenuConfiguration).to(beNil()); + + SDLSetGlobalProperties *receivedSGP = (SDLSetGlobalProperties *)testConnectionManager.receivedRequests[0]; + expect(receivedSGP.menuLayout).to(equal(testMenuConfiguration.mainMenuLayout)); }); + // if an error returned context(@"if an error returned", ^{ + beforeEach(^{ + response.success = @NO; + response.resultCode = SDLResultRejected; + }); + it(@"should return an error and finish", ^{ + [testConnectionManager respondToLastRequestWithResponse:response]; + expect(testOp.error).toNot(beNil()); + expect(testConnectionManager.receivedRequests).toNot(beEmpty()); + expect(testOp.isFinished).to(beTrue()); + expect(updatedBlockMenuConfiguration).to(beNil()); }); }); + // if it succeeded context(@"if it succeeded", ^{ + beforeEach(^{ + response.success = @YES; + response.resultCode = SDLResultSuccess; + }); + it(@"should not return an error and finish", ^{ + [testConnectionManager respondToLastRequestWithResponse:response]; + expect(testOp.error).to(beNil()); + expect(testConnectionManager.receivedRequests).toNot(beEmpty()); + expect(testOp.isFinished).to(beTrue()); + expect(updatedBlockMenuConfiguration).to(equal(testMenuConfiguration)); }); }); }); diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m index ba0f86ebe..60a8db2bf 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m @@ -28,6 +28,7 @@ testOp = nil; }); + // opening to the main menu context(@"opening to the main menu", ^{ beforeEach(^{ testOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:testConnectionManager toMenuCell:nil]; @@ -38,6 +39,7 @@ expect(testConnectionManager.receivedRequests).to(haveCount(1)); }); + // when the response is not SUCCESS or WARNINGS context(@"when the response is not SUCCESS or WARNINGS", ^{ beforeEach(^{ SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init]; @@ -53,6 +55,7 @@ }); }); + // when the response is SUCCESS context(@"when the response is SUCCESS", ^{ beforeEach(^{ SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init]; @@ -68,6 +71,7 @@ }); }); + // when the response is WARNINGS context(@"when the response is WARNINGS", ^{ beforeEach(^{ SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init]; @@ -84,6 +88,7 @@ }); }); + // opening to an inner menu context(@"opening to an inner menu", ^{ __block SDLMenuCell *openToCell = nil; __block SDLMenuCell *subcell = nil; @@ -98,6 +103,7 @@ expect(testConnectionManager.receivedRequests).to(haveCount(1)); }); + // when the response is not SUCCESS or WARNINGS context(@"when the response is not SUCCESS or WARNINGS", ^{ beforeEach(^{ SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init]; @@ -113,6 +119,7 @@ }); }); + // when the response is SUCCESS context(@"when the response is SUCCESS", ^{ beforeEach(^{ SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init]; @@ -128,6 +135,7 @@ }); }); + // when the response is WARNINGS context(@"when the response is WARNINGS", ^{ beforeEach(^{ SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init]; From 053dc3b49326cc6147c7e76673c2095d7230db60 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 17 Feb 2021 12:13:34 -0500 Subject: [PATCH 038/112] Moving test code around --- .../DevAPISpecs/SDLMenuManagerSpec.m | 21 +- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 707 +++++++++--------- 2 files changed, 372 insertions(+), 356 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index 2df2bba2e..2c3c0313e 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -41,33 +41,18 @@ @interface SDLMenuManager() __block TestConnectionManager *mockConnectionManager = nil; __block SDLFileManager *mockFileManager = nil; __block SDLSystemCapabilityManager *mockSystemCapabilityManager = nil; - __block SDLArtwork *testArtwork = nil; - __block SDLArtwork *testArtwork2 = nil; - __block SDLArtwork *testArtwork3 = nil; + + __block SDLMenuConfiguration *testMenuConfiguration = nil; __block SDLMenuCell *textOnlyCell = nil; - __block SDLMenuCell *textOnlyCell2 = nil; - __block SDLMenuCell *textAndImageCell = nil; __block SDLMenuCell *submenuCell = nil; - __block SDLMenuCell *submenuImageCell = nil; - - __block SDLMenuConfiguration *testMenuConfiguration = nil; beforeEach(^{ - testArtwork = [[SDLArtwork alloc] initWithData:[@"Test data" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO]; - testArtwork2 = [[SDLArtwork alloc] initWithData:[@"Test data 2" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name 2" fileExtension:@"png" persistent:NO]; - testArtwork3 = [[SDLArtwork alloc] initWithData:[@"Test data 3" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO]; - testArtwork3.overwrite = YES; - textOnlyCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" icon:nil submenuLayout:nil subCells:@[textOnlyCell, textAndImageCell]]; - submenuImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 4" icon:testArtwork2 submenuLayout:SDLMenuLayoutTiles subCells:@[textOnlyCell]]; - textOnlyCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 5" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" icon:nil submenuLayout:nil subCells:@[textOnlyCell]]; testMenuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutTiles defaultSubmenuLayout:SDLMenuLayoutList]; - mockConnectionManager = [[TestConnectionManager alloc] init]; mockFileManager = OCMClassMock([SDLFileManager class]); mockSystemCapabilityManager = OCMClassMock([SDLSystemCapabilityManager class]); diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 17bfc795e..f62cee7dd 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -26,344 +26,375 @@ __block NSArray *testCurrentMenu = nil; __block NSArray *testNewMenu = nil; -// describe(@"sending menu cells", ^{ -// it(@"should check if all artworks are uploaded and return NO", ^{ -// textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; -// testManager.menuCells = @[textAndImageCell, textOnlyCell]; -// OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); -// expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beFalse()); -// }); -// -// it(@"should properly update a text cell", ^{ -// testManager.menuCells = @[textOnlyCell]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// expect(deletes).to(beEmpty()); -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; -// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// expect(add).toNot(beEmpty()); -// }); -// -// it(@"should properly update with subcells", ^{ -// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); -// testManager.menuCells = @[submenuCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// NSPredicate *submenuCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenus = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:submenuCommandPredicate]; -// -// expect(adds).to(haveCount(2)); -// expect(submenus).to(haveCount(1)); -// }); -// }); -// -// describe(@"updating with an image", ^{ -// context(@"when the image is already on the head unit", ^{ -// beforeEach(^{ -// OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES); -// }); -// -// it(@"should check if all artworks are uploaded", ^{ -// textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; -// testManager.menuCells = @[textAndImageCell, textOnlyCell]; -// OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); -// expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beTrue()); -// }); -// -// it(@"should properly update an image cell", ^{ -// testManager.menuCells = @[textAndImageCell, submenuImageCell]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; -// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// SDLAddCommand *sentCommand = add.firstObject; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// SDLAddSubMenu *sentSubmenu = submenu.firstObject; -// -// expect(add).to(haveCount(1)); -// expect(submenu).to(haveCount(1)); -// expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name)); -// expect(sentSubmenu.menuIcon.value).to(equal(testArtwork2.name)); -// OCMReject([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); -// }); -// -// it(@"should properly overwrite an image cell", ^{ -// OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); -// textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; -// testManager.menuCells = @[textAndImageCell, submenuImageCell]; -// OCMVerify([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); -// }); -// }); -// -// // No longer a valid unit test -// context(@"when the image is not on the head unit", ^{ -// beforeEach(^{ -// testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; -// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); -// }); -// -// it(@"should wait till image is on head unit and attempt to update without the image", ^{ -// testManager.menuCells = @[textAndImageCell, submenuImageCell]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; -// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// SDLAddCommand *sentCommand = add.firstObject; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// SDLAddSubMenu *sentSubmenu = submenu.firstObject; -// -// expect(add).to(haveCount(1)); -// expect(submenu).to(haveCount(1)); -// expect(sentCommand.cmdIcon.value).to(beNil()); -// expect(sentSubmenu.menuIcon.value).to(beNil()); -// }); -// }); -// }); -// -// describe(@"updating when a menu already exists with dynamic updates on", ^{ -// beforeEach(^{ -// testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOn; -// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); -// }); -// -// it(@"should send deletes first", ^{ -// testManager.menuCells = @[textOnlyCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// expect(deletes).to(haveCount(1)); -// expect(adds).to(haveCount(2)); -// }); -// -// it(@"should send dynamic deletes first then dynamic adds case with 2 submenu cells", ^{ -// testManager.menuCells = @[textOnlyCell, submenuCell, submenuImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[submenuCell, submenuImageCell, textOnlyCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// -// expect(deletes).to(haveCount(1)); -// expect(adds).to(haveCount(5)); -// expect(submenu).to(haveCount(2)); -// }); -// -// it(@"should send dynamic deletes first then dynamic adds when removing one submenu cell", ^{ -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; -// NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// -// expect(deletes).to(haveCount(0)); -// expect(subDeletes).to(haveCount(1)); -// expect(adds).to(haveCount(5)); -// expect(submenu).to(haveCount(2)); -// }); -// -// it(@"should send dynamic deletes first then dynamic adds when adding one new cell", ^{ -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell, textOnlyCell2]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// -// expect(deletes).to(haveCount(0)); -// expect(adds).to(haveCount(6)); -// expect(submenu).to(haveCount(2)); -// }); -// -// it(@"should send dynamic deletes first then dynamic adds when cells stay the same", ^{ -// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// expect(deletes).to(haveCount(0)); -// expect(adds).to(haveCount(3)); -// }); -// }); -// -// describe(@"updating when a menu already exists with dynamic updates off", ^{ -// beforeEach(^{ -// testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; -// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); -// }); -// -// it(@"should send deletes first", ^{ -// testManager.menuCells = @[textOnlyCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// expect(deletes).to(haveCount(1)); -// expect(adds).to(haveCount(2)); -// }); -// -// it(@"should deletes first case 2", ^{ -// testManager.menuCells = @[textOnlyCell, textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textAndImageCell, textOnlyCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// expect(deletes).to(haveCount(2)); -// expect(adds).to(haveCount(4)); -// }); -// -// it(@"should send deletes first case 3", ^{ -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; -// NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// -// expect(deletes).to(haveCount(2)); -// expect(subDeletes).to(haveCount(2)); -// expect(adds).to(haveCount(9)); -// expect(submenu).to(haveCount(3)); -// }); -// -// it(@"should send deletes first case 4", ^{ -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, textOnlyCell2]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// -// NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; -// NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; -// -// expect(deletes).to(haveCount(2)); -// expect(adds).to(haveCount(9)); -// expect(submenu).to(haveCount(2)); -// expect(subDeletes).to(haveCount(1)); -// }); -// -// it(@"should deletes first case 5", ^{ -// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// expect(deletes).to(haveCount(3)); -// expect(adds).to(haveCount(6)); -// }); -// }); + __block SDLArtwork *testArtwork = nil; + __block SDLArtwork *testArtwork2 = nil; + __block SDLArtwork *testArtwork3 = nil; + + __block SDLMenuCell *textOnlyCell = nil; + __block SDLMenuCell *textOnlyCell2 = nil; + __block SDLMenuCell *textAndImageCell = nil; + __block SDLMenuCell *submenuCell = nil; + __block SDLMenuCell *submenuImageCell = nil; + + beforeEach(^{ + testArtwork = [[SDLArtwork alloc] initWithData:[@"Test data" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO]; + testArtwork2 = [[SDLArtwork alloc] initWithData:[@"Test data 2" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name 2" fileExtension:@"png" persistent:NO]; + testArtwork3 = [[SDLArtwork alloc] initWithData:[@"Test data 3" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO]; + testArtwork3.overwrite = YES; + + textOnlyCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" icon:nil submenuLayout:nil subCells:@[textOnlyCell, textAndImageCell]]; + submenuImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 4" icon:testArtwork2 submenuLayout:SDLMenuLayoutTiles subCells:@[textOnlyCell]]; + textOnlyCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 5" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + + testOp = nil; + testConnectionManager = [[TestConnectionManager alloc] init]; + testFileManager = OCMClassMock([SDLFileManager class]); + testWindowCapability = nil; + testMenuConfiguration = nil; + testCurrentMenu = nil; + testNewMenu = nil; + }); + + describe(@"sending menu cells", ^{ + it(@"should check if all artworks are uploaded and return NO", ^{ + textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + testManager.menuCells = @[textAndImageCell, textOnlyCell]; + OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); + expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beFalse()); + }); + + it(@"should properly update a text cell", ^{ + testManager.menuCells = @[textOnlyCell]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]]; + NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + expect(deletes).to(beEmpty()); + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; + NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + expect(add).toNot(beEmpty()); + }); + + it(@"should properly update with subcells", ^{ + OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); + testManager.menuCells = @[submenuCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; + NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + NSPredicate *submenuCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; + NSArray *submenus = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:submenuCommandPredicate]; + + expect(adds).to(haveCount(2)); + expect(submenus).to(haveCount(1)); + }); + }); + + describe(@"updating with an image", ^{ + context(@"when the image is already on the head unit", ^{ + beforeEach(^{ + OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES); + }); + + it(@"should check if all artworks are uploaded", ^{ + textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + testManager.menuCells = @[textAndImageCell, textOnlyCell]; + OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); + expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beTrue()); + }); + + it(@"should properly update an image cell", ^{ + testManager.menuCells = @[textAndImageCell, submenuImageCell]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; + NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + SDLAddCommand *sentCommand = add.firstObject; + + NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; + NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; + SDLAddSubMenu *sentSubmenu = submenu.firstObject; + + expect(add).to(haveCount(1)); + expect(submenu).to(haveCount(1)); + expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name)); + expect(sentSubmenu.menuIcon.value).to(equal(testArtwork2.name)); + OCMReject([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); + }); + + it(@"should properly overwrite an image cell", ^{ + OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); + textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + testManager.menuCells = @[textAndImageCell, submenuImageCell]; + OCMVerify([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); + }); + }); + + // No longer a valid unit test + context(@"when the image is not on the head unit", ^{ + beforeEach(^{ + testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; + OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); + }); + + it(@"should wait till image is on head unit and attempt to update without the image", ^{ + testManager.menuCells = @[textAndImageCell, submenuImageCell]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; + NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + SDLAddCommand *sentCommand = add.firstObject; + + NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; + NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; + SDLAddSubMenu *sentSubmenu = submenu.firstObject; + + expect(add).to(haveCount(1)); + expect(submenu).to(haveCount(1)); + expect(sentCommand.cmdIcon.value).to(beNil()); + expect(sentSubmenu.menuIcon.value).to(beNil()); + }); + }); + }); + + describe(@"updating when a menu already exists with dynamic updates on", ^{ + beforeEach(^{ + testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOn; + OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); + }); + + it(@"should send deletes first", ^{ + testManager.menuCells = @[textOnlyCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + testManager.menuCells = @[textAndImageCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(1)); + expect(adds).to(haveCount(2)); + }); + + it(@"should send dynamic deletes first then dynamic adds case with 2 submenu cells", ^{ + testManager.menuCells = @[textOnlyCell, submenuCell, submenuImageCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + testManager.menuCells = @[submenuCell, submenuImageCell, textOnlyCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; + NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; + + expect(deletes).to(haveCount(1)); + expect(adds).to(haveCount(5)); + expect(submenu).to(haveCount(2)); + }); + + it(@"should send dynamic deletes first then dynamic adds when removing one submenu cell", ^{ + testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; + NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; + NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; + + expect(deletes).to(haveCount(0)); + expect(subDeletes).to(haveCount(1)); + expect(adds).to(haveCount(5)); + expect(submenu).to(haveCount(2)); + }); + + it(@"should send dynamic deletes first then dynamic adds when adding one new cell", ^{ + testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell, textOnlyCell2]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; + NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; + + expect(deletes).to(haveCount(0)); + expect(adds).to(haveCount(6)); + expect(submenu).to(haveCount(2)); + }); + + it(@"should send dynamic deletes first then dynamic adds when cells stay the same", ^{ + testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(0)); + expect(adds).to(haveCount(3)); + }); + }); + + describe(@"updating when a menu already exists with dynamic updates off", ^{ + beforeEach(^{ + testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; + OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); + }); + + it(@"should send deletes first", ^{ + testManager.menuCells = @[textOnlyCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + testManager.menuCells = @[textAndImageCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(1)); + expect(adds).to(haveCount(2)); + }); + + it(@"should deletes first case 2", ^{ + testManager.menuCells = @[textOnlyCell, textAndImageCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + testManager.menuCells = @[textAndImageCell, textOnlyCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(2)); + expect(adds).to(haveCount(4)); + }); + + it(@"should send deletes first case 3", ^{ + testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; + NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; + NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; + + expect(deletes).to(haveCount(2)); + expect(subDeletes).to(haveCount(2)); + expect(adds).to(haveCount(9)); + expect(submenu).to(haveCount(3)); + }); + + it(@"should send deletes first case 4", ^{ + testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, textOnlyCell2]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; + NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; + + NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; + NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; + + expect(deletes).to(haveCount(2)); + expect(adds).to(haveCount(9)); + expect(submenu).to(haveCount(2)); + expect(subDeletes).to(haveCount(1)); + }); + + it(@"should deletes first case 5", ^{ + testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(3)); + expect(adds).to(haveCount(6)); + }); + }); }); QuickSpecEnd From c3117615c4cc689e0eba1fcaf21a06b7e510e0e3 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 18 Feb 2021 09:57:17 -0500 Subject: [PATCH 039/112] Working on tests --- .../private/SDLMenuReplaceOperation.h | 2 + .../private/SDLMenuReplaceUtilities.h | 2 - .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 682 +++++++++--------- 3 files changed, 349 insertions(+), 337 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.h b/SmartDeviceLink/private/SDLMenuReplaceOperation.h index 3e9c261e3..fb8d48881 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.h +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.h @@ -21,6 +21,8 @@ NS_ASSUME_NONNULL_BEGIN +typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCells); + @interface SDLMenuReplaceOperation : SDLAsynchronousOperation @property (strong, nonatomic) SDLWindowCapability *windowCapability; diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index 816cb3dd6..239fb5623 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -19,8 +19,6 @@ NS_ASSUME_NONNULL_BEGIN -typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCells); - @interface SDLMenuReplaceUtilities : NSObject #pragma mark - Artworks diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index f62cee7dd..b5ff6253c 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -36,6 +36,9 @@ __block SDLMenuCell *submenuCell = nil; __block SDLMenuCell *submenuImageCell = nil; + __block NSArray *newCurrentMenuCells = nil; + __block SDLCurrentMenuUpdatedBlock testCurrentMenuUpdatedBlock = nil; + beforeEach(^{ testArtwork = [[SDLArtwork alloc] initWithData:[@"Test data" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO]; testArtwork2 = [[SDLArtwork alloc] initWithData:[@"Test data 2" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name 2" fileExtension:@"png" persistent:NO]; @@ -51,350 +54,359 @@ testOp = nil; testConnectionManager = [[TestConnectionManager alloc] init]; testFileManager = OCMClassMock([SDLFileManager class]); - testWindowCapability = nil; - testMenuConfiguration = nil; - testCurrentMenu = nil; + testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil]; + testMenuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutList defaultSubmenuLayout:SDLMenuLayoutList]; + testCurrentMenu = @[]; testNewMenu = nil; - }); - - describe(@"sending menu cells", ^{ - it(@"should check if all artworks are uploaded and return NO", ^{ - textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - testManager.menuCells = @[textAndImageCell, textOnlyCell]; - OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); - expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beFalse()); - }); - - it(@"should properly update a text cell", ^{ - testManager.menuCells = @[textOnlyCell]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - expect(deletes).to(beEmpty()); - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; - NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - expect(add).toNot(beEmpty()); - }); - - it(@"should properly update with subcells", ^{ - OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); - testManager.menuCells = @[submenuCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - NSPredicate *submenuCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenus = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:submenuCommandPredicate]; - - expect(adds).to(haveCount(2)); - expect(submenus).to(haveCount(1)); - }); + newCurrentMenuCells = nil; + testCurrentMenuUpdatedBlock = ^(NSArray *currentMenuCells) { + newCurrentMenuCells = currentMenuCells; + }; }); - describe(@"updating with an image", ^{ - context(@"when the image is already on the head unit", ^{ - beforeEach(^{ - OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES); - }); - - it(@"should check if all artworks are uploaded", ^{ + describe(@"sending initial batch of cells", ^{ + context(@"when uploading text and image cells", ^{ + it(@"should check if all artworks are uploaded and return NO", ^{ textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - testManager.menuCells = @[textAndImageCell, textOnlyCell]; - OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); - expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beTrue()); - }); - - it(@"should properly update an image cell", ^{ - testManager.menuCells = @[textAndImageCell, submenuImageCell]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; - NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - SDLAddCommand *sentCommand = add.firstObject; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - SDLAddSubMenu *sentSubmenu = submenu.firstObject; - - expect(add).to(haveCount(1)); - expect(submenu).to(haveCount(1)); - expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name)); - expect(sentSubmenu.menuIcon.value).to(equal(testArtwork2.name)); - OCMReject([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); - }); - - it(@"should properly overwrite an image cell", ^{ - OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); - textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - testManager.menuCells = @[textAndImageCell, submenuImageCell]; - OCMVerify([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); - }); - }); - - // No longer a valid unit test - context(@"when the image is not on the head unit", ^{ - beforeEach(^{ - testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; - OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); - }); - - it(@"should wait till image is on head unit and attempt to update without the image", ^{ - testManager.menuCells = @[textAndImageCell, submenuImageCell]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; - NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - SDLAddCommand *sentCommand = add.firstObject; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - SDLAddSubMenu *sentSubmenu = submenu.firstObject; - - expect(add).to(haveCount(1)); - expect(submenu).to(haveCount(1)); - expect(sentCommand.cmdIcon.value).to(beNil()); - expect(sentSubmenu.menuIcon.value).to(beNil()); + testNewMenu = @[textAndImageCell]; + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; +// testManager.menuCells = @[textAndImageCell, textOnlyCell]; +// expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beFalse()); }); }); }); - describe(@"updating when a menu already exists with dynamic updates on", ^{ - beforeEach(^{ - testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOn; - OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); - }); - - it(@"should send deletes first", ^{ - testManager.menuCells = @[textOnlyCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - expect(deletes).to(haveCount(1)); - expect(adds).to(haveCount(2)); - }); - - it(@"should send dynamic deletes first then dynamic adds case with 2 submenu cells", ^{ - testManager.menuCells = @[textOnlyCell, submenuCell, submenuImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[submenuCell, submenuImageCell, textOnlyCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - expect(deletes).to(haveCount(1)); - expect(adds).to(haveCount(5)); - expect(submenu).to(haveCount(2)); - }); - - it(@"should send dynamic deletes first then dynamic adds when removing one submenu cell", ^{ - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; - NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - expect(deletes).to(haveCount(0)); - expect(subDeletes).to(haveCount(1)); - expect(adds).to(haveCount(5)); - expect(submenu).to(haveCount(2)); - }); - - it(@"should send dynamic deletes first then dynamic adds when adding one new cell", ^{ - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell, textOnlyCell2]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - expect(deletes).to(haveCount(0)); - expect(adds).to(haveCount(6)); - expect(submenu).to(haveCount(2)); - }); - - it(@"should send dynamic deletes first then dynamic adds when cells stay the same", ^{ - testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - expect(deletes).to(haveCount(0)); - expect(adds).to(haveCount(3)); - }); - }); - - describe(@"updating when a menu already exists with dynamic updates off", ^{ - beforeEach(^{ - testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; - OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); - }); - - it(@"should send deletes first", ^{ - testManager.menuCells = @[textOnlyCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - expect(deletes).to(haveCount(1)); - expect(adds).to(haveCount(2)); - }); - - it(@"should deletes first case 2", ^{ - testManager.menuCells = @[textOnlyCell, textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textAndImageCell, textOnlyCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - expect(deletes).to(haveCount(2)); - expect(adds).to(haveCount(4)); - }); - - it(@"should send deletes first case 3", ^{ - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; - NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - expect(deletes).to(haveCount(2)); - expect(subDeletes).to(haveCount(2)); - expect(adds).to(haveCount(9)); - expect(submenu).to(haveCount(3)); - }); - - it(@"should send deletes first case 4", ^{ - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, textOnlyCell2]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; - NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; - - NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; - NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; - - expect(deletes).to(haveCount(2)); - expect(adds).to(haveCount(9)); - expect(submenu).to(haveCount(2)); - expect(subDeletes).to(haveCount(1)); - }); - - it(@"should deletes first case 5", ^{ - testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - - expect(deletes).to(haveCount(3)); - expect(adds).to(haveCount(6)); - }); - }); +// it(@"should properly update a text cell", ^{ +// testManager.menuCells = @[textOnlyCell]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// expect(deletes).to(beEmpty()); +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; +// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// expect(add).toNot(beEmpty()); +// }); +// +// it(@"should properly update with subcells", ^{ +// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); +// testManager.menuCells = @[submenuCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// NSPredicate *submenuCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenus = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:submenuCommandPredicate]; +// +// expect(adds).to(haveCount(2)); +// expect(submenus).to(haveCount(1)); +// }); +// }); +// +// describe(@"updating with an image", ^{ +// context(@"when the image is already on the head unit", ^{ +// beforeEach(^{ +// OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES); +// }); +// +// it(@"should check if all artworks are uploaded", ^{ +// textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; +// testManager.menuCells = @[textAndImageCell, textOnlyCell]; +// OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); +// expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beTrue()); +// }); +// +// it(@"should properly update an image cell", ^{ +// testManager.menuCells = @[textAndImageCell, submenuImageCell]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; +// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// SDLAddCommand *sentCommand = add.firstObject; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// SDLAddSubMenu *sentSubmenu = submenu.firstObject; +// +// expect(add).to(haveCount(1)); +// expect(submenu).to(haveCount(1)); +// expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name)); +// expect(sentSubmenu.menuIcon.value).to(equal(testArtwork2.name)); +// OCMReject([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); +// }); +// +// it(@"should properly overwrite an image cell", ^{ +// OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); +// textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; +// testManager.menuCells = @[textAndImageCell, submenuImageCell]; +// OCMVerify([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); +// }); +// }); +// +// // No longer a valid unit test +// context(@"when the image is not on the head unit", ^{ +// beforeEach(^{ +// testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; +// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); +// }); +// +// it(@"should wait till image is on head unit and attempt to update without the image", ^{ +// testManager.menuCells = @[textAndImageCell, submenuImageCell]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; +// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// SDLAddCommand *sentCommand = add.firstObject; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// SDLAddSubMenu *sentSubmenu = submenu.firstObject; +// +// expect(add).to(haveCount(1)); +// expect(submenu).to(haveCount(1)); +// expect(sentCommand.cmdIcon.value).to(beNil()); +// expect(sentSubmenu.menuIcon.value).to(beNil()); +// }); +// }); +// }); +// +// describe(@"updating when a menu already exists with dynamic updates on", ^{ +// beforeEach(^{ +// testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOn; +// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); +// }); +// +// it(@"should send deletes first", ^{ +// testManager.menuCells = @[textOnlyCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// expect(deletes).to(haveCount(1)); +// expect(adds).to(haveCount(2)); +// }); +// +// it(@"should send dynamic deletes first then dynamic adds case with 2 submenu cells", ^{ +// testManager.menuCells = @[textOnlyCell, submenuCell, submenuImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[submenuCell, submenuImageCell, textOnlyCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// +// expect(deletes).to(haveCount(1)); +// expect(adds).to(haveCount(5)); +// expect(submenu).to(haveCount(2)); +// }); +// +// it(@"should send dynamic deletes first then dynamic adds when removing one submenu cell", ^{ +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; +// NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// +// expect(deletes).to(haveCount(0)); +// expect(subDeletes).to(haveCount(1)); +// expect(adds).to(haveCount(5)); +// expect(submenu).to(haveCount(2)); +// }); +// +// it(@"should send dynamic deletes first then dynamic adds when adding one new cell", ^{ +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell, textOnlyCell2]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// +// expect(deletes).to(haveCount(0)); +// expect(adds).to(haveCount(6)); +// expect(submenu).to(haveCount(2)); +// }); +// +// it(@"should send dynamic deletes first then dynamic adds when cells stay the same", ^{ +// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// expect(deletes).to(haveCount(0)); +// expect(adds).to(haveCount(3)); +// }); +// }); +// +// describe(@"updating when a menu already exists with dynamic updates off", ^{ +// beforeEach(^{ +// testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; +// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); +// }); +// +// it(@"should send deletes first", ^{ +// testManager.menuCells = @[textOnlyCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// expect(deletes).to(haveCount(1)); +// expect(adds).to(haveCount(2)); +// }); +// +// it(@"should deletes first case 2", ^{ +// testManager.menuCells = @[textOnlyCell, textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textAndImageCell, textOnlyCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// expect(deletes).to(haveCount(2)); +// expect(adds).to(haveCount(4)); +// }); +// +// it(@"should send deletes first case 3", ^{ +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; +// NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// +// expect(deletes).to(haveCount(2)); +// expect(subDeletes).to(haveCount(2)); +// expect(adds).to(haveCount(9)); +// expect(submenu).to(haveCount(3)); +// }); +// +// it(@"should send deletes first case 4", ^{ +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, textOnlyCell2]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// +// NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; +// NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; +// +// expect(deletes).to(haveCount(2)); +// expect(adds).to(haveCount(9)); +// expect(submenu).to(haveCount(2)); +// expect(subDeletes).to(haveCount(1)); +// }); +// +// it(@"should deletes first case 5", ^{ +// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; +// +// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; +// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; +// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// +// expect(deletes).to(haveCount(3)); +// expect(adds).to(haveCount(6)); +// }); +// }); }); QuickSpecEnd From d575aafbffee109c41ff0b3610807326e27a6127 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 18 Feb 2021 16:00:26 -0500 Subject: [PATCH 040/112] Lots of little bug fixes * Added menu replace op unit tests * Updated MenuCellState to a more descriptive title * Updated printing a run score to be much clearer * Add a null check to menu replace utilities * Make MenuCell copyable * Fix MenuCell missing private property causing a crash --- .../private/SDLDynamicMenuUpdateAlgorithm.h | 8 +- .../private/SDLDynamicMenuUpdateAlgorithm.m | 10 +- .../private/SDLDynamicMenuUpdateRunScore.m | 21 ++ .../private/SDLMenuReplaceOperation.m | 14 +- .../private/SDLMenuReplaceUtilities.m | 2 +- SmartDeviceLink/public/SDLMenuCell.h | 2 +- SmartDeviceLink/public/SDLMenuCell.m | 13 + .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 228 ++++++++++-------- 8 files changed, 181 insertions(+), 117 deletions(-) diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h index d2d117e51..b89307c5c 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h @@ -16,15 +16,15 @@ NS_ASSUME_NONNULL_BEGIN /// Menu cell state /// /// Cell state that tells the menu manager what it should do with a given SDLMenuCell -typedef NS_ENUM(NSUInteger, MenuCellState) { +typedef NS_ENUM(NSUInteger, SDLMenuCellUpdateState) { /// Marks the cell to be deleted - MenuCellStateDelete = 0, + SDLMenuCellUpdateStateDelete = 0, /// Marks the cell to be added - MenuCellStateAdd, + SDLMenuCellUpdateStateAdd, /// Marks the cell to be kept - MenuCellStateKeep + SDLMenuCellUpdateStateKeep }; @interface SDLDynamicMenuUpdateAlgorithm : NSObject diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m index 4a17eb727..ad8249ee1 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m @@ -41,8 +41,8 @@ + (nullable SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)sta // Create inner loop to compare old cells to new cells to find a match, if a match if found we mark the index at match for both the old and the new status to keep since we do not want to send RPCs for those cases for (NSUInteger newCellIndex = startIndex; newCellIndex < updatedMenuCells.count; newCellIndex++) { if ([oldMenuCells[oldCellIndex] isEqual:updatedMenuCells[newCellIndex]]) { - oldMenuStatus[oldCellIndex] = @(MenuCellStateKeep); - newMenuStatus[newCellIndex] = @(MenuCellStateKeep); + oldMenuStatus[oldCellIndex] = @(SDLMenuCellUpdateStateKeep); + newMenuStatus[newCellIndex] = @(SDLMenuCellUpdateStateKeep); startIndex = newCellIndex + 1; break; } @@ -53,7 +53,7 @@ + (nullable SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)sta NSUInteger numberOfAdds = 0; for (NSUInteger status = 0; status < newMenuStatus.count; status++) { // 0 = Delete 1 = Add 2 = Keep - if (newMenuStatus[status].integerValue == MenuCellStateAdd) { + if (newMenuStatus[status].integerValue == SDLMenuCellUpdateStateAdd) { numberOfAdds++; } } @@ -79,7 +79,7 @@ + (nullable SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)sta + (NSMutableArray *)sdl_buildAllDeleteStatusesforMenu:(NSArray *)oldMenu { NSMutableArray *oldMenuStatus = [[NSMutableArray alloc] initWithCapacity:oldMenu.count]; for (NSUInteger index = 0; index < oldMenu.count; index++) { - [oldMenuStatus addObject:@(MenuCellStateDelete)]; + [oldMenuStatus addObject:@(SDLMenuCellUpdateStateDelete)]; } return [oldMenuStatus mutableCopy]; } @@ -92,7 +92,7 @@ + (nullable SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)sta + (NSMutableArray *)sdl_buildAllAddStatusesForMenu:(NSArray *)newMenu { NSMutableArray *newMenuStatus = [[NSMutableArray alloc] initWithCapacity:newMenu.count]; for (NSUInteger index = 0; index < newMenu.count; index++) { - [newMenuStatus addObject:@(MenuCellStateAdd)]; + [newMenuStatus addObject:@(SDLMenuCellUpdateStateAdd)]; } return [newMenuStatus mutableCopy]; } diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.m b/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.m index 5066904a5..568ad5399 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.m +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.m @@ -8,6 +8,8 @@ #import "SDLDynamicMenuUpdateRunScore.h" +#import "SDLDynamicMenuUpdateAlgorithm.h" + NS_ASSUME_NONNULL_BEGIN @implementation SDLDynamicMenuUpdateRunScore @@ -23,6 +25,25 @@ - (instancetype)initWithOldStatus:(NSArray *)oldStatus updatedStatus return self; } +- (NSString *)description { + return [NSString stringWithFormat:@"Run Score: %ld, old status: %@, updated status: %@", (long)self.score, [self sdl_stringArrayForCellUpdateStatuses:self.oldStatus], [self sdl_stringArrayForCellUpdateStatuses:self.updatedStatus]]; +} + +- (NSArray *)sdl_stringArrayForCellUpdateStatuses:(NSArray *)statuses { + NSMutableArray *mutableStringArray = [NSMutableArray arrayWithCapacity:statuses.count]; + for (NSNumber *status in statuses) { + if (status.unsignedIntegerValue == SDLMenuCellUpdateStateDelete) { + [mutableStringArray addObject:@"DELETE"]; + } else if (status.unsignedIntegerValue == SDLMenuCellUpdateStateAdd) { + [mutableStringArray addObject:@"ADD"]; + } else if (status.unsignedIntegerValue == SDLMenuCellUpdateStateKeep) { + [mutableStringArray addObject:@"KEEP"]; + } + } + + return [mutableStringArray copy]; +} + @end NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 5d7a04d34..fcc2cae25 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -70,7 +70,7 @@ - (void)start { SDLDynamicMenuUpdateRunScore *runScore = nil; if (self.compatbilityModeEnabled) { SDLLogV(@"Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); - runScore = [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[self sdl_buildAllDeleteStatusesForMenu:self.mutableCurrentMenu] updatedStatus:[self sdl_buildAllAddStatusesForMenu:self.updatedMenu] score:self.updatedMenu.count]; + runScore = [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[self sdl_buildAllDeleteStatusesForMenu:self.currentMenu] updatedStatus:[self sdl_buildAllAddStatusesForMenu:self.updatedMenu] score:self.updatedMenu.count]; } else { SDLLogV(@"Dynamic menu update active. Running the algorithm to find the best way to delete / add cells."); runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; @@ -262,7 +262,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA // The index of the status should correlate 1-1 with the number of items in the menu // [2,0,2,0] => [A,B,C,D] = [B,D] for (NSUInteger index = 0; index < oldStatusList.count; index++) { - if (oldStatusList[index].integerValue == MenuCellStateDelete) { + if (oldStatusList[index].integerValue == SDLMenuCellUpdateStateDelete) { [deleteCells addObject:oldMenuCells[index]]; } } @@ -274,7 +274,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA // The index of the status should corrleate 1-1 with the number of items in the menu // [2,1,2,1] => [A,B,C,D] = [B,D] for (NSUInteger index = 0; index < newStatusList.count; index++) { - if (newStatusList[index].integerValue == MenuCellStateAdd) { + if (newStatusList[index].integerValue == SDLMenuCellUpdateStateAdd) { [addCells addObject:newMenuCells[index]]; } } @@ -285,7 +285,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA NSMutableArray *keepMenuCells = [[NSMutableArray alloc] init]; for (NSUInteger index = 0; index < keepStatusList.count; index++) { - if (keepStatusList[index].integerValue == MenuCellStateKeep) { + if (keepStatusList[index].unsignedIntegerValue == SDLMenuCellUpdateStateKeep) { [keepMenuCells addObject:oldMenuCells[index]]; } } @@ -295,7 +295,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA - (NSArray *)sdl_filterKeepMenuItemsWithNewMenuItems:(NSArray *)newMenuCells basedOnStatusList:(NSArray *)keepStatusList { NSMutableArray *keepMenuCells = [[NSMutableArray alloc] init]; for (NSUInteger index = 0; index < keepStatusList.count; index++) { - if (keepStatusList[index].integerValue == MenuCellStateKeep) { + if (keepStatusList[index].unsignedIntegerValue == SDLMenuCellUpdateStateKeep) { [keepMenuCells addObject:newMenuCells[index]]; } } @@ -312,7 +312,7 @@ - (void)sdl_transferCellIDFromOldCells:(NSArray *)oldCells toKept - (NSArray *)sdl_buildAllDeleteStatusesForMenu:(NSArray *)menuCells { NSMutableArray *mutableNumbers = [NSMutableArray arrayWithCapacity:menuCells.count]; for (int i = 0; i < menuCells.count; i++) { - [mutableNumbers addObject:@(MenuCellStateDelete)]; + [mutableNumbers addObject:@(SDLMenuCellUpdateStateDelete)]; } return [mutableNumbers copy]; @@ -321,7 +321,7 @@ - (void)sdl_transferCellIDFromOldCells:(NSArray *)oldCells toKept - (NSArray *)sdl_buildAllAddStatusesForMenu:(NSArray *)menuCells { NSMutableArray *mutableNumbers = [NSMutableArray arrayWithCapacity:menuCells.count]; for (int i = 0; i < menuCells.count; i++) { - [mutableNumbers addObject:@(MenuCellStateDelete)]; + [mutableNumbers addObject:@(SDLMenuCellUpdateStateAdd)]; } return [mutableNumbers copy]; diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index b8e095b05..6d0479e0d 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -40,7 +40,7 @@ @implementation SDLMenuReplaceUtilities NSMutableSet *mutableArtworks = [NSMutableSet set]; for (SDLMenuCell *cell in cells) { - if ([fileManager fileNeedsUpload:cell.icon]) { + if ((cell.icon != nil) && [fileManager fileNeedsUpload:cell.icon]) { [mutableArtworks addObject:cell.icon]; } diff --git a/SmartDeviceLink/public/SDLMenuCell.h b/SmartDeviceLink/public/SDLMenuCell.h index 403294612..2bbbbd5a2 100644 --- a/SmartDeviceLink/public/SDLMenuCell.h +++ b/SmartDeviceLink/public/SDLMenuCell.h @@ -23,7 +23,7 @@ The handler to run when a menu item is selected. typedef void(^SDLMenuCellSelectionHandler)(SDLTriggerSource triggerSource); /// A menu cell item for the main menu or sub-menu. -@interface SDLMenuCell : NSObject +@interface SDLMenuCell : NSObject /** The cell's text to be displayed diff --git a/SmartDeviceLink/public/SDLMenuCell.m b/SmartDeviceLink/public/SDLMenuCell.m index 4655aee15..707c7ab67 100644 --- a/SmartDeviceLink/public/SDLMenuCell.m +++ b/SmartDeviceLink/public/SDLMenuCell.m @@ -17,6 +17,7 @@ @interface SDLMenuCell() @property (assign, nonatomic) UInt32 parentCellId; @property (assign, nonatomic) UInt32 cellId; +@property (copy, nonatomic, readwrite, nullable) NSArray *subCells; @end @@ -79,6 +80,18 @@ - (BOOL)isEqualToCell:(SDLMenuCell *)cell { return (self.hash == cell.hash); } +#pragma mark - Copying + +- (id)copyWithZone:(nullable NSZone *)zone { + SDLMenuCell *copy = [[SDLMenuCell allocWithZone:zone] initWithTitle:_title icon:[_icon copy] submenuLayout:_submenuLayout subCells:[_subCells copy]]; + copy->_voiceCommands = [_voiceCommands copy]; + copy->_cellId = _cellId; + copy->_parentCellId = _parentCellId; + copy->_handler = [_handler copy]; + + return copy; +} + @end NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index b5ff6253c..f142bde91 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -12,6 +12,7 @@ #import #import "SDLMenuReplaceOperation.h" +#import "SDLMenuReplaceUtilities.h" #import "TestConnectionManager.h" QuickSpecBegin(SDLMenuReplaceOperationSpec) @@ -39,6 +40,8 @@ __block NSArray *newCurrentMenuCells = nil; __block SDLCurrentMenuUpdatedBlock testCurrentMenuUpdatedBlock = nil; + __block SDLMenuReplaceUtilities *mockReplaceUtilities = nil; + beforeEach(^{ testArtwork = [[SDLArtwork alloc] initWithData:[@"Test data" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO]; testArtwork2 = [[SDLArtwork alloc] initWithData:[@"Test data 2" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name 2" fileExtension:@"png" persistent:NO]; @@ -54,7 +57,9 @@ testOp = nil; testConnectionManager = [[TestConnectionManager alloc] init]; testFileManager = OCMClassMock([SDLFileManager class]); - testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil]; + + SDLImageField *iconImageField = [[SDLImageField alloc] initWithName:SDLImageFieldNameCommandIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil]; + testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:@[iconImageField] imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil]; testMenuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutList defaultSubmenuLayout:SDLMenuLayoutList]; testCurrentMenu = @[]; testNewMenu = nil; @@ -63,112 +68,137 @@ testCurrentMenuUpdatedBlock = ^(NSArray *currentMenuCells) { newCurrentMenuCells = currentMenuCells; }; + + mockReplaceUtilities = OCMClassMock([SDLMenuReplaceUtilities class]); }); + // sending initial batch of cells describe(@"sending initial batch of cells", ^{ - context(@"when uploading text and image cells", ^{ - it(@"should check if all artworks are uploaded and return NO", ^{ - textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - testNewMenu = @[textAndImageCell]; + + // when setting no cells + context(@"when setting no cells", ^{ + it(@"should finish without doing anything", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; -// testManager.menuCells = @[textAndImageCell, textOnlyCell]; -// expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beFalse()); + [testOp start]; + + expect(testConnectionManager.receivedRequests).to(beEmpty()); + expect(testOp.isFinished).to(beTrue()); }); }); - }); -// it(@"should properly update a text cell", ^{ -// testManager.menuCells = @[textOnlyCell]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// expect(deletes).to(beEmpty()); -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; -// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// expect(add).toNot(beEmpty()); -// }); -// -// it(@"should properly update with subcells", ^{ -// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); -// testManager.menuCells = @[submenuCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// NSPredicate *submenuCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenus = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:submenuCommandPredicate]; -// -// expect(adds).to(haveCount(2)); -// expect(submenus).to(haveCount(1)); -// }); -// }); -// -// describe(@"updating with an image", ^{ -// context(@"when the image is already on the head unit", ^{ -// beforeEach(^{ -// OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES); -// }); -// -// it(@"should check if all artworks are uploaded", ^{ -// textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; -// testManager.menuCells = @[textAndImageCell, textOnlyCell]; -// OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); -// expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beTrue()); -// }); -// -// it(@"should properly update an image cell", ^{ -// testManager.menuCells = @[textAndImageCell, submenuImageCell]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; -// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// SDLAddCommand *sentCommand = add.firstObject; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// SDLAddSubMenu *sentSubmenu = submenu.firstObject; -// -// expect(add).to(haveCount(1)); -// expect(submenu).to(haveCount(1)); -// expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name)); -// expect(sentSubmenu.menuIcon.value).to(equal(testArtwork2.name)); -// OCMReject([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); -// }); -// -// it(@"should properly overwrite an image cell", ^{ -// OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); -// textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; -// testManager.menuCells = @[textAndImageCell, submenuImageCell]; -// OCMVerify([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); -// }); -// }); -// -// // No longer a valid unit test -// context(@"when the image is not on the head unit", ^{ -// beforeEach(^{ -// testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; -// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); -// }); -// -// it(@"should wait till image is on head unit and attempt to update without the image", ^{ -// testManager.menuCells = @[textAndImageCell, submenuImageCell]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; -// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// SDLAddCommand *sentCommand = add.firstObject; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// SDLAddSubMenu *sentSubmenu = submenu.firstObject; -// -// expect(add).to(haveCount(1)); -// expect(submenu).to(haveCount(1)); -// expect(sentCommand.cmdIcon.value).to(beNil()); -// expect(sentSubmenu.menuIcon.value).to(beNil()); + // when uploading text and image cell + context(@"when uploading text and image cell", ^{ + beforeEach(^{ + testNewMenu = @[textAndImageCell]; + + OCMStub([testFileManager fileNeedsUpload:[OCMArg any]]).andReturn(YES); + OCMStub([testFileManager uploadArtworks:[OCMArg any] progressHandler:([OCMArg invokeBlockWithArgs:textAndImageCell.icon.name, @1.0, [NSNull null], nil]) completionHandler:([OCMArg invokeBlockWithArgs: @[textAndImageCell.icon.name], [NSNull null], nil])]); + }); + + // when the image is already on the head unit +// context(@"when the image is already on the head unit", ^{ +// it(@"should check if all artworks are uploaded", ^{ +// textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; +// testManager.menuCells = @[textAndImageCell, textOnlyCell]; +// OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); +// expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beTrue()); +// }); +// +// it(@"should properly update an image cell", ^{ +// testManager.menuCells = @[textAndImageCell, submenuImageCell]; +// +// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; +// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; +// SDLAddCommand *sentCommand = add.firstObject; +// +// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; +// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; +// SDLAddSubMenu *sentSubmenu = submenu.firstObject; +// +// expect(add).to(haveCount(1)); +// expect(submenu).to(haveCount(1)); +// expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name)); +// expect(sentSubmenu.menuIcon.value).to(equal(testArtwork2.name)); +// OCMReject([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); +// }); // }); -// }); -// }); + + // when the image is not on the head unit + context(@"when the image is not on the head unit", ^{ + it(@"should attempt to upload artworks then send the add", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + OCMVerify([testFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]); + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + expect(deletes).to(beEmpty()); + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; + NSArray *add = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + expect(add).toNot(beEmpty()); + }); + }); + + it(@"should properly overwrite an image cell", ^{ + OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); + textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + testManager.menuCells = @[textAndImageCell, submenuImageCell]; + OCMVerify([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); + }); + }); + + // when uploading a text-only cell + context(@"when uploading a text-only cell", ^{ + beforeEach(^{ + testNewMenu = @[textOnlyCell]; + OCMStub([testFileManager fileNeedsUpload:[OCMArg any]]).andReturn(NO); + }); + + it(@"should properly update a text cell", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + OCMReject([testFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]); + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + expect(deletes).to(beEmpty()); + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; + NSArray *add = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + expect(add).toNot(beEmpty()); + }); + }); + + // when uploading a cell with subcells + context(@"when uploading a cell with subcells", ^{ + beforeEach(^{ + testNewMenu = @[submenuCell]; + }); + + it(@"should send an appropriate number of AddSubmenu and AddCommandRequests", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + SDLAddSubMenuResponse *response = [[SDLAddSubMenuResponse alloc] init]; + response.success = @YES; + response.resultCode = SDLResultSuccess; + [testConnectionManager respondToLastRequestWithResponse:response]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + NSPredicate *submenuCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; + NSArray *submenus = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:submenuCommandPredicate]; + + expect(adds).to(haveCount(2)); + expect(submenus).to(haveCount(1)); + }); + }); + }); // // describe(@"updating when a menu already exists with dynamic updates on", ^{ // beforeEach(^{ From 34d82e0232f9045f33ade13d18e88b11d48bf48e Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 18 Feb 2021 16:06:58 -0500 Subject: [PATCH 041/112] Remove a few additional items --- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index f142bde91..92760661c 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -91,19 +91,12 @@ beforeEach(^{ testNewMenu = @[textAndImageCell]; - OCMStub([testFileManager fileNeedsUpload:[OCMArg any]]).andReturn(YES); + OCMStub([testFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); OCMStub([testFileManager uploadArtworks:[OCMArg any] progressHandler:([OCMArg invokeBlockWithArgs:textAndImageCell.icon.name, @1.0, [NSNull null], nil]) completionHandler:([OCMArg invokeBlockWithArgs: @[textAndImageCell.icon.name], [NSNull null], nil])]); }); // when the image is already on the head unit // context(@"when the image is already on the head unit", ^{ -// it(@"should check if all artworks are uploaded", ^{ -// textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; -// testManager.menuCells = @[textAndImageCell, textOnlyCell]; -// OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]); -// expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beTrue()); -// }); -// // it(@"should properly update an image cell", ^{ // testManager.menuCells = @[textAndImageCell, submenuImageCell]; // @@ -140,13 +133,6 @@ expect(add).toNot(beEmpty()); }); }); - - it(@"should properly overwrite an image cell", ^{ - OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); - textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - testManager.menuCells = @[textAndImageCell, submenuImageCell]; - OCMVerify([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); - }); }); // when uploading a text-only cell From 317e27019e3a1f70b2568960a7a03de59bdb27d5 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 18 Feb 2021 16:19:48 -0500 Subject: [PATCH 042/112] Additional test work --- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 92760661c..fc82dc46e 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -96,25 +96,24 @@ }); // when the image is already on the head unit -// context(@"when the image is already on the head unit", ^{ -// it(@"should properly update an image cell", ^{ -// testManager.menuCells = @[textAndImageCell, submenuImageCell]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; -// NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// SDLAddCommand *sentCommand = add.firstObject; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// SDLAddSubMenu *sentSubmenu = submenu.firstObject; -// -// expect(add).to(haveCount(1)); -// expect(submenu).to(haveCount(1)); -// expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name)); -// expect(sentSubmenu.menuIcon.value).to(equal(testArtwork2.name)); -// OCMReject([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]); -// }); -// }); + context(@"when the image is already on the head unit", ^{ + beforeEach(^{ + OCMStub([testFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(NO); + }); + + it(@"should properly update an image cell", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; + NSArray *add = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + SDLAddCommand *sentCommand = add.firstObject; + + expect(add).to(haveCount(1)); + expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name)); + OCMReject([testFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]); + }); + }); // when the image is not on the head unit context(@"when the image is not on the head unit", ^{ From 535c872ed63e4a6fde5719e4269cad61a5bd04b3 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 19 Feb 2021 09:12:42 -0500 Subject: [PATCH 043/112] Slight change to test --- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index fc82dc46e..59c851efa 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -91,7 +91,6 @@ beforeEach(^{ testNewMenu = @[textAndImageCell]; - OCMStub([testFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); OCMStub([testFileManager uploadArtworks:[OCMArg any] progressHandler:([OCMArg invokeBlockWithArgs:textAndImageCell.icon.name, @1.0, [NSNull null], nil]) completionHandler:([OCMArg invokeBlockWithArgs: @[textAndImageCell.icon.name], [NSNull null], nil])]); }); @@ -101,7 +100,7 @@ OCMStub([testFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(NO); }); - it(@"should properly update an image cell", ^{ + fit(@"should properly update an image cell", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; [testOp start]; @@ -117,6 +116,10 @@ // when the image is not on the head unit context(@"when the image is not on the head unit", ^{ + beforeEach(^{ + OCMStub([testFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); + }); + it(@"should attempt to upload artworks then send the add", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; [testOp start]; From cb4e3dbc52f4a0caf3ae25e6e52233bf9ead089e Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 19 Feb 2021 13:59:53 -0500 Subject: [PATCH 044/112] Fix some spelling and a menu operation bug * Fix checking wrong image field causing no images to be uploaded --- SmartDeviceLink/private/SDLMenuReplaceOperation.h | 2 +- SmartDeviceLink/private/SDLMenuReplaceOperation.m | 14 +++++++------- SmartDeviceLink/private/SDLMenuReplaceUtilities.m | 4 ++-- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 7 +++++-- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.h b/SmartDeviceLink/private/SDLMenuReplaceOperation.h index fb8d48881..e4057b7cd 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.h +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.h @@ -29,7 +29,7 @@ typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCel @property (strong, nonatomic) SDLMenuConfiguration *menuConfiguration; @property (strong, nonatomic) NSArray *currentMenu; -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatbilityModeEnabled currentMenuUpdatedHandler:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock; +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatibilityModeEnabled currentMenuUpdatedHandler:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index fcc2cae25..793465769 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -37,7 +37,7 @@ @interface SDLMenuReplaceOperation () @property (weak, nonatomic) id connectionManager; @property (weak, nonatomic) SDLFileManager *fileManager; @property (strong, nonatomic) NSArray *updatedMenu; -@property (assign, nonatomic) BOOL compatbilityModeEnabled; +@property (assign, nonatomic) BOOL compatibilityModeEnabled; @property (assign, nonatomic) SDLCurrentMenuUpdatedBlock currentMenuUpdatedBlock; @property (strong, nonatomic) NSMutableArray *mutableCurrentMenu; @@ -47,7 +47,7 @@ @interface SDLMenuReplaceOperation () @implementation SDLMenuReplaceOperation -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatbilityModeEnabled currentMenuUpdatedHandler:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock { +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatibilityModeEnabled currentMenuUpdatedHandler:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock { self = [super init]; if (!self) { return nil; } @@ -57,7 +57,7 @@ - (instancetype)initWithConnectionManager:(id)connecti _menuConfiguration = menuConfiguration; _mutableCurrentMenu = [currentMenu mutableCopy]; _updatedMenu = updatedMenu; - _compatbilityModeEnabled = compatbilityModeEnabled; + _compatibilityModeEnabled = compatibilityModeEnabled; _currentMenuUpdatedBlock = currentMenuUpdatedBlock; return self; @@ -68,7 +68,7 @@ - (void)start { if (self.isCancelled) { return; } SDLDynamicMenuUpdateRunScore *runScore = nil; - if (self.compatbilityModeEnabled) { + if (self.compatibilityModeEnabled) { SDLLogV(@"Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); runScore = [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[self sdl_buildAllDeleteStatusesForMenu:self.currentMenu] updatedStatus:[self sdl_buildAllAddStatusesForMenu:self.updatedMenu] score:self.updatedMenu.count]; } else { @@ -87,7 +87,7 @@ - (void)start { NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:runScore.oldStatus]; NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; - // Since we are creating a new Menu but keeping old cells we must firt transfer the old cellIDs to the new menus kept cells. + // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. [self sdl_transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; // Upload the artworks, then we will start updating the main menu @@ -189,7 +189,7 @@ - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuC if (error != nil) { errors[request] = error; } else if (response.success.boolValue) { - // Find the id of the successful request and remove it from the current menu list whereever it may have been + // Find the id of the successful request and remove it from the current menu list wherever it may have been UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; [SDLMenuReplaceUtilities removeMenuCellFromList:self.mutableCurrentMenu withCmdId:commandId]; } @@ -271,7 +271,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA - (NSArray *)sdl_filterAddMenuItemsWithNewMenuItems:(NSArray *)newMenuCells basedOnStatusList:(NSArray *)newStatusList { NSMutableArray *addCells = [[NSMutableArray alloc] init]; - // The index of the status should corrleate 1-1 with the number of items in the menu + // The index of the status should correlate 1-1 with the number of items in the menu // [2,1,2,1] => [A,B,C,D] = [B,D] for (NSUInteger index = 0; index < newStatusList.count; index++) { if (newStatusList[index].integerValue == SDLMenuCellUpdateStateAdd) { diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 6d0479e0d..d0a4e3de1 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -54,8 +54,8 @@ @implementation SDLMenuReplaceUtilities + (BOOL)sdl_shouldCellIncludeImage:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image - BOOL supportsImage = (cell.subCells.count > 0) ? [windowCapability hasImageFieldOfName:SDLImageFieldNameSubMenuIcon] : [windowCapability hasImageFieldOfName:SDLImageFieldNameMenuIcon]; - return cell.icon != nil && supportsImage && ([fileManager hasUploadedFile:cell.icon] || cell.icon.isStaticIcon); + BOOL supportsImage = (cell.subCells.count > 0) ? [windowCapability hasImageFieldOfName:SDLImageFieldNameSubMenuIcon] : [windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]; + return (cell.icon != nil) && supportsImage && ([fileManager hasUploadedFile:cell.icon] || cell.icon.isStaticIcon); } #pragma mark - RPC Commands diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 59c851efa..922dfc610 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -58,8 +58,9 @@ testConnectionManager = [[TestConnectionManager alloc] init]; testFileManager = OCMClassMock([SDLFileManager class]); - SDLImageField *iconImageField = [[SDLImageField alloc] initWithName:SDLImageFieldNameCommandIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil]; - testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:@[iconImageField] imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil]; + SDLImageField *commandImageField = [[SDLImageField alloc] initWithName:SDLImageFieldNameCommandIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil]; + SDLImageField *submenuImageField = [[SDLImageField alloc] initWithName:SDLImageFieldNameSubMenuIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil]; + testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:@[commandImageField, submenuImageField] imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil]; testMenuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutList defaultSubmenuLayout:SDLMenuLayoutList]; testCurrentMenu = @[]; testNewMenu = nil; @@ -97,6 +98,7 @@ // when the image is already on the head unit context(@"when the image is already on the head unit", ^{ beforeEach(^{ + OCMStub([testFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES); OCMStub([testFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(NO); }); @@ -117,6 +119,7 @@ // when the image is not on the head unit context(@"when the image is not on the head unit", ^{ beforeEach(^{ + OCMStub([testFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(NO); OCMStub([testFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES); }); From 38f8f4b09e015fe1e4e8429d22411fc0bfa03d1c Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 19 Feb 2021 16:36:35 -0500 Subject: [PATCH 045/112] Fix menu bug and updating unit tests --- .../private/SDLMenuReplaceUtilities.m | 9 +- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 83 ++++++++++++------- 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index d0a4e3de1..8bad8a981 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -225,8 +225,15 @@ + (BOOL)sdl_addMenuCell:(SDLMenuCell *)cell toList:(NSMutableArray *newList = [menuCell.subCells mutableCopy]; + NSMutableArray *newList = nil; + if (menuCell.subCells != nil) { + newList = [menuCell.subCells mutableCopy]; + } else { + newList = [NSMutableArray array]; + } + [self sdl_insertMenuCell:cell intoList:newList atPosition:position]; + menuCell.subCells = [newList copy]; return YES; } else if (menuCell.subCells.count > 0) { // Check the subcells of this cell to see if any of those have cell ids that match the parent cell id diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 922dfc610..0740d23af 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -75,7 +75,6 @@ // sending initial batch of cells describe(@"sending initial batch of cells", ^{ - // when setting no cells context(@"when setting no cells", ^{ it(@"should finish without doing anything", ^{ @@ -102,7 +101,7 @@ OCMStub([testFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(NO); }); - fit(@"should properly update an image cell", ^{ + it(@"should properly update an image cell", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; [testOp start]; @@ -190,32 +189,60 @@ }); }); }); -// -// describe(@"updating when a menu already exists with dynamic updates on", ^{ -// beforeEach(^{ -// testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOn; -// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); -// }); -// -// it(@"should send deletes first", ^{ -// testManager.menuCells = @[textOnlyCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// expect(deletes).to(haveCount(1)); -// expect(adds).to(haveCount(2)); -// }); -// + + // updating a menu without dynamic updates + describe(@"updating a menu without dynamic updates", ^{ + context(@"adding a text cell", ^{ + beforeEach(^{ + testCurrentMenu = @[textOnlyCell]; + testNewMenu = @[textOnlyCell, textOnlyCell2]; + }); + + fit(@"should send a delete and two adds", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + SDLDeleteCommandResponse *deleteCommandResponse = [[SDLDeleteCommandResponse alloc] init]; + deleteCommandResponse.success = @YES; + deleteCommandResponse.resultCode = SDLResultSuccess; + [testConnectionManager respondToLastRequestWithResponse:deleteCommandResponse]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(1)); + expect(adds).to(haveCount(2)); + }); + }); + }); + + // updating a menu with dynamic updates + describe(@"updating a menu with dynamic updates", ^{ + context(@"adding a text cell", ^{ + beforeEach(^{ + testCurrentMenu = @[textOnlyCell]; + testNewMenu = @[textOnlyCell, textOnlyCell2]; + }); + + it(@"should only send an add", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(0)); + expect(adds).to(haveCount(1)); + }); + }); + }); + // it(@"should send dynamic deletes first then dynamic adds case with 2 submenu cells", ^{ // testManager.menuCells = @[textOnlyCell, submenuCell, submenuImageCell]; // [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; From 44a7bf81c8fdeb7510958d47b5e50c9364e0d53a Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 24 Feb 2021 15:58:31 -0500 Subject: [PATCH 046/112] Continuing to add tests --- .../DevAPISpecs/SDLMenuManagerSpec.m | 380 ++++++++++-------- 1 file changed, 212 insertions(+), 168 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index 2c3c0313e..a7763f098 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -68,6 +68,7 @@ @interface SDLMenuManager() testManager.windowCapability = windowCapability; }); + // should instantiate correctly it(@"should instantiate correctly", ^{ expect(testManager.menuCells).to(beEmpty()); @@ -84,7 +85,32 @@ @interface SDLMenuManager() expect(testManager.lastMenuId).to(equal(1)); }); - describe(@"updating menu cells before HMI is ready", ^{ + // when the manager stops + describe(@"when the manager stops", ^{ + beforeEach(^{ + [testManager stop]; + }); + + it(@"should reset correctly", ^{ + expect(testManager.menuCells).to(beEmpty()); + + expect(@(testManager.dynamicMenuUpdatesMode)).to(equal(@(SDLDynamicMenuUpdatesModeOnWithCompatibility))); + expect(testManager.connectionManager).to(equal(mockConnectionManager)); + expect(testManager.fileManager).to(equal(mockFileManager)); + expect(testManager.systemCapabilityManager).to(equal(mockSystemCapabilityManager)); + expect(testManager.transactionQueue).toNot(beNil()); + expect(testManager.windowCapability).to(beNil()); + expect(testManager.currentHMILevel).to(beNil()); + expect(testManager.currentSystemContext).to(beNil()); + expect(testManager.currentMenuCells).to(beEmpty()); + expect(testManager.currentMenuConfiguration).to(beNil()); + expect(testManager.lastMenuId).to(equal(1)); + }); + }); + + // before the HMI is ready + context(@"before the HMI is ready", ^{ + // when in HMI NONE context(@"when in HMI NONE", ^{ beforeEach(^{ testManager.currentHMILevel = SDLHMILevelNone; @@ -107,57 +133,220 @@ @interface SDLMenuManager() }); it(@"should update", ^{ - expect(testManager.transactionQueue.operationCount).to(equal(1)); + expect(testManager.transactionQueue.isSuspended).to(beFalse()); }); }); }); + // when no HMI level has been received context(@"when no HMI level has been received", ^{ beforeEach(^{ testManager.currentHMILevel = nil; }); - it(@"should not update the menu configuration", ^{ + // should queue the menu configuration + it(@"should queue the menu configuration", ^{ testManager.menuConfiguration = testMenuConfiguration; - expect(mockConnectionManager.receivedRequests).to(beEmpty()); - expect(testManager.menuConfiguration).toNot(equal(testMenuConfiguration)); + expect(testManager.transactionQueue.operationCount).to(equal(1)); + expect(testManager.transactionQueue.isSuspended).to(beTrue()); + expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); }); - it(@"should not open the menu", ^{ - + // should queue opening the menu + it(@"should queue opening the menu", ^{ + [testManager openMenu:nil]; + expect(testManager.transactionQueue.operationCount).to(equal(1)); + expect(testManager.transactionQueue.isSuspended).to(beTrue()); }); - it(@"should not update the menu cells", ^{ - testManager.menuCells = @[textOnlyCell]; - expect(mockConnectionManager.receivedRequests).to(beEmpty()); + // should queue updating the menu cells + it(@"should queue updating the menu cells", ^{ + NSArray *menuCells = @[textOnlyCell]; + testManager.menuCells = menuCells; + expect(testManager.transactionQueue.operationCount).to(equal(1)); + expect(testManager.transactionQueue.isSuspended).to(beTrue()); + expect(testManager.menuCells).to(equal(menuCells)); }); }); }); - describe(@"updating menu cells", ^{ + // when the HMI is ready + context(@"when the HMI is ready", ^{ beforeEach(^{ testManager.currentHMILevel = SDLHMILevelFull; testManager.currentSystemContext = SDLSystemContextMain; }); - context(@"duplicate titles", ^{ - it(@"should fail with a duplicate title", ^{ - testManager.menuCells = @[textOnlyCell, textOnlyCell]; - expect(testManager.menuCells).to(beEmpty()); + // updating menu cells + describe(@"updating menu cells", ^{ + // containing duplicate titles + context(@"containing duplicate titles", ^{ + it(@"should fail with a duplicate title", ^{ + testManager.menuCells = @[textOnlyCell, textOnlyCell]; + expect(testManager.menuCells).to(beEmpty()); // TODO: Fix, add op count + }); }); + + // containing duplicate VR commands + context(@"containing duplicate VR commands", ^{ + __block SDLMenuCell *textAndVRCell1 = [[SDLMenuCell alloc] initWithTitle:@"Test 1" icon:nil voiceCommands:@[@"Cat", @"Turtle"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + __block SDLMenuCell *textAndVRCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 3" icon:nil voiceCommands:@[@"Cat", @"Dog"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + + it(@"should fail when menu items have duplicate vr commands", ^{ + testManager.menuCells = @[textAndVRCell1, textAndVRCell2]; + expect(testManager.menuCells).to(beEmpty()); // TODO Fix, add op count + }); + }); + + context(@"if the new menu cells are identical to the old menu cells", ^{ + it(@"should only queue one transaction", ^{ + testManager.menuCells = @[textOnlyCell]; + testManager.menuCells = @[textOnlyCell]; + + expect(testManager.menuCells).to(equal(@[textOnlyCell])); + expect(testManager.transactionQueue.operationCount).to(equal(1)); + }); + }); + + // TODO: Add tests for normal setting, including tests for ids, canceling old tasks (including on menu config and opening menu), updating current menu cells }); - context(@"duplicate VR commands", ^{ - __block SDLMenuCell *textAndVRCell1 = [[SDLMenuCell alloc] initWithTitle:@"Test 1" icon:nil voiceCommands:@[@"Cat", @"Turtle"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - __block SDLMenuCell *textAndVRCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 3" icon:nil voiceCommands:@[@"Cat", @"Dog"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + // updating the menu configuration + describe(@"updating the menu configuration", ^{ + beforeEach(^{ + testManager.currentHMILevel = SDLHMILevelFull; + testManager.currentSystemContext = SDLSystemContextMain; + }); + + // if the connection RPC version is less than 6.0.0 + context(@"if the connection RPC version is less than 6.0.0", ^{ + beforeEach(^{ + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.0.0"]; + }); + + it(@"should not queue a menu configuration update", ^{ + testManager.menuConfiguration = testMenuConfiguration; + + expect(testManager.menuConfiguration).toNot(equal(testMenuConfiguration)); + expect(testManager.transactionQueue.operationCount).to(equal(0)); + }); + }); - it(@"should fail when menu items have duplicate vr commands", ^{ - testManager.menuCells = @[textAndVRCell1, textAndVRCell2]; - expect(testManager.menuCells).to(beEmpty()); + // if the connection RPC version is greater than or equal to 6.0.0 + context(@"if the connection RPC version is greater than or equal to 6.0.0", ^{ + beforeEach(^{ + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"6.0.0"]; + }); + + it(@"should should queue a menu configuration update", ^{ + testManager.menuConfiguration = testMenuConfiguration; + + expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); + expect(testManager.transactionQueue.operationCount).to(equal(1)); + }); + }); + + // when no HMI level has been received + context(@"when no HMI level has been received", ^{ + beforeEach(^{ + testManager.currentHMILevel = nil; + }); + + it(@"should queue a menu configuration update", ^{ + testManager.menuConfiguration = testMenuConfiguration; + + expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); + expect(testManager.transactionQueue.operationCount).to(equal(1)); + }); + }); + + // when in the menu + context(@"when in the menu", ^{ + beforeEach(^{ + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"6.0.0"]; + testManager.currentHMILevel = SDLHMILevelFull; + testManager.currentSystemContext = SDLSystemContextMenu; + }); + + it(@"should queue a menu configuration update", ^{ + testManager.menuConfiguration = testMenuConfiguration; + expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); + expect(testManager.transactionQueue.operationCount).to(equal(1)); + }); + }); + }); + + // opening the menu + describe(@"opening the menu", ^{ + beforeEach(^{ + testManager.currentHMILevel = SDLHMILevelFull; + testManager.currentSystemContext = SDLSystemContextMain; + }); + + // when open menu RPC can be sent + context(@"when open menu RPC can be sent", ^{ + beforeEach(^{ + SDLVersion *oldVersion = [SDLVersion versionWithMajor:6 minor:0 patch:0]; + id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]); + OCMStub([globalMock rpcVersion]).andReturn(oldVersion); + }); + + // should queue an open menu operation for the main menu + it(@"should queue an open menu operation for the main menu", ^{ + BOOL canSendRPC = [testManager openMenu:nil]; + + expect(testManager.transactionQueue.operationCount).to(equal(1)); + expect(canSendRPC).to(equal(YES)); + }); + + // should queue an open menu operation for a submenu cell + it(@"should queue an open menu operation for a submenu cell", ^ { + testManager.menuCells = @[submenuCell]; + BOOL canSendRPC = [testManager openMenu:submenuCell]; + + expect(testManager.transactionQueue.operationCount).to(equal(2)); + expect(canSendRPC).to(equal(YES)); + }); + }); + + // when open menu RPC can not be sent + context(@"when the open menu RPC can not be sent", ^{ + // should not queue an open menu operation when cell has no subcells + it(@"should not queue an open menu operation when cell has no subcells", ^ { + BOOL canSendRPC = [testManager openMenu:textOnlyCell]; + + expect(testManager.transactionQueue.operationCount).to(equal(0)); + expect(canSendRPC).to(equal(NO)); + }); + + // should not queue an open menu operation when RPC version is not at least 6.0.0 + it(@"should not queue an open menu operation when RPC version is not at least 6.0.0", ^ { + SDLVersion *oldVersion = [SDLVersion versionWithMajor:5 minor:0 patch:0]; + id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]); + OCMStub([globalMock rpcVersion]).andReturn(oldVersion); + + BOOL canSendRPC = [testManager openMenu:submenuCell]; + + expect(testManager.transactionQueue.operationCount).to(equal(0)); + expect(canSendRPC).to(equal(NO)); + }); + + // should not queue an open menu operation when the cell is not in the menu array + it(@"should not queue an open menu operation when the cell is not in the menu array", ^ { + SDLVersion *oldVersion = [SDLVersion versionWithMajor:6 minor:0 patch:0]; + id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]); + OCMStub([globalMock rpcVersion]).andReturn(oldVersion); + + BOOL canSendRPC = [testManager openMenu:submenuCell]; + + expect(testManager.transactionQueue.operationCount).to(equal(0)); + expect(canSendRPC).to(equal(NO)); + }); }); }); }); + // running menu cell handlers describe(@"running menu cell handlers", ^{ __block SDLMenuCell *cellWithHandler = nil; __block BOOL cellCalled = NO; @@ -171,6 +360,7 @@ @interface SDLMenuManager() testTriggerSource = nil; }); + // on a main menu cell context(@"on a main menu cell", ^{ beforeEach(^{ cellWithHandler = [[SDLMenuCell alloc] initWithTitle:@"Hello" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { @@ -194,6 +384,7 @@ @interface SDLMenuManager() }); }); + // on a submenu menu cell context(@"on a submenu menu cell", ^{ beforeEach(^{ cellWithHandler = [[SDLMenuCell alloc] initWithTitle:@"Hello" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { @@ -219,153 +410,6 @@ @interface SDLMenuManager() }); }); }); - - describe(@"updating the menu configuration", ^{ - beforeEach(^{ - testManager.currentHMILevel = SDLHMILevelFull; - testManager.currentSystemContext = SDLSystemContextMain; - }); - - context(@"if the connection RPC version is less than 6.0.0", ^{ - beforeEach(^{ - [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.0.0"]; - }); - - it(@"should fail to send a SetGlobalProperties RPC update", ^{ - testManager.menuConfiguration = testMenuConfiguration; - - expect(testManager.menuConfiguration).toNot(equal(testMenuConfiguration)); - expect(testManager.transactionQueue.operationCount).to(equal(0)); - }); - }); - - context(@"if the connection RPC version is greater than or equal to 6.0.0", ^{ - beforeEach(^{ - [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"6.0.0"]; - }); - - it(@"should send a SetGlobalProperties RPC update", ^{ - testManager.menuConfiguration = testMenuConfiguration; - - expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); - expect(testManager.transactionQueue.operationCount).to(equal(1)); - }); - }); - - context(@"when no HMI level has been received", ^{ - beforeEach(^{ - testManager.currentHMILevel = nil; - }); - - it(@"should queue the update to the menu configuration", ^{ - testManager.menuConfiguration = testMenuConfiguration; - - expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); - expect(testManager.transactionQueue.operationCount).to(equal(1)); - }); - }); - - context(@"when in the menu", ^{ - beforeEach(^{ - [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"6.0.0"]; - testManager.currentHMILevel = SDLHMILevelFull; - testManager.currentSystemContext = SDLSystemContextMenu; - }); - - it(@"should update the menu configuration", ^{ - testManager.menuConfiguration = testMenuConfiguration; - expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); - expect(testManager.transactionQueue.operationCount).to(equal(1)); - }); - }); - }); - - context(@"when the manager stops", ^{ - beforeEach(^{ - [testManager stop]; - }); - - it(@"should reset correctly", ^{ - expect(testManager.menuCells).to(beEmpty()); - - expect(@(testManager.dynamicMenuUpdatesMode)).to(equal(@(SDLDynamicMenuUpdatesModeOnWithCompatibility))); - expect(testManager.connectionManager).to(equal(mockConnectionManager)); - expect(testManager.fileManager).to(equal(mockFileManager)); - expect(testManager.systemCapabilityManager).to(equal(mockSystemCapabilityManager)); - expect(testManager.transactionQueue).toNot(beNil()); - expect(testManager.windowCapability).to(beNil()); - expect(testManager.currentHMILevel).to(beNil()); - expect(testManager.currentSystemContext).to(beNil()); - expect(testManager.currentMenuCells).to(beEmpty()); - expect(testManager.currentMenuConfiguration).to(beNil()); - expect(testManager.lastMenuId).to(equal(1)); - }); - }); - - describe(@"opening the menu", ^{ - beforeEach(^{ - testManager.currentHMILevel = SDLHMILevelFull; - testManager.currentSystemContext = SDLSystemContextMain; - }); - - context(@"when open menu RPC can be sent", ^{ - beforeEach(^{ - SDLVersion *oldVersion = [SDLVersion versionWithMajor:6 minor:0 patch:0]; - id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]); - OCMStub([globalMock rpcVersion]).andReturn(oldVersion); - }); - - it(@"should send showAppMenu RPC", ^{ - BOOL canSendRPC = [testManager openMenu:nil]; - - expect(testManager.transactionQueue.operationCount).to(equal(1)); - expect(canSendRPC).to(equal(YES)); - }); - - it(@"should send showAppMenu RPC with cellID", ^ { - testManager.menuCells = @[submenuCell]; - BOOL canSendRPC = [testManager openMenu:submenuCell]; - - expect(testManager.transactionQueue.operationCount).to(equal(2)); - expect(canSendRPC).to(equal(YES)); - }); - }); - - context(@"when open menu RPC can not be sent", ^{ - it(@"should not send a showAppMenu RPC when cell has no subcells", ^ { - BOOL canSendRPC = [testManager openMenu:textOnlyCell]; - - expect(testManager.transactionQueue.operationCount).to(equal(0)); - expect(canSendRPC).to(equal(NO)); - }); - - it(@"should not send a showAppMenu RPC when RPC verison is not at least 6.0.0", ^ { - SDLVersion *oldVersion = [SDLVersion versionWithMajor:5 minor:0 patch:0]; - id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]); - OCMStub([globalMock rpcVersion]).andReturn(oldVersion); - - BOOL canSendRPC = [testManager openMenu:submenuCell]; - - expect(testManager.transactionQueue.operationCount).to(equal(0)); - expect(canSendRPC).to(equal(NO)); - }); - - it(@"should not send a showAppMenu RPC when the cell is not in the menu array", ^ { - SDLVersion *oldVersion = [SDLVersion versionWithMajor:6 minor:0 patch:0]; - id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]); - OCMStub([globalMock rpcVersion]).andReturn(oldVersion); - - BOOL canSendRPC = [testManager openMenu:submenuCell]; - - expect(testManager.transactionQueue.operationCount).to(equal(0)); - expect(canSendRPC).to(equal(NO)); - }); - }); - }); - - afterEach(^{ - testManager = nil; - }); }); QuickSpecEnd From 745e6df7b45cadee48922acf4707427b8e3e9fb1 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 25 Feb 2021 15:05:14 -0500 Subject: [PATCH 047/112] Update menu replace op callback to pass back error too --- SmartDeviceLink/private/SDLMenuManager.m | 9 +-------- SmartDeviceLink/private/SDLMenuReplaceOperation.h | 2 +- SmartDeviceLink/private/SDLMenuReplaceOperation.m | 3 ++- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index af21b5b10..9bca7953c 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -209,18 +209,11 @@ - (void)setMenuCells:(NSArray *)menuCells { _menuCells = menuCells; __weak typeof(self) weakself = self; - SDLMenuReplaceOperation *menuReplaceOperation = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells compatibilityModeEnabled:(![self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) currentMenuUpdatedHandler:^(NSArray * _Nonnull currentMenuCells) { + SDLMenuReplaceOperation *menuReplaceOperation = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells compatibilityModeEnabled:(![self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) currentMenuUpdatedHandler:^(NSArray * _Nonnull currentMenuCells, NSError *error) { weakself.currentMenuCells = currentMenuCells; [weakself sdl_updateMenuReplaceOperationsWithNewCurrentMenu]; }]; - __weak typeof(menuReplaceOperation) weakOp = menuReplaceOperation; - menuReplaceOperation.completionBlock = ^{ - if (weakOp.error != nil) { - SDLLogE(@"Updating menu failed with error: %@", weakOp.error); - } - }; - // Cancel previous replace menu operations for (NSOperation *operation in self.transactionQueue.operations) { if ([operation isMemberOfClass:[SDLMenuReplaceOperation class]]) { diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.h b/SmartDeviceLink/private/SDLMenuReplaceOperation.h index e4057b7cd..708b65c42 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.h +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.h @@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN -typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCells); +typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCells, NSError *_Nullable error); @interface SDLMenuReplaceOperation : SDLAsynchronousOperation diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 793465769..c1048122d 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -38,7 +38,7 @@ @interface SDLMenuReplaceOperation () @property (weak, nonatomic) SDLFileManager *fileManager; @property (strong, nonatomic) NSArray *updatedMenu; @property (assign, nonatomic) BOOL compatibilityModeEnabled; -@property (assign, nonatomic) SDLCurrentMenuUpdatedBlock currentMenuUpdatedBlock; +@property (copy, nonatomic) SDLCurrentMenuUpdatedBlock currentMenuUpdatedBlock; @property (strong, nonatomic) NSMutableArray *mutableCurrentMenu; @property (copy, nonatomic, nullable) NSError *internalError; @@ -345,6 +345,7 @@ - (void)finishOperation { self.internalError = [NSError sdl_menuManager_replaceOperationCancelled]; } + self.currentMenuUpdatedBlock(self.currentMenu.copy, self.error); [super finishOperation]; } From 2ecf74d2d34d5e71e139eeef54633f1a9ef8fe7c Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 25 Feb 2021 15:05:24 -0500 Subject: [PATCH 048/112] Additional menu manager tests --- .../DevAPISpecs/SDLMenuManagerSpec.m | 148 ++++++++---------- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 13 +- 2 files changed, 77 insertions(+), 84 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index a7763f098..d7533c526 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -4,9 +4,10 @@ #import +#import "SDLGlobals.h" #import "SDLMenuManager.h" +#import "SDLMenuReplaceOperation.h" #import "TestConnectionManager.h" -#import "SDLGlobals.h" @interface SDLMenuCell() @@ -108,64 +109,30 @@ @interface SDLMenuManager() }); }); - // before the HMI is ready - context(@"before the HMI is ready", ^{ - // when in HMI NONE - context(@"when in HMI NONE", ^{ - beforeEach(^{ - testManager.currentHMILevel = SDLHMILevelNone; - testManager.menuCells = @[textOnlyCell]; - }); - - it(@"should not update", ^{ - expect(testManager.transactionQueue.isSuspended).to(beTrue()); - expect(testManager.transactionQueue.operationCount).to(equal(1)); - }); - - describe(@"when entering the foreground", ^{ - beforeEach(^{ - SDLOnHMIStatus *onHMIStatus = [[SDLOnHMIStatus alloc] init]; - onHMIStatus.hmiLevel = SDLHMILevelFull; - onHMIStatus.systemContext = SDLSystemContextMain; - - SDLRPCNotificationNotification *testSystemContextNotification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:onHMIStatus]; - [[NSNotificationCenter defaultCenter] postNotification:testSystemContextNotification]; - }); + // when in HMI NONE + context(@"when in HMI NONE", ^{ + beforeEach(^{ + SDLOnHMIStatus *noneStatus = [[SDLOnHMIStatus alloc] initWithHMILevel:SDLHMILevelNone systemContext:SDLSystemContextMain audioStreamingState:SDLAudioStreamingStateNotAudible videoStreamingState:nil windowID:nil]; + [[NSNotificationCenter defaultCenter] postNotification:[[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:noneStatus]]; + }); - it(@"should update", ^{ - expect(testManager.transactionQueue.isSuspended).to(beFalse()); - }); - }); + it(@"should not update", ^{ + expect(testManager.transactionQueue.isSuspended).to(beTrue()); }); - // when no HMI level has been received - context(@"when no HMI level has been received", ^{ + // when entering HMI FULL + describe(@"when entering HMI FULL", ^{ beforeEach(^{ - testManager.currentHMILevel = nil; - }); - - // should queue the menu configuration - it(@"should queue the menu configuration", ^{ - testManager.menuConfiguration = testMenuConfiguration; - expect(testManager.transactionQueue.operationCount).to(equal(1)); - expect(testManager.transactionQueue.isSuspended).to(beTrue()); - expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); - }); + SDLOnHMIStatus *onHMIStatus = [[SDLOnHMIStatus alloc] init]; + onHMIStatus.hmiLevel = SDLHMILevelFull; + onHMIStatus.systemContext = SDLSystemContextMain; - // should queue opening the menu - it(@"should queue opening the menu", ^{ - [testManager openMenu:nil]; - expect(testManager.transactionQueue.operationCount).to(equal(1)); - expect(testManager.transactionQueue.isSuspended).to(beTrue()); + SDLRPCNotificationNotification *testSystemContextNotification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:onHMIStatus]; + [[NSNotificationCenter defaultCenter] postNotification:testSystemContextNotification]; }); - // should queue updating the menu cells - it(@"should queue updating the menu cells", ^{ - NSArray *menuCells = @[textOnlyCell]; - testManager.menuCells = menuCells; - expect(testManager.transactionQueue.operationCount).to(equal(1)); - expect(testManager.transactionQueue.isSuspended).to(beTrue()); - expect(testManager.menuCells).to(equal(menuCells)); + it(@"should update", ^{ + expect(testManager.transactionQueue.isSuspended).to(beFalse()); }); }); }); @@ -198,6 +165,7 @@ @interface SDLMenuManager() }); }); + // if the new menu cells are identical to the old menu cells context(@"if the new menu cells are identical to the old menu cells", ^{ it(@"should only queue one transaction", ^{ testManager.menuCells = @[textOnlyCell]; @@ -208,7 +176,38 @@ @interface SDLMenuManager() }); }); - // TODO: Add tests for normal setting, including tests for ids, canceling old tasks (including on menu config and opening menu), updating current menu cells + // when a second menu cells update is queued before the first is done + context(@"when a second menu cells update is queued before the first is done", ^{ + it(@"should cancel the first operation", ^{ + testManager.menuCells = @[textOnlyCell]; + testManager.menuCells = @[submenuCell]; + + expect(testManager.menuCells).to(equal(@[submenuCell])); + expect(testManager.transactionQueue.operationCount).to(equal(2)); + expect(testManager.transactionQueue.operations[0].isCancelled).to(beTrue()); + }); + }); + + // if cells are formed properly + context(@"if cells are formed properly", ^{ + it(@"should properly prepare and queue the transaction", ^{ + testManager.menuCells = @[textOnlyCell]; + + // Assign proper cell id + expect(textOnlyCell.cellId).to(equal(1)); + expect(testManager.transactionQueue.operationCount).to(equal(1)); + expect(testManager.transactionQueue.operations[0]).to(beAnInstanceOf([SDLMenuReplaceOperation class])); + + // Assign proper current menu + SDLMenuReplaceOperation *testOp = testManager.transactionQueue.operations[0]; + expect(testOp.currentMenu).to(haveCount(0)); + + // Callback proper current menu + testOp.currentMenu = @[textOnlyCell]; + [testOp finishOperation]; + expect(testManager.currentMenuCells).to(haveCount(1)); + }); + }); }); // updating the menu configuration @@ -244,34 +243,16 @@ @interface SDLMenuManager() expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); expect(testManager.transactionQueue.operationCount).to(equal(1)); }); - }); - - // when no HMI level has been received - context(@"when no HMI level has been received", ^{ - beforeEach(^{ - testManager.currentHMILevel = nil; - }); - - it(@"should queue a menu configuration update", ^{ - testManager.menuConfiguration = testMenuConfiguration; - - expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); - expect(testManager.transactionQueue.operationCount).to(equal(1)); - }); - }); - // when in the menu - context(@"when in the menu", ^{ - beforeEach(^{ - [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"6.0.0"]; - testManager.currentHMILevel = SDLHMILevelFull; - testManager.currentSystemContext = SDLSystemContextMenu; - }); + // when queueing a second task after the first + context(@"when queueing a second task after the first", ^{ + it(@"should cancel the first task", ^{ + testManager.menuConfiguration = testMenuConfiguration; + testManager.menuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutList defaultSubmenuLayout:SDLMenuLayoutList]; - it(@"should queue a menu configuration update", ^{ - testManager.menuConfiguration = testMenuConfiguration; - expect(testManager.menuConfiguration).to(equal(testMenuConfiguration)); - expect(testManager.transactionQueue.operationCount).to(equal(1)); + expect(testManager.transactionQueue.operationCount).to(equal(2)); + expect(testManager.transactionQueue.operations[0].isCancelled).to(beTrue()); + }); }); }); }); @@ -307,6 +288,15 @@ @interface SDLMenuManager() expect(testManager.transactionQueue.operationCount).to(equal(2)); expect(canSendRPC).to(equal(YES)); }); + + it(@"should cancel the first task if a second is queued", ^{ + testManager.menuCells = @[submenuCell]; + [testManager openMenu:nil]; + [testManager openMenu:submenuCell]; + + expect(testManager.transactionQueue.operationCount).to(equal(3)); + expect(testManager.transactionQueue.operations[1].isCancelled).to(beTrue()); + }); }); // when open menu RPC can not be sent diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 0740d23af..702c1a3e0 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -37,7 +37,8 @@ __block SDLMenuCell *submenuCell = nil; __block SDLMenuCell *submenuImageCell = nil; - __block NSArray *newCurrentMenuCells = nil; + __block NSArray *receivedMenuCells = nil; + __block NSError *receivedError = nil; __block SDLCurrentMenuUpdatedBlock testCurrentMenuUpdatedBlock = nil; __block SDLMenuReplaceUtilities *mockReplaceUtilities = nil; @@ -65,9 +66,11 @@ testCurrentMenu = @[]; testNewMenu = nil; - newCurrentMenuCells = nil; - testCurrentMenuUpdatedBlock = ^(NSArray *currentMenuCells) { - newCurrentMenuCells = currentMenuCells; + receivedMenuCells = nil; + receivedError = nil; + testCurrentMenuUpdatedBlock = ^(NSArray *currentMenuCells, NSError *error) { + receivedMenuCells = currentMenuCells; + receivedError = error; }; mockReplaceUtilities = OCMClassMock([SDLMenuReplaceUtilities class]); @@ -198,7 +201,7 @@ testNewMenu = @[textOnlyCell, textOnlyCell2]; }); - fit(@"should send a delete and two adds", ^{ + it(@"should send a delete and two adds", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; [testOp start]; From 3e238f0ecebfb067c4dae10ff6b35364f4c6243a Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 25 Feb 2021 16:05:21 -0500 Subject: [PATCH 049/112] New tests and fixes --- SmartDeviceLink/private/SDLLifecycleManager.m | 3 +- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 69 ++++++++++++------- .../TestUtilities/TestConnectionManager.m | 8 +++ 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/SmartDeviceLink/private/SDLLifecycleManager.m b/SmartDeviceLink/private/SDLLifecycleManager.m index c81dc6dd0..966296a82 100644 --- a/SmartDeviceLink/private/SDLLifecycleManager.m +++ b/SmartDeviceLink/private/SDLLifecycleManager.m @@ -672,8 +672,7 @@ - (void)sendRequest:(SDLRPCRequest *)request withResponseHandler:(nullable SDLRe - (void)sendRequests:(NSArray *)requests progressHandler:(nullable SDLMultipleAsyncRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler { if (requests.count == 0) { - completionHandler(YES); - return; + return completionHandler(YES); } SDLAsynchronousRPCRequestOperation *op = [[SDLAsynchronousRPCRequestOperation alloc] initWithConnectionManager:self requests:requests progressHandler:progressHandler completionHandler:completionHandler]; diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 702c1a3e0..5eea34c80 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -37,6 +37,8 @@ __block SDLMenuCell *submenuCell = nil; __block SDLMenuCell *submenuImageCell = nil; + __block SDLAddCommandResponse *addCommandSuccessResponse = nil; + __block NSArray *receivedMenuCells = nil; __block NSError *receivedError = nil; __block SDLCurrentMenuUpdatedBlock testCurrentMenuUpdatedBlock = nil; @@ -55,6 +57,10 @@ submenuImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 4" icon:testArtwork2 submenuLayout:SDLMenuLayoutTiles subCells:@[textOnlyCell]]; textOnlyCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 5" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + addCommandSuccessResponse = [[SDLAddCommandResponse alloc] init]; + addCommandSuccessResponse.success = @YES; + addCommandSuccessResponse.resultCode = SDLResultSuccess; + testOp = nil; testConnectionManager = [[TestConnectionManager alloc] init]; testFileManager = OCMClassMock([SDLFileManager class]); @@ -89,6 +95,46 @@ }); }); + // when starting while cancelled + context(@"when starting while cancelled", ^{ + it(@"should finish without doing anything", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp cancel]; + [testOp start]; + + expect(testConnectionManager.receivedRequests).to(beEmpty()); + expect(testOp.isFinished).to(beTrue()); + }); + }); + + // when uploading a text-only cell + context(@"when uploading a text-only cell", ^{ + beforeEach(^{ + testNewMenu = @[textOnlyCell]; + OCMStub([testFileManager fileNeedsUpload:[OCMArg any]]).andReturn(NO); + }); + + fit(@"should properly send the RPCs and finish the operation", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + OCMReject([testFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]); + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + expect(deletes).to(beEmpty()); + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; + NSArray *add = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + expect(add).to(haveCount(1)); + + [testConnectionManager respondToLastRequestWithResponse:addCommandSuccessResponse]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + expect(testOp.isFinished).to(beTrue()); + }); + }); + // when uploading text and image cell context(@"when uploading text and image cell", ^{ beforeEach(^{ @@ -142,29 +188,6 @@ }); }); - // when uploading a text-only cell - context(@"when uploading a text-only cell", ^{ - beforeEach(^{ - testNewMenu = @[textOnlyCell]; - OCMStub([testFileManager fileNeedsUpload:[OCMArg any]]).andReturn(NO); - }); - - it(@"should properly update a text cell", ^{ - testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; - [testOp start]; - - OCMReject([testFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]); - - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]]; - NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - expect(deletes).to(beEmpty()); - - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; - NSArray *add = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - expect(add).toNot(beEmpty()); - }); - }); - // when uploading a cell with subcells context(@"when uploading a cell with subcells", ^{ beforeEach(^{ diff --git a/SmartDeviceLinkTests/TestUtilities/TestConnectionManager.m b/SmartDeviceLinkTests/TestUtilities/TestConnectionManager.m index 4774f8d09..c62f02dfd 100644 --- a/SmartDeviceLinkTests/TestUtilities/TestConnectionManager.m +++ b/SmartDeviceLinkTests/TestUtilities/TestConnectionManager.m @@ -54,6 +54,10 @@ - (void)sendConnectionManagerRPC:(__kindof SDLRPCMessage *)rpc { } - (void)sendRequests:(nonnull NSArray *)requests progressHandler:(nullable SDLMultipleAsyncRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler { + if (requests.count == 0) { + return completionHandler(YES); + } + [requests enumerateObjectsUsingBlock:^(SDLRPCRequest * _Nonnull request, NSUInteger idx, BOOL * _Nonnull stop) { [self sendConnectionRequest:request withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { if (progressHandler != nil) { @@ -66,6 +70,10 @@ - (void)sendRequests:(nonnull NSArray *)requests progressHandle } - (void)sendSequentialRequests:(nonnull NSArray *)requests progressHandler:(nullable SDLMultipleSequentialRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler { + if (requests.count == 0) { + return completionHandler(YES); + } + [requests enumerateObjectsUsingBlock:^(SDLRPCRequest * _Nonnull request, NSUInteger idx, BOOL * _Nonnull stop) { [self sendConnectionRequest:request withResponseHandler:nil]; progressHandler(request, nil, nil, (double)idx / (double)requests.count); From 5a765239dfbe98419039dd67f43f48923750253b Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 28 Jul 2021 15:48:30 -0400 Subject: [PATCH 050/112] Starting updates of menu refactor --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 40 +++++---- SmartDeviceLink/private/SDLMenuManager.m | 59 ++++++++----- .../SDLMenuReplaceOperation+MenuUniqueness.h | 19 ++++ .../SDLMenuReplaceOperation+MenuUniqueness.m | 86 +++++++++++++++++++ .../private/SDLMenuReplaceOperation.m | 33 ++++--- SmartDeviceLink/public/SDLMenuCell.m | 25 ++---- 6 files changed, 198 insertions(+), 64 deletions(-) create mode 100644 SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.h create mode 100644 SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 08d0fd282..a6ee76a91 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -282,6 +282,8 @@ 1FF7DAC01F75CF6C00B46C30 /* SDLHapticManagerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FF7DABF1F75CF6C00B46C30 /* SDLHapticManagerSpec.m */; }; 2BF2F85220ED068200A26EF2 /* SDLAudioStreamingIndicatorSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BF2F85120ED068200A26EF2 /* SDLAudioStreamingIndicatorSpec.m */; }; 4A1B036F24CF484E008C6B13 /* SDLDriverDistractionCapabilitySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A1B036E24CF484E008C6B13 /* SDLDriverDistractionCapabilitySpec.m */; }; + 4A1B7A9D26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A1B7A9B26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.h */; }; + 4A1B7A9E26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A1B7A9C26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.m */; }; 4A1FA09B25114833006B7851 /* SDLErrorConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A1FA09A25114833006B7851 /* SDLErrorConstants.m */; }; 4A39C6FA25E84C87005C8943 /* SDLKeyboardCapabilities.m in Sources */ = {isa = PBXBuildFile; fileRef = B3A9D9DF25D2571000CDFD21 /* SDLKeyboardCapabilities.m */; }; 4A40254124FFDA660080E159 /* SDLTextAndGraphicState.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A40253D24FFDA660080E159 /* SDLTextAndGraphicState.m */; }; @@ -513,9 +515,6 @@ 4A8BD3CD24F999BE000945E3 /* TestSubscribeButtonObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8BD3CC24F999BE000945E3 /* TestSubscribeButtonObserver.m */; }; 4A8BD3D024FE7CF1000945E3 /* SDLPermissionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8BD3CE24FE7CF1000945E3 /* SDLPermissionManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4A8BD3D124FE7CF1000945E3 /* SDLPermissionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8BD3CF24FE7CF1000945E3 /* SDLPermissionManager.m */; }; - 4AAB6A1225E57BEA0017A5A7 /* SDLSystemInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AAB6A1025E57BEA0017A5A7 /* SDLSystemInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4AAB6A1325E57BEA0017A5A7 /* SDLSystemInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAB6A1125E57BEA0017A5A7 /* SDLSystemInfo.m */; }; - 4AAB6A2325E69D010017A5A7 /* SDLSystemInfoSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAB6A2225E69D010017A5A7 /* SDLSystemInfoSpec.m */; }; 4A93893D25B8CBD40069F438 /* SDLMenuReplaceOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93893B25B8CBD40069F438 /* SDLMenuReplaceOperation.h */; }; 4A93893E25B8CBD40069F438 /* SDLMenuReplaceOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93893C25B8CBD40069F438 /* SDLMenuReplaceOperation.m */; }; 4A93895325B9DACA0069F438 /* SDLMenuShowOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93895125B9DACA0069F438 /* SDLMenuShowOperation.h */; }; @@ -526,6 +525,9 @@ 4A93896725BB361C0069F438 /* SDLMenuReplaceUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */; }; 4A96113B25D1A0C600D787DA /* SDLMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A96113925D1A0C600D787DA /* SDLMacros.h */; }; 4A96114825D1B5DA00D787DA /* SDLMacros.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A96114725D1B5DA00D787DA /* SDLMacros.m */; }; + 4AAB6A1225E57BEA0017A5A7 /* SDLSystemInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AAB6A1025E57BEA0017A5A7 /* SDLSystemInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4AAB6A1325E57BEA0017A5A7 /* SDLSystemInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAB6A1125E57BEA0017A5A7 /* SDLSystemInfo.m */; }; + 4AAB6A2325E69D010017A5A7 /* SDLSystemInfoSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAB6A2225E69D010017A5A7 /* SDLSystemInfoSpec.m */; }; 4AAC0DBA25C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AAC0DB825C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h */; }; 4AAC0DBB25C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAC0DB925C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m */; }; 4AAC0DE025C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAC0DDF25C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m */; }; @@ -2157,6 +2159,8 @@ 1FF7DABF1F75CF6C00B46C30 /* SDLHapticManagerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLHapticManagerSpec.m; path = ProxySpecs/SDLHapticManagerSpec.m; sourceTree = ""; }; 2BF2F85120ED068200A26EF2 /* SDLAudioStreamingIndicatorSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLAudioStreamingIndicatorSpec.m; sourceTree = ""; }; 4A1B036E24CF484E008C6B13 /* SDLDriverDistractionCapabilitySpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLDriverDistractionCapabilitySpec.m; sourceTree = ""; }; + 4A1B7A9B26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SDLMenuReplaceOperation+MenuUniqueness.h"; path = "private/SDLMenuReplaceOperation+MenuUniqueness.h"; sourceTree = ""; }; + 4A1B7A9C26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "SDLMenuReplaceOperation+MenuUniqueness.m"; path = "private/SDLMenuReplaceOperation+MenuUniqueness.m"; sourceTree = ""; }; 4A1FA09A25114833006B7851 /* SDLErrorConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLErrorConstants.m; path = public/SDLErrorConstants.m; sourceTree = ""; }; 4A316E0A253F316700D4DDC7 /* test.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; name = test.yml; path = .github/workflows/test.yml; sourceTree = SOURCE_ROOT; }; 4A40253D24FFDA660080E159 /* SDLTextAndGraphicState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLTextAndGraphicState.m; path = private/SDLTextAndGraphicState.m; sourceTree = ""; }; @@ -2404,14 +2408,14 @@ 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceUtilities.m; path = private/SDLMenuReplaceUtilities.m; sourceTree = ""; }; 4A96113925D1A0C600D787DA /* SDLMacros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMacros.h; path = private/SDLMacros.h; sourceTree = ""; }; 4A96114725D1B5DA00D787DA /* SDLMacros.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMacros.m; path = private/SDLMacros.m; sourceTree = ""; }; + 4AAB6A1025E57BEA0017A5A7 /* SDLSystemInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLSystemInfo.h; path = public/SDLSystemInfo.h; sourceTree = ""; }; + 4AAB6A1125E57BEA0017A5A7 /* SDLSystemInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLSystemInfo.m; path = public/SDLSystemInfo.m; sourceTree = ""; }; + 4AAB6A2225E69D010017A5A7 /* SDLSystemInfoSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLSystemInfoSpec.m; path = DevAPISpecs/SDLSystemInfoSpec.m; sourceTree = ""; }; 4AAC0DB825C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuManagerPrivateConstants.h; path = private/SDLMenuManagerPrivateConstants.h; sourceTree = ""; }; 4AAC0DB925C1FEBA00746D33 /* SDLMenuManagerPrivateConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuManagerPrivateConstants.m; path = private/SDLMenuManagerPrivateConstants.m; sourceTree = ""; }; 4AAC0DDF25C468EC00746D33 /* SDLMenuReplaceUtilitiesSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceUtilitiesSpec.m; path = DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m; sourceTree = ""; }; 4AAC0DE625C493EE00746D33 /* SDLMenuReplaceUtilitiesSpecHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLMenuReplaceUtilitiesSpecHelpers.h; path = DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h; sourceTree = ""; }; 4AAC0DE725C493EE00746D33 /* SDLMenuReplaceUtilitiesSpecHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLMenuReplaceUtilitiesSpecHelpers.m; path = DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m; sourceTree = ""; }; - 4AAB6A1025E57BEA0017A5A7 /* SDLSystemInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLSystemInfo.h; path = public/SDLSystemInfo.h; sourceTree = ""; }; - 4AAB6A1125E57BEA0017A5A7 /* SDLSystemInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLSystemInfo.m; path = public/SDLSystemInfo.m; sourceTree = ""; }; - 4AAB6A2225E69D010017A5A7 /* SDLSystemInfoSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLSystemInfoSpec.m; path = DevAPISpecs/SDLSystemInfoSpec.m; sourceTree = ""; }; 4ABB24B224F592620061BF55 /* NSMutableArray+Safe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSMutableArray+Safe.h"; path = "private/NSMutableArray+Safe.h"; sourceTree = ""; }; 4ABB24B324F592620061BF55 /* NSMutableArray+Safe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSMutableArray+Safe.m"; path = "private/NSMutableArray+Safe.m"; sourceTree = ""; }; 4ABB24B424F592620061BF55 /* NSBundle+SDLBundle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSBundle+SDLBundle.m"; path = "private/NSBundle+SDLBundle.m"; sourceTree = ""; }; @@ -4459,6 +4463,8 @@ 4ABB259F24F7E6CE0061BF55 /* SDLDynamicMenuUpdateAlgorithm.m */, 4ABB259E24F7E6CE0061BF55 /* SDLDynamicMenuUpdateRunScore.h */, 4ABB25A024F7E6CE0061BF55 /* SDLDynamicMenuUpdateRunScore.m */, + 4A1B7A9B26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.h */, + 4A1B7A9C26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.m */, 4A93896425BB361C0069F438 /* SDLMenuReplaceUtilities.h */, 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */, ); @@ -4484,6 +4490,15 @@ name = "Status Manager"; sourceTree = ""; }; + 4AAB6A0F25E57B2D0017A5A7 /* Utilities */ = { + isa = PBXGroup; + children = ( + 4AAB6A1025E57BEA0017A5A7 /* SDLSystemInfo.h */, + 4AAB6A1125E57BEA0017A5A7 /* SDLSystemInfo.m */, + ); + name = Utilities; + sourceTree = ""; + }; 4AAC0DB725C1FE9700746D33 /* Constants */ = { isa = PBXGroup; children = ( @@ -4511,15 +4526,6 @@ 4ABC1CB025DC520300545AC6 /* SDLMenuShowOperationSpec.m */, ); name = Operations; - sourceTree = ""; - }; - 4AAB6A0F25E57B2D0017A5A7 /* Utilities */ = { - isa = PBXGroup; - children = ( - 4AAB6A1025E57BEA0017A5A7 /* SDLSystemInfo.h */, - 4AAB6A1125E57BEA0017A5A7 /* SDLSystemInfo.m */, - ); - name = Utilities; sourceTree = ""; }; 4AD1F16A2559952D00637FE1 /* Voice Command */ = { @@ -4710,7 +4716,6 @@ 5D339CE5207C0651000CC364 /* Menu */ = { isa = PBXGroup; children = ( - 4A32B3E425559D93001FFA26 /* Voice Commands */, 4AAC0DB725C1FE9700746D33 /* Constants */, 5D339CEC207C08AB000CC364 /* Cells */, 5D76751022D907F500E8D71A /* Configuration */, @@ -7324,6 +7329,7 @@ 4ABB2AB524F847F40061BF55 /* SDLShowAppMenuResponse.h in Headers */, 4A8BD3C724F998A8000945E3 /* SDLWindowState.h in Headers */, 4ABB286424F828E00061BF55 /* SDLVehicleDataNotificationStatus.h in Headers */, + 4A1B7A9D26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.h in Headers */, 4ABB287C24F8294A0061BF55 /* SDLVentilationMode.h in Headers */, 4ABB292F24F842A00061BF55 /* SDLDeleteFile.h in Headers */, 4ABB294924F843440061BF55 /* SDLEncodedSyncPData.h in Headers */, @@ -8196,6 +8202,7 @@ 4ABB2AB724F847F40061BF55 /* SDLSetGlobalPropertiesResponse.m in Sources */, C9DFFE79257ACE0000F7D57A /* SDLSeekStreamingIndicator.m in Sources */, 4ABB2B0B24F84D950061BF55 /* SDLAppServiceData.m in Sources */, + 4A1B7A9E26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.m in Sources */, 88D79EEE255D8D5B005FACB1 /* SDLPresentAlertOperation.m in Sources */, 4ABB282C24F824E70061BF55 /* SDLTBTState.m in Sources */, 4ABB27DF24F800CA0061BF55 /* SDLPowerModeQualificationStatus.m in Sources */, @@ -9227,7 +9234,6 @@ B3A3CEA025222A2900A7121D /* SDLOnAppCapabilityUpdatedSpec.m in Sources */, 162E831E1A9BDE8B00906325 /* SDLOnTBTClientStateSpec.m in Sources */, 162E83351A9BDE8B00906325 /* SDLReadDIDSpec.m in Sources */, - 5DF40B28208FDA9700DD6FDA /* SDLVoiceCommandManagerSpec.m in Sources */, 4AD1F1742559957100637FE1 /* SDLVoiceCommandUpdateOperationSpec.m in Sources */, 88B3BFA020DA8FD000943565 /* SDLFuelTypeSpec.m in Sources */, 162E836F1A9BDE8B00906325 /* SDLUnsubscribeVehicleDataResponseSpec.m in Sources */, diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 13ffc09cc..b4a0ca4f9 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -191,27 +191,8 @@ - (void)setMenuCells:(NSArray *)menuCells { if ([self.menuCells isEqualToArray:menuCells]) { SDLLogD(@"The set menu cells are identical to previously set menu cells. Skipping..."); return; - } - - NSMutableSet *titleCheckSet = [NSMutableSet set]; - NSMutableSet *allMenuVoiceCommands = [NSMutableSet set]; - NSUInteger voiceCommandCount = 0; - for (SDLMenuCell *cell in menuCells) { - [titleCheckSet addObject:cell.title]; - if (cell.voiceCommands == nil) { continue; } - [allMenuVoiceCommands addObjectsFromArray:cell.voiceCommands]; - voiceCommandCount += cell.voiceCommands.count; - } - - // Check for duplicate titles - if (titleCheckSet.count != menuCells.count) { - SDLLogE(@"Not all cell titles are unique. The menu will not be set."); - return; - } - - // Check for duplicate voice recognition commands - if (allMenuVoiceCommands.count != voiceCommandCount) { - SDLLogE(@"Attempted to create a menu with duplicate voice commands. Voice commands must be unique. The menu will not be set."); + } else if (![self sdl_menuCellsAreUnique:menuCells allVoiceCommands:[NSMutableArray array]]) { + SDLLogE(@"Not all set menu cells are unique, but that is required"); return; } @@ -318,6 +299,42 @@ - (BOOL)sdl_isDynamicMenuUpdateActive:(SDLDynamicMenuUpdatesMode)dynamicMenuUpda } } +/// Check for cell lists with completely duplicate information, or any duplicate voiceCommands +/// +/// @param cells The cells you will be adding +/// @return Boolean that indicates whether menuCells are unique or not +- (BOOL)sdl_menuCellsAreUnique:(NSArray *)cells allVoiceCommands:(NSMutableArray *)allVoiceCommands { + ///Check all voice commands for identical items and check each list of cells for identical cells + NSMutableSet *identicalCellsCheckSet = [NSMutableSet set]; + for (SDLMenuCell *cell in cells) { + [identicalCellsCheckSet addObject:cell]; + + // Recursively check the subcell lists to see if they are all unique as well. If anything is not, this will chain back up the list to return false. + if (cell.subCells.count > 0) { + BOOL subcellsAreUnique = [self sdl_menuCellsAreUnique:cell.subCells allVoiceCommands:allVoiceCommands]; + if (!subcellsAreUnique) { return NO; } + } + + // Voice commands have to be identical across all lists + if (cell.voiceCommands == nil) { continue; } + [allVoiceCommands addObjectsFromArray:cell.voiceCommands]; + } + + // Check for duplicate cells + if (identicalCellsCheckSet.count != cells.count) { + SDLLogE(@"Not all cells are unique. Cells in each list (such as main menu or subcell list) must have some differentiating property other than the subcells within a cell. The menu will not be set."); + return NO; + } + + // All the VR commands must be unique + if (allVoiceCommands.count != [NSSet setWithArray:allVoiceCommands].count) { + SDLLogE(@"Attempted to create a menu with duplicate voice commands, but voice commands must be unique across all menu items including main menu and subcell lists. The menu will not be set."); + return NO; + } + + return YES; +} + #pragma mark IDs /// Assign cell ids on an array of menu cells given a parent id (or no parent id) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.h b/SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.h new file mode 100644 index 000000000..15eee20a8 --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.h @@ -0,0 +1,19 @@ +// +// SDLMenuReplaceOperation+MenuUniqueness.h +// SmartDeviceLink +// +// Created by Joel Fischer on 7/28/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import "SDLMenuReplaceOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLMenuReplaceOperation (MenuUniqueness) + ++ (void)sdl_addUniqueNamesToCells:(NSArray *)menuCells basedOnWindowCapability:(SDLWindowCapability *)windowCapability; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.m b/SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.m new file mode 100644 index 000000000..a2fae9d3c --- /dev/null +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.m @@ -0,0 +1,86 @@ +// +// SDLMenuReplaceOperation+MenuUniqueness.m +// SmartDeviceLink +// +// Created by Joel Fischer on 7/28/21. +// Copyright © 2021 smartdevicelink. All rights reserved. +// + +#import "SDLMenuReplaceOperation+MenuUniqueness.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation SDLMenuReplaceOperation (MenuUniqueness) + ++ (void)sdl_addUniqueNamesToCells:(NSArray *)menuCells basedOnWindowCapability:(SDLWindowCapability *)windowCapability { + SDLVersion *supportsMenuUniqueness = [[SDLVersion alloc] initWithMajor:7 minor:1 patch:0]; + if ([[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:supportsMenuUniqueness]) { + [self sdl_removeUnusedProperties:menuCells basedOnWindowCapability:windowCapability]; + } + + [self sdl_addUniqueNamesToCells:menuCells supportsMenuUniqueness:supportsMenuUniqueness]; +} + ++ (void)sdl_removeUnusedProperties:(NSArray *)menuCells basedOnWindowCapability:(SDLWindowCapability *)windowCapability { + NSArray *removePropertiesCopy = [[NSArray alloc] initWithArray:menuCells copyItems:YES]; + for (SDLMenuCell *cell in removePropertiesCopy) { + // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI + cell.voiceCommands = nil; + + // Don't check SDLImageFieldNameSubMenuIcon because it was added in 7.0 when the feature was added in 5.0. Just assume that if CommandIcon is not available, the submenu icon is not either. + if (![windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]) { + cell.icon = nil; + } + + if (cell.subCells != nil) { + if (![windowCapability hasTextFieldOfName:SDLTextFieldNameMenuSubMenuSecondaryText]) { + cell.secondaryText = nil; + } + if (![windowCapability hasTextFieldOfName:SDLTextFieldNameMenuSubMenuTertiaryText]) { + cell.tertiaryText = nil; + } + if (![windowCapability hasImageFieldOfName:SDLImageFieldNameMenuSubMenuSecondaryImage]) { + cell.secondaryArtwork = nil; + } + [self sdl_removeUnusedProperties:cell.subCells basedOnWindowCapability:windowCapability]; + } else { + if (![windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandSecondaryText]) { + cell.secondaryText = nil; + } + if (![windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandTertiaryText]) { + cell.tertiaryText = nil; + } + if (![windowCapability hasImageFieldOfName:SDLImageFieldNameMenuCommandSecondaryImage]) { + cell.secondaryArtwork = nil; + } + } + } +} + ++ (void)sdl_addUniqueNamesToCells:(NSArray *)menuCells supportsMenuUniqueness:(BOOL)supportsMenuUniqueness { + // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary + NSMutableDictionary *dictCounter = [[NSMutableDictionary alloc] init]; + for (NSUInteger i = 0; i < menuCells.count; i++) { + SDLMenuCell *cell = menuCells[i]; + NSNumber *counter = dictCounter[cell]; + if (counter != nil) { + counter = @(counter.intValue + 1); + dictCounter[cell] = counter; + } else { + dictCounter[cell] = @1; + } + + counter = dictCounter[cell]; + if (counter.intValue > 1) { + menuCells[i].uniqueTitle = [NSString stringWithFormat: @"%@ (%d)", menuCells[i].title, counter.intValue]; + } + + if (cell.subCells.count > 0) { + [self sdl_addUniqueNamesToCells:menuCells supportsMenuUniqueness:supportsMenuUniqueness]; + } + } +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index c1048122d..9ee35c13c 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -14,21 +14,32 @@ #import "SDLDynamicMenuUpdateRunScore.h" #import "SDLError.h" #import "SDLFileManager.h" +#import "SDLGlobals.h" #import "SDLLogMacros.h" #import "SDLMenuCell.h" #import "SDLMenuConfiguration.h" +#import "SDLMenuReplaceOperation+MenuUniqueness.h" #import "SDLTextFieldName.h" +#import "SDLVersion.h" #import "SDLWindowCapability.h" #import "SDLWindowCapability+ScreenManagerExtensions.h" NS_ASSUME_NONNULL_BEGIN -typedef void(^SDLMenuUpdateCompletionHandler)(NSError *__nullable error); - @interface SDLMenuCell() @property (assign, nonatomic) UInt32 parentCellId; @property (assign, nonatomic) UInt32 cellId; +@property (strong, nonatomic, readwrite) NSString *uniqueTitle; + +@property (copy, nonatomic, readwrite) NSString *title; +@property (strong, nonatomic, readwrite, nullable) SDLArtwork *icon; +@property (copy, nonatomic, readwrite, nullable) NSArray *voiceCommands; +@property (copy, nonatomic, readwrite, nullable) NSString *secondaryText; +@property (copy, nonatomic, readwrite, nullable) NSString *tertiaryText; +@property (strong, nonatomic, readwrite, nullable) SDLArtwork *secondaryArtwork; +@property (copy, nonatomic, readwrite, nullable) NSArray *subCells; +@property (copy, nonatomic, readwrite, nullable) SDLMenuCellSelectionHandler handler; @end @@ -56,7 +67,7 @@ - (instancetype)initWithConnectionManager:(id)connecti _windowCapability = windowCapability; _menuConfiguration = menuConfiguration; _mutableCurrentMenu = [currentMenu mutableCopy]; - _updatedMenu = updatedMenu; + _updatedMenu = [updatedMenu copy]; _compatibilityModeEnabled = compatibilityModeEnabled; _currentMenuUpdatedBlock = currentMenuUpdatedBlock; @@ -67,6 +78,8 @@ - (void)start { [super start]; if (self.isCancelled) { return; } + [self.class sdl_addUniqueNamesToCells:self.updatedMenu basedOnWindowCapability:self.windowCapability]; + SDLDynamicMenuUpdateRunScore *runScore = nil; if (self.compatibilityModeEnabled) { SDLLogV(@"Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); @@ -128,7 +141,7 @@ - (void)start { /// @param deleteCells The cells that need to be deleted /// @param addCells The cells that need to be added /// @param handler A handler called when complete -- (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(SDLMenuUpdateCompletionHandler)handler { +- (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(void(^)(NSError *_Nullable error))handler { __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { if (weakself.isCancelled) { return handler(error); } @@ -175,12 +188,12 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells } } -#pragma mark - Adding / Deleting cell RPCs +#pragma mark - Adding / Deleting Cell RPCs /// Send Delete RPCs for given menu cells /// @param deleteMenuCells The menu cells to be deleted /// @param completionHandler A handler called when the RPCs are finished with an error if any failed -- (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuCells withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { +- (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuCells withCompletionHandler:(void(^)(NSError *_Nullable error))completionHandler { if (deleteMenuCells.count == 0) { return completionHandler(nil); } __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; @@ -208,7 +221,7 @@ - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuC /// @param newMenuCells The new menu cells we want displayed /// @param oldMenu The old menu cells we no longer want displayed /// @param completionHandler A handler called when the RPCs are finished with an error if any failed -- (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSArray *)oldMenu withCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler { +- (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSArray *)oldMenu withCompletionHandler:(void(^)(NSError *_Nullable error))completionHandler { if (self.updatedMenu.count == 0 || newMenuCells.count == 0) { SDLLogV(@"There are no cells to update."); return completionHandler(nil); @@ -223,7 +236,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA if (error != nil) { errors[request] = error; } else { - // Find the id of the successful request and add it from the current menu list whereever it needs to be + // Find the id of the successful request and add it from the current menu list wherever it needs to be UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; [SDLMenuReplaceUtilities addMenuRequestWithCommandId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; @@ -238,7 +251,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA if (error != nil) { errors[request] = error; } else { - // Find the id of the successful request and add it from the current menu list whereever it needs to be + // Find the id of the successful request and add it from the current menu list wherever it needs to be UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; [SDLMenuReplaceUtilities addMenuRequestWithCommandId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; @@ -255,7 +268,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA }]; } -#pragma mark Dynamic Menu Helpers +#pragma mark - Dynamic Menu Helpers - (NSArray *)sdl_filterDeleteMenuItemsWithOldMenuItems:(NSArray *)oldMenuCells basedOnStatusList:(NSArray *)oldStatusList { NSMutableArray *deleteCells = [[NSMutableArray alloc] init]; diff --git a/SmartDeviceLink/public/SDLMenuCell.m b/SmartDeviceLink/public/SDLMenuCell.m index b189cc411..05ab2a786 100644 --- a/SmartDeviceLink/public/SDLMenuCell.m +++ b/SmartDeviceLink/public/SDLMenuCell.m @@ -92,16 +92,6 @@ - (NSString *)debugDescription { #pragma mark - Object Equality -- (id)copyWithZone:(nullable NSZone *)zone { - SDLMenuCell *newCell = [[SDLMenuCell allocWithZone:zone] initWithTitle:_title secondaryText:_secondaryText tertiaryText:_tertiaryText icon:_icon secondaryArtwork:_secondaryArtwork voiceCommands:_voiceCommands handler:_handler]; - - if (_subCells.count > 0) { - newCell.subCells = [[NSArray alloc] initWithArray:_subCells copyItems:YES]; - } - - return newCell; -} - - (NSUInteger)hash { return NSUIntRotateCell(self.title.hash, NSUIntBitCell / 2) ^ NSUIntRotateCell(self.icon.name.hash, NSUIntBitCell / 3) @@ -129,13 +119,16 @@ - (BOOL)isEqualToCell:(SDLMenuCell *)cell { #pragma mark - Copying - (id)copyWithZone:(nullable NSZone *)zone { - SDLMenuCell *copy = [[SDLMenuCell allocWithZone:zone] initWithTitle:_title icon:[_icon copy] submenuLayout:_submenuLayout subCells:[_subCells copy]]; - copy->_voiceCommands = [_voiceCommands copy]; - copy->_cellId = _cellId; - copy->_parentCellId = _parentCellId; - copy->_handler = [_handler copy]; + SDLMenuCell *newCell = [[SDLMenuCell allocWithZone:zone] initWithTitle:_title secondaryText:_secondaryText tertiaryText:_tertiaryText icon:_icon secondaryArtwork:_secondaryArtwork voiceCommands:_voiceCommands handler:_handler]; + newCell->_cellId = _cellId; + newCell->_parentCellId = _parentCellId; + newCell->_uniqueTitle = _uniqueTitle; + + if (_subCells.count > 0) { + newCell.subCells = [[NSArray alloc] initWithArray:_subCells copyItems:YES]; + } - return copy; + return newCell; } @end From d50d1a9d93b2571a522edc3a3a5234c57bf212fd Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 29 Jul 2021 09:43:10 -0400 Subject: [PATCH 051/112] Fix menu show completion block --- SmartDeviceLink/private/SDLMenuManager.m | 14 ++++++-------- SmartDeviceLink/private/SDLMenuShowOperation.h | 3 ++- SmartDeviceLink/private/SDLMenuShowOperation.m | 6 +++++- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index b4a0ca4f9..f0621dfd3 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -196,8 +196,8 @@ - (void)setMenuCells:(NSArray *)menuCells { return; } - [self sdl_updateIdsOnMenuCells:menuCells parentId:ParentIdNotFound]; - _menuCells = menuCells; + _menuCells = [[NSArray alloc] initWithArray:menuCells copyItems:YES]; + [self sdl_updateIdsOnMenuCells:self.menuCells parentId:ParentIdNotFound]; __weak typeof(self) weakself = self; SDLMenuReplaceOperation *menuReplaceOperation = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells compatibilityModeEnabled:(![self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) currentMenuUpdatedHandler:^(NSArray * _Nonnull currentMenuCells, NSError *error) { @@ -230,13 +230,11 @@ - (BOOL)openMenu:(nullable SDLMenuCell *)cell { } // Create the operation - SDLMenuShowOperation *showMenuOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:self.connectionManager toMenuCell:cell]; - __weak typeof(showMenuOp) weakMenuOp = showMenuOp; - showMenuOp.completionBlock = ^{ - if (weakMenuOp.error != nil) { - SDLLogE(@"Opening menu with error: %@, info: %@. Failed subcell (if nil, attempted to open to main menu): %@", weakMenuOp.error, weakMenuOp.error.userInfo, cell); + SDLMenuShowOperation *showMenuOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:self.connectionManager toMenuCell:cell completionHandler:^(NSError * _Nullable error) { + if (error != nil) { + SDLLogE(@"Opening menu with error: %@, info: %@. Failed subcell (if nil, attempted to open to main menu): %@", error, error.userInfo, cell); } - }; + }]; // Cancel previous open menu operations for (NSOperation *operation in self.transactionQueue.operations) { diff --git a/SmartDeviceLink/private/SDLMenuShowOperation.h b/SmartDeviceLink/private/SDLMenuShowOperation.h index 52496a899..b79e3d163 100644 --- a/SmartDeviceLink/private/SDLMenuShowOperation.h +++ b/SmartDeviceLink/private/SDLMenuShowOperation.h @@ -16,8 +16,9 @@ NS_ASSUME_NONNULL_BEGIN @interface SDLMenuShowOperation : SDLAsynchronousOperation +typedef void(^SDLMenuShowCompletionBlock)(NSError *_Nullable error); -- (instancetype)initWithConnectionManager:(id)connectionManager toMenuCell:(nullable SDLMenuCell *)menuCell; +- (instancetype)initWithConnectionManager:(id)connectionManager toMenuCell:(nullable SDLMenuCell *)menuCell completionHandler:(SDLMenuShowCompletionBlock)completionHandler; @end diff --git a/SmartDeviceLink/private/SDLMenuShowOperation.m b/SmartDeviceLink/private/SDLMenuShowOperation.m index cc6672b47..92fa5a971 100644 --- a/SmartDeviceLink/private/SDLMenuShowOperation.m +++ b/SmartDeviceLink/private/SDLMenuShowOperation.m @@ -30,6 +30,7 @@ @interface SDLMenuShowOperation () @property (weak, nonatomic) id connectionManager; @property (strong, nonatomic, nullable) SDLMenuCell *submenuCell; +@property (copy, nonatomic) SDLMenuShowCompletionBlock showCompletionHandler; @property (copy, nonatomic, nullable) NSError *internalError; @@ -37,12 +38,13 @@ @interface SDLMenuShowOperation () @implementation SDLMenuShowOperation -- (instancetype)initWithConnectionManager:(id)connectionManager toMenuCell:(nullable SDLMenuCell *)menuCell { +- (instancetype)initWithConnectionManager:(id)connectionManager toMenuCell:(nullable SDLMenuCell *)menuCell completionHandler:(nonnull SDLMenuShowCompletionBlock)completionHandler { self = [super init]; if (!self) { return nil; } _connectionManager = connectionManager; _submenuCell = menuCell; + _showCompletionHandler = completionHandler; return self; } @@ -81,6 +83,8 @@ - (void)finishOperation { self.internalError = [NSError sdl_menuManager_openMenuOperationCancelled]; } + self.showCompletionHandler(self.internalError); + [super finishOperation]; } From 6716b98e1ba8215dce54beaafd923b485e5fbe5c Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 29 Jul 2021 15:53:26 -0400 Subject: [PATCH 052/112] Continued updating to menu manager * Fixes and streamlining * Update to support cell secondary / tertiary properties --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 12 --- .../private/SDLDynamicMenuUpdateAlgorithm.h | 4 +- .../private/SDLDynamicMenuUpdateAlgorithm.m | 20 +++- .../SDLMenuReplaceOperation+MenuUniqueness.h | 19 ---- .../SDLMenuReplaceOperation+MenuUniqueness.m | 86 ----------------- .../private/SDLMenuReplaceOperation.h | 2 +- .../private/SDLMenuReplaceOperation.m | 96 ++++++++++++++----- .../private/SDLMenuReplaceUtilities.m | 20 +++- 8 files changed, 106 insertions(+), 153 deletions(-) delete mode 100644 SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.h delete mode 100644 SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index a6ee76a91..b5aa60742 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -282,8 +282,6 @@ 1FF7DAC01F75CF6C00B46C30 /* SDLHapticManagerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FF7DABF1F75CF6C00B46C30 /* SDLHapticManagerSpec.m */; }; 2BF2F85220ED068200A26EF2 /* SDLAudioStreamingIndicatorSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BF2F85120ED068200A26EF2 /* SDLAudioStreamingIndicatorSpec.m */; }; 4A1B036F24CF484E008C6B13 /* SDLDriverDistractionCapabilitySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A1B036E24CF484E008C6B13 /* SDLDriverDistractionCapabilitySpec.m */; }; - 4A1B7A9D26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A1B7A9B26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.h */; }; - 4A1B7A9E26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A1B7A9C26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.m */; }; 4A1FA09B25114833006B7851 /* SDLErrorConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A1FA09A25114833006B7851 /* SDLErrorConstants.m */; }; 4A39C6FA25E84C87005C8943 /* SDLKeyboardCapabilities.m in Sources */ = {isa = PBXBuildFile; fileRef = B3A9D9DF25D2571000CDFD21 /* SDLKeyboardCapabilities.m */; }; 4A40254124FFDA660080E159 /* SDLTextAndGraphicState.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A40253D24FFDA660080E159 /* SDLTextAndGraphicState.m */; }; @@ -524,7 +522,6 @@ 4A93896625BB361C0069F438 /* SDLMenuReplaceUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A93896425BB361C0069F438 /* SDLMenuReplaceUtilities.h */; }; 4A93896725BB361C0069F438 /* SDLMenuReplaceUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */; }; 4A96113B25D1A0C600D787DA /* SDLMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A96113925D1A0C600D787DA /* SDLMacros.h */; }; - 4A96114825D1B5DA00D787DA /* SDLMacros.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A96114725D1B5DA00D787DA /* SDLMacros.m */; }; 4AAB6A1225E57BEA0017A5A7 /* SDLSystemInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AAB6A1025E57BEA0017A5A7 /* SDLSystemInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4AAB6A1325E57BEA0017A5A7 /* SDLSystemInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAB6A1125E57BEA0017A5A7 /* SDLSystemInfo.m */; }; 4AAB6A2325E69D010017A5A7 /* SDLSystemInfoSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAB6A2225E69D010017A5A7 /* SDLSystemInfoSpec.m */; }; @@ -1757,7 +1754,6 @@ B3F7918324E062C200DB5CAF /* SDLGetVehicleDataSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 162E824C1A9BDE8A00906325 /* SDLGetVehicleDataSpec.m */; }; C9707D1825DEE786009D00F2 /* NSArray+Extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = C9707D1625DEE786009D00F2 /* NSArray+Extensions.h */; }; C9707D1925DEE786009D00F2 /* NSArray+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = C9707D1725DEE786009D00F2 /* NSArray+Extensions.m */; }; - C9707D3025E0444D009D00F2 /* SDLMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = C9707D2E25E0444D009D00F2 /* SDLMacros.h */; }; C9707D3125E0444D009D00F2 /* SDLMacros.m in Sources */ = {isa = PBXBuildFile; fileRef = C9707D2F25E0444D009D00F2 /* SDLMacros.m */; }; C971E3EE2649BD8700FC24D6 /* SDLOnSystemRequestSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 162E82371A9BDE8A00906325 /* SDLOnSystemRequestSpec.m */; }; C971E3EF2649C52000FC24D6 /* SDLResponseDispatcherSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DBAE0AC1D368D1A00CE00BF /* SDLResponseDispatcherSpec.m */; }; @@ -2159,8 +2155,6 @@ 1FF7DABF1F75CF6C00B46C30 /* SDLHapticManagerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLHapticManagerSpec.m; path = ProxySpecs/SDLHapticManagerSpec.m; sourceTree = ""; }; 2BF2F85120ED068200A26EF2 /* SDLAudioStreamingIndicatorSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLAudioStreamingIndicatorSpec.m; sourceTree = ""; }; 4A1B036E24CF484E008C6B13 /* SDLDriverDistractionCapabilitySpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLDriverDistractionCapabilitySpec.m; sourceTree = ""; }; - 4A1B7A9B26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SDLMenuReplaceOperation+MenuUniqueness.h"; path = "private/SDLMenuReplaceOperation+MenuUniqueness.h"; sourceTree = ""; }; - 4A1B7A9C26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "SDLMenuReplaceOperation+MenuUniqueness.m"; path = "private/SDLMenuReplaceOperation+MenuUniqueness.m"; sourceTree = ""; }; 4A1FA09A25114833006B7851 /* SDLErrorConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLErrorConstants.m; path = public/SDLErrorConstants.m; sourceTree = ""; }; 4A316E0A253F316700D4DDC7 /* test.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; name = test.yml; path = .github/workflows/test.yml; sourceTree = SOURCE_ROOT; }; 4A40253D24FFDA660080E159 /* SDLTextAndGraphicState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLTextAndGraphicState.m; path = private/SDLTextAndGraphicState.m; sourceTree = ""; }; @@ -4463,8 +4457,6 @@ 4ABB259F24F7E6CE0061BF55 /* SDLDynamicMenuUpdateAlgorithm.m */, 4ABB259E24F7E6CE0061BF55 /* SDLDynamicMenuUpdateRunScore.h */, 4ABB25A024F7E6CE0061BF55 /* SDLDynamicMenuUpdateRunScore.m */, - 4A1B7A9B26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.h */, - 4A1B7A9C26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.m */, 4A93896425BB361C0069F438 /* SDLMenuReplaceUtilities.h */, 4A93896525BB361C0069F438 /* SDLMenuReplaceUtilities.m */, ); @@ -7329,7 +7321,6 @@ 4ABB2AB524F847F40061BF55 /* SDLShowAppMenuResponse.h in Headers */, 4A8BD3C724F998A8000945E3 /* SDLWindowState.h in Headers */, 4ABB286424F828E00061BF55 /* SDLVehicleDataNotificationStatus.h in Headers */, - 4A1B7A9D26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.h in Headers */, 4ABB287C24F8294A0061BF55 /* SDLVentilationMode.h in Headers */, 4ABB292F24F842A00061BF55 /* SDLDeleteFile.h in Headers */, 4ABB294924F843440061BF55 /* SDLEncodedSyncPData.h in Headers */, @@ -7630,7 +7621,6 @@ 4ABB25E024F7E7980061BF55 /* SDLStreamingMediaConfiguration.h in Headers */, 4A8BD2FE24F938A4000945E3 /* SDLVehicleType.h in Headers */, 4ABB282B24F824E70061BF55 /* SDLTBTState.h in Headers */, - C9707D3025E0444D009D00F2 /* SDLMacros.h in Headers */, 4ABB26D724F7FAFD0061BF55 /* SDLRPCMessage.h in Headers */, 4A8BD24A24F93135000945E3 /* SDLMyKey.h in Headers */, 4ABB24F924F5959E0061BF55 /* SDLAsynchronousOperation.h in Headers */, @@ -8202,7 +8192,6 @@ 4ABB2AB724F847F40061BF55 /* SDLSetGlobalPropertiesResponse.m in Sources */, C9DFFE79257ACE0000F7D57A /* SDLSeekStreamingIndicator.m in Sources */, 4ABB2B0B24F84D950061BF55 /* SDLAppServiceData.m in Sources */, - 4A1B7A9E26B1EBAC00F9E574 /* SDLMenuReplaceOperation+MenuUniqueness.m in Sources */, 88D79EEE255D8D5B005FACB1 /* SDLPresentAlertOperation.m in Sources */, 4ABB282C24F824E70061BF55 /* SDLTBTState.m in Sources */, 4ABB27DF24F800CA0061BF55 /* SDLPowerModeQualificationStatus.m in Sources */, @@ -8537,7 +8526,6 @@ 4A8BD31124F938D6000945E3 /* SDLWeatherServiceManifest.m in Sources */, 4ABB271524F7FC4E0061BF55 /* SDLCompassDirection.m in Sources */, 4ABB254424F7E48D0061BF55 /* SDLLockScreenRootViewController.m in Sources */, - 4A96114825D1B5DA00D787DA /* SDLMacros.m in Sources */, 4A93895A25B9E5E40069F438 /* SDLMenuConfigurationUpdateOperation.m in Sources */, 4ABB265F24F7F5F20061BF55 /* SDLNotificationDispatcher.m in Sources */, 4A8BD31624F938D6000945E3 /* SDLWindowTypeCapabilities.m in Sources */, diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h index b89307c5c..5b9423651 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h @@ -35,7 +35,9 @@ typedef NS_ENUM(NSUInteger, SDLMenuCellUpdateState) { @param oldMenuCells The old menu array @param updatedMenuCells The new menu array */ -+ (nullable SDLDynamicMenuUpdateRunScore *)compareOldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells; ++ (SDLDynamicMenuUpdateRunScore *)compareOldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells; + ++ (SDLDynamicMenuUpdateRunScore *)compatibilityRunScoreWithOldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells; @end diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m index ad8249ee1..bfa46188f 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m @@ -15,21 +15,31 @@ @implementation SDLDynamicMenuUpdateAlgorithm -#pragma mark - Update Menu Cells -+ (nullable SDLDynamicMenuUpdateRunScore *)compareOldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells{ +#pragma mark Compatibility Menu Run Score + ++ (SDLDynamicMenuUpdateRunScore *)compatibilityRunScoreWithOldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells { + return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[self sdl_buildAllDeleteStatusesforMenu:oldMenuCells] updatedStatus:[self sdl_buildAllAddStatusesForMenu:updatedMenuCells] score:updatedMenuCells.count]; +} + +#pragma mark - Dynamic Menu Run Score + ++ (SDLDynamicMenuUpdateRunScore *)compareOldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells{ if (oldMenuCells.count > 0 && updatedMenuCells.count == 0) { + // Deleting all cells return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[SDLDynamicMenuUpdateAlgorithm sdl_buildAllDeleteStatusesforMenu:oldMenuCells] updatedStatus:@[] score:0]; }else if (oldMenuCells.count == 0 && updatedMenuCells.count > 0) { + // No cells to delete return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:@[] updatedStatus:[SDLDynamicMenuUpdateAlgorithm sdl_buildAllAddStatusesForMenu:updatedMenuCells] score:updatedMenuCells.count]; } else if (oldMenuCells.count == 0 && updatedMenuCells.count == 0) { - return nil; + // Empty menu to empty menu + return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:@[] updatedStatus:@[] score:0]; } return [SDLDynamicMenuUpdateAlgorithm sdl_startCompareAtRun:0 oldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; } -+ (nullable SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)startRun oldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells { - SDLDynamicMenuUpdateRunScore *bestScore = nil; ++ (SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)startRun oldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells { + SDLDynamicMenuUpdateRunScore *bestScore = [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:@[] updatedStatus:@[] score:0]; for (NSUInteger run = startRun; run < oldMenuCells.count; run++) { // Set the menu status as a 1-1 array, start off will oldMenus = all Deletes, newMenu = all Adds diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.h b/SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.h deleted file mode 100644 index 15eee20a8..000000000 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// SDLMenuReplaceOperation+MenuUniqueness.h -// SmartDeviceLink -// -// Created by Joel Fischer on 7/28/21. -// Copyright © 2021 smartdevicelink. All rights reserved. -// - -#import "SDLMenuReplaceOperation.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface SDLMenuReplaceOperation (MenuUniqueness) - -+ (void)sdl_addUniqueNamesToCells:(NSArray *)menuCells basedOnWindowCapability:(SDLWindowCapability *)windowCapability; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.m b/SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.m deleted file mode 100644 index a2fae9d3c..000000000 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation+MenuUniqueness.m +++ /dev/null @@ -1,86 +0,0 @@ -// -// SDLMenuReplaceOperation+MenuUniqueness.m -// SmartDeviceLink -// -// Created by Joel Fischer on 7/28/21. -// Copyright © 2021 smartdevicelink. All rights reserved. -// - -#import "SDLMenuReplaceOperation+MenuUniqueness.h" - -NS_ASSUME_NONNULL_BEGIN - -@implementation SDLMenuReplaceOperation (MenuUniqueness) - -+ (void)sdl_addUniqueNamesToCells:(NSArray *)menuCells basedOnWindowCapability:(SDLWindowCapability *)windowCapability { - SDLVersion *supportsMenuUniqueness = [[SDLVersion alloc] initWithMajor:7 minor:1 patch:0]; - if ([[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:supportsMenuUniqueness]) { - [self sdl_removeUnusedProperties:menuCells basedOnWindowCapability:windowCapability]; - } - - [self sdl_addUniqueNamesToCells:menuCells supportsMenuUniqueness:supportsMenuUniqueness]; -} - -+ (void)sdl_removeUnusedProperties:(NSArray *)menuCells basedOnWindowCapability:(SDLWindowCapability *)windowCapability { - NSArray *removePropertiesCopy = [[NSArray alloc] initWithArray:menuCells copyItems:YES]; - for (SDLMenuCell *cell in removePropertiesCopy) { - // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI - cell.voiceCommands = nil; - - // Don't check SDLImageFieldNameSubMenuIcon because it was added in 7.0 when the feature was added in 5.0. Just assume that if CommandIcon is not available, the submenu icon is not either. - if (![windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]) { - cell.icon = nil; - } - - if (cell.subCells != nil) { - if (![windowCapability hasTextFieldOfName:SDLTextFieldNameMenuSubMenuSecondaryText]) { - cell.secondaryText = nil; - } - if (![windowCapability hasTextFieldOfName:SDLTextFieldNameMenuSubMenuTertiaryText]) { - cell.tertiaryText = nil; - } - if (![windowCapability hasImageFieldOfName:SDLImageFieldNameMenuSubMenuSecondaryImage]) { - cell.secondaryArtwork = nil; - } - [self sdl_removeUnusedProperties:cell.subCells basedOnWindowCapability:windowCapability]; - } else { - if (![windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandSecondaryText]) { - cell.secondaryText = nil; - } - if (![windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandTertiaryText]) { - cell.tertiaryText = nil; - } - if (![windowCapability hasImageFieldOfName:SDLImageFieldNameMenuCommandSecondaryImage]) { - cell.secondaryArtwork = nil; - } - } - } -} - -+ (void)sdl_addUniqueNamesToCells:(NSArray *)menuCells supportsMenuUniqueness:(BOOL)supportsMenuUniqueness { - // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary - NSMutableDictionary *dictCounter = [[NSMutableDictionary alloc] init]; - for (NSUInteger i = 0; i < menuCells.count; i++) { - SDLMenuCell *cell = menuCells[i]; - NSNumber *counter = dictCounter[cell]; - if (counter != nil) { - counter = @(counter.intValue + 1); - dictCounter[cell] = counter; - } else { - dictCounter[cell] = @1; - } - - counter = dictCounter[cell]; - if (counter.intValue > 1) { - menuCells[i].uniqueTitle = [NSString stringWithFormat: @"%@ (%d)", menuCells[i].title, counter.intValue]; - } - - if (cell.subCells.count > 0) { - [self sdl_addUniqueNamesToCells:menuCells supportsMenuUniqueness:supportsMenuUniqueness]; - } - } -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.h b/SmartDeviceLink/private/SDLMenuReplaceOperation.h index 708b65c42..05a7d25f6 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.h +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.h @@ -29,7 +29,7 @@ typedef void(^SDLCurrentMenuUpdatedBlock)(NSArray *currentMenuCel @property (strong, nonatomic) SDLMenuConfiguration *menuConfiguration; @property (strong, nonatomic) NSArray *currentMenu; -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatibilityModeEnabled currentMenuUpdatedHandler:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock; +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatibilityModeEnabled currentMenuUpdatedHandler:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedHandler; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 9ee35c13c..0ef5e757e 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -18,7 +18,6 @@ #import "SDLLogMacros.h" #import "SDLMenuCell.h" #import "SDLMenuConfiguration.h" -#import "SDLMenuReplaceOperation+MenuUniqueness.h" #import "SDLTextFieldName.h" #import "SDLVersion.h" #import "SDLWindowCapability.h" @@ -49,16 +48,17 @@ @interface SDLMenuReplaceOperation () @property (weak, nonatomic) SDLFileManager *fileManager; @property (strong, nonatomic) NSArray *updatedMenu; @property (assign, nonatomic) BOOL compatibilityModeEnabled; -@property (copy, nonatomic) SDLCurrentMenuUpdatedBlock currentMenuUpdatedBlock; +@property (copy, nonatomic) SDLCurrentMenuUpdatedBlock currentMenuUpdatedHandler; @property (strong, nonatomic) NSMutableArray *mutableCurrentMenu; +@property (strong, nonatomic) NSArray *updatedStrippedMenu; @property (copy, nonatomic, nullable) NSError *internalError; @end @implementation SDLMenuReplaceOperation -- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatibilityModeEnabled currentMenuUpdatedHandler:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedBlock { +- (instancetype)initWithConnectionManager:(id)connectionManager fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability menuConfiguration:(SDLMenuConfiguration *)menuConfiguration currentMenu:(NSArray *)currentMenu updatedMenu:(NSArray *)updatedMenu compatibilityModeEnabled:(BOOL)compatibilityModeEnabled currentMenuUpdatedHandler:(SDLCurrentMenuUpdatedBlock)currentMenuUpdatedHandler { self = [super init]; if (!self) { return nil; } @@ -69,7 +69,7 @@ - (instancetype)initWithConnectionManager:(id)connecti _mutableCurrentMenu = [currentMenu mutableCopy]; _updatedMenu = [updatedMenu copy]; _compatibilityModeEnabled = compatibilityModeEnabled; - _currentMenuUpdatedBlock = currentMenuUpdatedBlock; + _currentMenuUpdatedHandler = currentMenuUpdatedHandler; return self; } @@ -78,22 +78,24 @@ - (void)start { [super start]; if (self.isCancelled) { return; } - [self.class sdl_addUniqueNamesToCells:self.updatedMenu basedOnWindowCapability:self.windowCapability]; + self.updatedStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.updatedStrippedMenu basedOnWindowCapability:self.windowCapability]; + + BOOL supportsMenuUniqueness = [[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithMajor:7 minor:1 patch:0]]; + [self.class sdl_addUniqueNamesToCells:self.updatedMenu supportsMenuUniqueness:supportsMenuUniqueness]; SDLDynamicMenuUpdateRunScore *runScore = nil; if (self.compatibilityModeEnabled) { SDLLogV(@"Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); - runScore = [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[self sdl_buildAllDeleteStatusesForMenu:self.currentMenu] updatedStatus:[self sdl_buildAllAddStatusesForMenu:self.updatedMenu] score:self.updatedMenu.count]; + runScore = [SDLDynamicMenuUpdateAlgorithm compatibilityRunScoreWithOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; } else { SDLLogV(@"Dynamic menu update active. Running the algorithm to find the best way to delete / add cells."); runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; } // If both old and new cells are empty, nothing needs to happen - if ((runScore.oldStatus.count == 0) && (runScore.updatedStatus.count == 0)) { - return [self finishOperation]; - } + if ((runScore.oldStatus.count == 0) && (runScore.updatedStatus.count == 0)) { return [self finishOperation]; } + // Drop the cells into buckets based on the run score NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:runScore.oldStatus]; NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares @@ -101,7 +103,7 @@ - (void)start { NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. - [self sdl_transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; + [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; // Upload the artworks, then we will start updating the main menu __weak typeof(self) weakself = self; @@ -169,7 +171,7 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; - [self sdl_transferCellIDFromOldCells:oldKeeps toKeptCells:newKeeps]; + [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { @@ -315,29 +317,75 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA return [keepMenuCells copy]; } -- (void)sdl_transferCellIDFromOldCells:(NSArray *)oldCells toKeptCells:(NSArray *)newCells { +- (void)sdl_transferCellIDsFromOldCells:(NSArray *)oldCells toKeptCells:(NSArray *)newCells { if (oldCells.count == 0) { return; } for (NSUInteger i = 0; i < newCells.count; i++) { newCells[i].cellId = oldCells[i].cellId; } } -- (NSArray *)sdl_buildAllDeleteStatusesForMenu:(NSArray *)menuCells { - NSMutableArray *mutableNumbers = [NSMutableArray arrayWithCapacity:menuCells.count]; - for (int i = 0; i < menuCells.count; i++) { - [mutableNumbers addObject:@(SDLMenuCellUpdateStateDelete)]; +#pragma mark - Menu Uniqueness + ++ (NSArray *)sdl_cellsWithRemovedPropertiesFromCells:(NSArray *)menuCells basedOnWindowCapability:(SDLWindowCapability *)windowCapability { + NSArray *removePropertiesCopy = [[NSArray alloc] initWithArray:menuCells copyItems:YES]; + for (SDLMenuCell *cell in removePropertiesCopy) { + // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI + cell.voiceCommands = nil; + + // Don't check SDLImageFieldNameSubMenuIcon because it was added in 7.0 when the feature was added in 5.0. Just assume that if CommandIcon is not available, the submenu icon is not either. + if (![windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]) { + cell.icon = nil; + } + + if (cell.subCells != nil) { + if (![windowCapability hasTextFieldOfName:SDLTextFieldNameMenuSubMenuSecondaryText]) { + cell.secondaryText = nil; + } + if (![windowCapability hasTextFieldOfName:SDLTextFieldNameMenuSubMenuTertiaryText]) { + cell.tertiaryText = nil; + } + if (![windowCapability hasImageFieldOfName:SDLImageFieldNameMenuSubMenuSecondaryImage]) { + cell.secondaryArtwork = nil; + } + cell.subCells = [self sdl_cellsWithRemovedPropertiesFromCells:cell.subCells basedOnWindowCapability:windowCapability]; + } else { + if (![windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandSecondaryText]) { + cell.secondaryText = nil; + } + if (![windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandTertiaryText]) { + cell.tertiaryText = nil; + } + if (![windowCapability hasImageFieldOfName:SDLImageFieldNameMenuCommandSecondaryImage]) { + cell.secondaryArtwork = nil; + } + } } - return [mutableNumbers copy]; + return removePropertiesCopy; } -- (NSArray *)sdl_buildAllAddStatusesForMenu:(NSArray *)menuCells { - NSMutableArray *mutableNumbers = [NSMutableArray arrayWithCapacity:menuCells.count]; - for (int i = 0; i < menuCells.count; i++) { - [mutableNumbers addObject:@(SDLMenuCellUpdateStateAdd)]; - } ++ (void)sdl_addUniqueNamesToCells:(NSArray *)menuCells supportsMenuUniqueness:(BOOL)supportsMenuUniqueness { + // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary + NSMutableDictionary, NSNumber *> *dictCounter = [[NSMutableDictionary alloc] init]; + for (NSUInteger i = 0; i < menuCells.count; i++) { + id key = supportsMenuUniqueness ? menuCells[i] : menuCells[i].title; + NSNumber *counter = dictCounter[key]; + if (counter != nil) { + counter = @(counter.intValue + 1); + dictCounter[key] = counter; + } else { + dictCounter[key] = @1; + } + + counter = dictCounter[key]; + if (counter.intValue > 1) { + menuCells[i].uniqueTitle = [NSString stringWithFormat: @"%@ (%d)", menuCells[i].title, counter.intValue]; + } - return [mutableNumbers copy]; + if (menuCells[i].subCells.count > 0) { + [self sdl_addUniqueNamesToCells:menuCells[i].subCells supportsMenuUniqueness:supportsMenuUniqueness]; + } + } } #pragma mark - Getter / Setters @@ -358,7 +406,7 @@ - (void)finishOperation { self.internalError = [NSError sdl_menuManager_replaceOperationCancelled]; } - self.currentMenuUpdatedBlock(self.currentMenu.copy, self.error); + self.currentMenuUpdatedHandler(self.currentMenu.copy, self.error); [super finishOperation]; } diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 8bad8a981..61bd3b12d 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -52,12 +52,18 @@ @implementation SDLMenuReplaceUtilities return [mutableArtworks allObjects]; } -+ (BOOL)sdl_shouldCellIncludeImage:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { - // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image +/// If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image ++ (BOOL)sdl_shouldCellIncludeImageFromCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { BOOL supportsImage = (cell.subCells.count > 0) ? [windowCapability hasImageFieldOfName:SDLImageFieldNameSubMenuIcon] : [windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]; return (cell.icon != nil) && supportsImage && ([fileManager hasUploadedFile:cell.icon] || cell.icon.isStaticIcon); } +/// If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image ++ (BOOL)sdl_shouldCellIncludeSecondaryImageFromCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { + BOOL supportsImage = (cell.subCells.count > 0) ? [windowCapability hasImageFieldOfName:SDLImageFieldNameMenuSubMenuSecondaryImage] : [windowCapability hasImageFieldOfName:SDLImageFieldNameMenuCommandSecondaryImage]; + return (cell.secondaryArtwork != nil) && supportsImage && ([fileManager hasUploadedFile:cell.secondaryArtwork] || cell.secondaryArtwork.isStaticIcon); +} + #pragma mark - RPC Commands + (UInt32)commandIdForRPCRequest:(SDLRPCRequest *)request { @@ -152,19 +158,23 @@ + (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFi SDLMenuParams *params = [[SDLMenuParams alloc] init]; params.menuName = cell.title; + params.secondaryText = [windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandSecondaryText] ? cell.secondaryText : nil; + params.tertiaryText = [windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandTertiaryText] ? cell.tertiaryText : nil; params.parentID = (cell.parentCellId != ParentIdNotFound) ? @(cell.parentCellId) : nil; params.position = @(position); command.menuParams = params; command.vrCommands = (cell.voiceCommands.count == 0) ? nil : cell.voiceCommands; - command.cmdIcon = [self sdl_shouldCellIncludeImage:cell fileManager:fileManager windowCapability:windowCapability] ? cell.icon.imageRPC : nil; + command.cmdIcon = [self sdl_shouldCellIncludeImageFromCell:cell fileManager:fileManager windowCapability:windowCapability] ? cell.icon.imageRPC : nil; + command.secondaryImage = [self sdl_shouldCellIncludeSecondaryImageFromCell:cell fileManager:fileManager windowCapability:windowCapability] ? cell.secondaryArtwork.imageRPC : nil; command.cmdID = @(cell.cellId); return command; } + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager position:(UInt16)position windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { - SDLImage *icon = [self sdl_shouldCellIncludeImage:cell fileManager:fileManager windowCapability:windowCapability] ? cell.icon.imageRPC : nil; + SDLImage *icon = [self sdl_shouldCellIncludeImageFromCell:cell fileManager:fileManager windowCapability:windowCapability] ? cell.icon.imageRPC : nil; + SDLImage *secondaryIcon = [self sdl_shouldCellIncludeSecondaryImageFromCell:cell fileManager:fileManager windowCapability:windowCapability] ? cell.secondaryArtwork.imageRPC : nil; SDLMenuLayout submenuLayout = nil; if (cell.submenuLayout && [windowCapability.menuLayoutsAvailable containsObject:cell.submenuLayout]) { @@ -173,7 +183,7 @@ + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager submenuLayout = defaultSubmenuLayout; } - return [[SDLAddSubMenu alloc] initWithMenuID:cell.cellId menuName:cell.title position:@(position) menuIcon:icon menuLayout:submenuLayout parentID:nil]; + return [[SDLAddSubMenu alloc] initWithMenuID:cell.cellId menuName:cell.title position:@(position) menuIcon:icon menuLayout:submenuLayout parentID:nil secondaryText:cell.secondaryText tertiaryText:cell.tertiaryText secondaryImage:secondaryIcon]; } #pragma mark - Updating Menu Cells From 23b0aea938abe6c41db65a85c2c809c755e5f8eb Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 30 Jul 2021 13:55:11 -0400 Subject: [PATCH 053/112] Dynamic run score now compares unique titles --- .../private/SDLDynamicMenuUpdateAlgorithm.h | 3 ++- .../private/SDLDynamicMenuUpdateAlgorithm.m | 23 ++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h index 5b9423651..a07a8c1de 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h @@ -10,6 +10,7 @@ @class SDLDynamicMenuUpdateRunScore; @class SDLMenuCell; +@class SDLWindowCapability; NS_ASSUME_NONNULL_BEGIN @@ -35,7 +36,7 @@ typedef NS_ENUM(NSUInteger, SDLMenuCellUpdateState) { @param oldMenuCells The old menu array @param updatedMenuCells The new menu array */ -+ (SDLDynamicMenuUpdateRunScore *)compareOldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells; ++ (SDLDynamicMenuUpdateRunScore *)dynamicRunScoreOldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells; + (SDLDynamicMenuUpdateRunScore *)compatibilityRunScoreWithOldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells; diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m index bfa46188f..9bdf0538d 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m @@ -10,23 +10,30 @@ #import "SDLDynamicMenuUpdateRunScore.h" #import "SDLMenuCell.h" #import "SDLLogMacros.h" +#import "SDLWindowCapability.h" NS_ASSUME_NONNULL_BEGIN +@interface SDLMenuCell () + +- (BOOL)isEqualToCellWithUniqueTitle:(SDLMenuCell *)cell; + +@end + @implementation SDLDynamicMenuUpdateAlgorithm #pragma mark Compatibility Menu Run Score + (SDLDynamicMenuUpdateRunScore *)compatibilityRunScoreWithOldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells { - return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[self sdl_buildAllDeleteStatusesforMenu:oldMenuCells] updatedStatus:[self sdl_buildAllAddStatusesForMenu:updatedMenuCells] score:updatedMenuCells.count]; + return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[self sdl_buildAllDeleteStatusesForMenu:oldMenuCells] updatedStatus:[self sdl_buildAllAddStatusesForMenu:updatedMenuCells] score:updatedMenuCells.count]; } #pragma mark - Dynamic Menu Run Score -+ (SDLDynamicMenuUpdateRunScore *)compareOldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells{ ++ (SDLDynamicMenuUpdateRunScore *)dynamicRunScoreOldMenuCells:(NSArray *)oldMenuCells updatedMenuCells:(NSArray *)updatedMenuCells { if (oldMenuCells.count > 0 && updatedMenuCells.count == 0) { // Deleting all cells - return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[SDLDynamicMenuUpdateAlgorithm sdl_buildAllDeleteStatusesforMenu:oldMenuCells] updatedStatus:@[] score:0]; + return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[SDLDynamicMenuUpdateAlgorithm sdl_buildAllDeleteStatusesForMenu:oldMenuCells] updatedStatus:@[] score:0]; }else if (oldMenuCells.count == 0 && updatedMenuCells.count > 0) { // No cells to delete return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:@[] updatedStatus:[SDLDynamicMenuUpdateAlgorithm sdl_buildAllAddStatusesForMenu:updatedMenuCells] score:updatedMenuCells.count]; @@ -43,14 +50,14 @@ + (SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)startRun old for (NSUInteger run = startRun; run < oldMenuCells.count; run++) { // Set the menu status as a 1-1 array, start off will oldMenus = all Deletes, newMenu = all Adds - NSMutableArray *oldMenuStatus = [SDLDynamicMenuUpdateAlgorithm sdl_buildAllDeleteStatusesforMenu:oldMenuCells]; + NSMutableArray *oldMenuStatus = [SDLDynamicMenuUpdateAlgorithm sdl_buildAllDeleteStatusesForMenu:oldMenuCells]; NSMutableArray *newMenuStatus = [SDLDynamicMenuUpdateAlgorithm sdl_buildAllAddStatusesForMenu:updatedMenuCells]; NSUInteger startIndex = 0; for (NSUInteger oldCellIndex = run; oldCellIndex < oldMenuCells.count; oldCellIndex++) { //For each old item // Create inner loop to compare old cells to new cells to find a match, if a match if found we mark the index at match for both the old and the new status to keep since we do not want to send RPCs for those cases for (NSUInteger newCellIndex = startIndex; newCellIndex < updatedMenuCells.count; newCellIndex++) { - if ([oldMenuCells[oldCellIndex] isEqual:updatedMenuCells[newCellIndex]]) { + if ([oldMenuCells[oldCellIndex] isEqualToCellWithUniqueTitle:updatedMenuCells[newCellIndex]]) { oldMenuStatus[oldCellIndex] = @(SDLMenuCellUpdateStateKeep); newMenuStatus[newCellIndex] = @(SDLMenuCellUpdateStateKeep); startIndex = newCellIndex + 1; @@ -86,12 +93,12 @@ + (SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)startRun old @param oldMenu The old menu array */ -+ (NSMutableArray *)sdl_buildAllDeleteStatusesforMenu:(NSArray *)oldMenu { ++ (NSMutableArray *)sdl_buildAllDeleteStatusesForMenu:(NSArray *)oldMenu { NSMutableArray *oldMenuStatus = [[NSMutableArray alloc] initWithCapacity:oldMenu.count]; for (NSUInteger index = 0; index < oldMenu.count; index++) { [oldMenuStatus addObject:@(SDLMenuCellUpdateStateDelete)]; } - return [oldMenuStatus mutableCopy]; + return oldMenuStatus; } /** @@ -104,7 +111,7 @@ + (SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)startRun old for (NSUInteger index = 0; index < newMenu.count; index++) { [newMenuStatus addObject:@(SDLMenuCellUpdateStateAdd)]; } - return [newMenuStatus mutableCopy]; + return newMenuStatus; } @end From f884416b029657af516a1bd5e7c1486088875d50 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 30 Jul 2021 13:57:54 -0400 Subject: [PATCH 054/112] Add SDLMenuCell equivalency method including unique titles --- SmartDeviceLink/public/SDLMenuCell.h | 5 +++++ SmartDeviceLink/public/SDLMenuCell.m | 23 ++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/SmartDeviceLink/public/SDLMenuCell.h b/SmartDeviceLink/public/SDLMenuCell.h index fc658725e..18c49c40c 100644 --- a/SmartDeviceLink/public/SDLMenuCell.h +++ b/SmartDeviceLink/public/SDLMenuCell.h @@ -125,6 +125,11 @@ typedef void(^SDLMenuCellSelectionHandler)(SDLTriggerSource triggerSource); */ - (instancetype)initWithTitle:(NSString *)title secondaryText:(nullable NSString *)secondaryText tertiaryText:(nullable NSString *)tertiaryText icon:(nullable SDLArtwork *)icon secondaryArtwork:(nullable SDLArtwork *)secondaryArtwork submenuLayout:(nullable SDLMenuLayout)layout subCells:(NSArray *)subCells; +/// Check cell equality including the internally used `uniqueTitle` property. To compare without `uniqueTitle`, use the `isEqual:` method. +/// @param cell The other cell to compare with +/// @return True if the cells are equal, including the `uniqueTitle` property, False otherwise +- (BOOL)isEqualToCellWithUniqueTitle:(SDLMenuCell *)cell; + @end NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/public/SDLMenuCell.m b/SmartDeviceLink/public/SDLMenuCell.m index 05ab2a786..737a6049c 100644 --- a/SmartDeviceLink/public/SDLMenuCell.m +++ b/SmartDeviceLink/public/SDLMenuCell.m @@ -8,9 +8,10 @@ #import "SDLMenuCell.h" +#import "NSArray+Extensions.h" #import "SDLArtwork.h" #import "SDLMacros.h" -#import "NSArray+Extensions.h" +#import "SDLWindowCapability+ScreenManagerExtensions.h" NS_ASSUME_NONNULL_BEGIN @@ -96,10 +97,10 @@ - (NSUInteger)hash { return NSUIntRotateCell(self.title.hash, NSUIntBitCell / 2) ^ NSUIntRotateCell(self.icon.name.hash, NSUIntBitCell / 3) ^ NSUIntRotateCell(self.voiceCommands.dynamicHash, NSUIntBitCell / 4) - ^ NSUIntRotateCell((self.subCells.count != 0), NSUIntBitCell / 5) - ^ NSUIntRotateCell(self.secondaryText.hash, NSUIntBitCell / 6) - ^ NSUIntRotateCell(self.tertiaryText.hash, NSUIntBitCell / 7) - ^ NSUIntRotateCell(self.secondaryArtwork.name.hash, NSUIntBitCell / 8) + ^ NSUIntRotateCell((self.subCells.count != 0), NSUIntBitCell / 5) + ^ NSUIntRotateCell(self.secondaryText.hash, NSUIntBitCell / 6) + ^ NSUIntRotateCell(self.tertiaryText.hash, NSUIntBitCell / 7) + ^ NSUIntRotateCell(self.secondaryArtwork.name.hash, NSUIntBitCell / 8) ^ NSUIntRotateCell(self.submenuLayout.hash, NSUIntBitCell / 9); } @@ -116,6 +117,18 @@ - (BOOL)isEqualToCell:(SDLMenuCell *)cell { return (self.hash == cell.hash); } +#pragma mark With Unique Title + +- (BOOL)isEqualToCellWithUniqueTitle:(SDLMenuCell *)cell { + if (cell == nil) { return NO; } + + return ([self sdl_hashWithUniqueTitle] == [cell sdl_hashWithUniqueTitle]); +} + +- (NSUInteger)sdl_hashWithUniqueTitle { + return self.hash ^ NSUIntRotateCell(self.uniqueTitle.hash, NSUIntBitCell / 10); +} + #pragma mark - Copying - (id)copyWithZone:(nullable NSZone *)zone { From 82d3ba6e3737d494362a223b83daeb77397179ac Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 30 Jul 2021 13:58:08 -0400 Subject: [PATCH 055/112] Remove unneeded private import --- SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m | 6 ------ 1 file changed, 6 deletions(-) diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m index 9bdf0538d..a69f0453f 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m @@ -14,12 +14,6 @@ NS_ASSUME_NONNULL_BEGIN -@interface SDLMenuCell () - -- (BOOL)isEqualToCellWithUniqueTitle:(SDLMenuCell *)cell; - -@end - @implementation SDLDynamicMenuUpdateAlgorithm #pragma mark Compatibility Menu Run Score From 66a09c32719c16e3b0173d0ec52344cda8e9ffe8 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 30 Jul 2021 13:58:23 -0400 Subject: [PATCH 056/112] Fixes to menu replace operation for previous changes --- .../private/SDLMenuReplaceOperation.m | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 0ef5e757e..340935884 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -44,13 +44,16 @@ @interface SDLMenuCell() @interface SDLMenuReplaceOperation () +// Dependencies @property (weak, nonatomic) id connectionManager; @property (weak, nonatomic) SDLFileManager *fileManager; @property (strong, nonatomic) NSArray *updatedMenu; +@property (strong, nonatomic) NSMutableArray *mutableCurrentMenu; @property (assign, nonatomic) BOOL compatibilityModeEnabled; @property (copy, nonatomic) SDLCurrentMenuUpdatedBlock currentMenuUpdatedHandler; -@property (strong, nonatomic) NSMutableArray *mutableCurrentMenu; +// Internal properties +@property (strong, nonatomic) NSArray *currentStrippedMenu; @property (strong, nonatomic) NSArray *updatedStrippedMenu; @property (copy, nonatomic, nullable) NSError *internalError; @@ -78,10 +81,12 @@ - (void)start { [super start]; if (self.isCancelled) { return; } - self.updatedStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.updatedStrippedMenu basedOnWindowCapability:self.windowCapability]; + self.updatedStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.updatedMenu basedOnWindowCapability:self.windowCapability]; + self.currentStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.mutableCurrentMenu basedOnWindowCapability:self.windowCapability]; BOOL supportsMenuUniqueness = [[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithMajor:7 minor:1 patch:0]]; - [self.class sdl_addUniqueNamesToCells:self.updatedMenu supportsMenuUniqueness:supportsMenuUniqueness]; + [self.class sdl_addUniqueNamesToCells:self.updatedStrippedMenu supportsMenuUniqueness:supportsMenuUniqueness]; + [self.class sdl_applyUniqueNamesOnCells:self.updatedStrippedMenu toCells:self.updatedMenu]; SDLDynamicMenuUpdateRunScore *runScore = nil; if (self.compatibilityModeEnabled) { @@ -89,7 +94,7 @@ - (void)start { runScore = [SDLDynamicMenuUpdateAlgorithm compatibilityRunScoreWithOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; } else { SDLLogV(@"Dynamic menu update active. Running the algorithm to find the best way to delete / add cells."); - runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; + runScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:self.currentMenu updatedMenuCells:self.updatedStrippedMenu]; } // If both old and new cells are empty, nothing needs to happen @@ -161,7 +166,7 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells if (oldKeptCells.count == 0 || startIndex >= oldKeptCells.count) { return; } if (oldKeptCells[startIndex].subCells.count > 0) { - SDLDynamicMenuUpdateRunScore *tempScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldKeptCells[startIndex].subCells updatedMenuCells:newKeptCells[startIndex].subCells]; + SDLDynamicMenuUpdateRunScore *tempScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:oldKeptCells[startIndex].subCells updatedMenuCells:newKeptCells[startIndex].subCells]; NSArray *deleteMenuStatus = tempScore.oldStatus; NSArray *addMenuStatus = tempScore.updatedStatus; @@ -388,6 +393,17 @@ + (void)sdl_addUniqueNamesToCells:(NSArray *)menuCells supportsMe } } ++ (void)sdl_applyUniqueNamesOnCells:(NSArray *)fromMenuCells toCells:(NSArray *)toMenuCells { + NSParameterAssert(fromMenuCells.count == toMenuCells.count); + + for (NSUInteger i = 0; i < fromMenuCells.count; i++) { + toMenuCells[i].uniqueTitle = fromMenuCells[i].uniqueTitle; + if (fromMenuCells[i].subCells.count > 0) { + [self sdl_applyUniqueNamesOnCells:fromMenuCells[i].subCells toCells:toMenuCells[i].subCells]; + } + } +} + #pragma mark - Getter / Setters - (void)setCurrentMenu:(NSArray *)currentMenu { From 6b20df4f0261da5560b4a7b6fe0ab74fe94ec513 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 30 Jul 2021 15:10:05 -0400 Subject: [PATCH 057/112] Refactoring uploading menu artworks --- .../private/SDLMenuReplaceOperation.m | 46 +++++++++---------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 340935884..eab001f57 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -112,38 +112,34 @@ - (void)start { // Upload the artworks, then we will start updating the main menu __weak typeof(self) weakself = self; - NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; - if (artworksToBeUploaded.count > 0) { - [self.fileManager uploadArtworks:artworksToBeUploaded progressHandler:^BOOL(NSString * _Nonnull artworkName, float uploadPercentage, NSError * _Nullable error) { - if (weakself.isCancelled) { - [weakself finishOperation]; - return NO; - } - - return YES; - } completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { - if (weakself.isCancelled) { return; } - if (error != nil) { SDLLogE(@"Error uploading menu artworks: %@", error); } - - SDLLogD(@"Menu artworks uploaded, beginning upload of main menu"); - // Start updating the main menu cells - [weakself sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { - if (weakself.isCancelled) { return [weakself finishOperation]; } - // Start uploading the submenu cells - [weakself sdl_updateSubMenuWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; - }]; - }]; - } else { - // Cells have no artwork to load + [self sdl_uploadMenuArtworksWithCompletionHandler:^(NSError * _Nullable error) { + if (weakself.isCancelled) { return [self finishOperation] } [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { if (weakself.isCancelled) { return [weakself finishOperation]; } [weakself sdl_updateSubMenuWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; }]; - } + }]; } #pragma mark - Update Main Menu / Submenu +- (void)sdl_uploadMenuArtworksWithCompletionHandler:(void(^)(NSError *_Nullable error))handler { + NSArray *artworksToBeUploaded = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:self.updatedMenu fileManager:self.fileManager windowCapability:self.windowCapability]; + if (artworksToBeUploaded.count == 0) { return handler(nil); } + + __weak typeof(self) weakself = self; + [self.fileManager uploadArtworks:artworksToBeUploaded progressHandler:^BOOL(NSString * _Nonnull artworkName, float uploadPercentage, NSError * _Nullable error) { + // If we're cancelled, stop uploading + return !weakself.isCancelled; + } completionHandler:^(NSArray * _Nonnull artworkNames, NSError * _Nullable error) { + if (error != nil) { SDLLogE(@"Error uploading menu artworks: %@", error); } + + SDLLogD(@"Menu artwork upload completed, beginning upload of main menu"); + // Start updating the main menu cells + handler(error); + }]; +} + /// Takes the main menu cells to delete and add, and deletes the current menu cells, then adds the new menu cells in the correct locations /// @param deleteCells The cells that need to be deleted /// @param addCells The cells that need to be added @@ -422,7 +418,7 @@ - (void)finishOperation { self.internalError = [NSError sdl_menuManager_replaceOperationCancelled]; } - self.currentMenuUpdatedHandler(self.currentMenu.copy, self.error); + self.currentMenuUpdatedHandler(self.currentMenu, self.error); [super finishOperation]; } From 271ce1a0b203e219c7e6defeee55ece27d98fa6b Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 4 Aug 2021 09:04:30 -0400 Subject: [PATCH 058/112] Fix missing semicolon --- SmartDeviceLink/private/SDLMenuReplaceOperation.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index eab001f57..193094ac0 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -113,7 +113,7 @@ - (void)start { // Upload the artworks, then we will start updating the main menu __weak typeof(self) weakself = self; [self sdl_uploadMenuArtworksWithCompletionHandler:^(NSError * _Nullable error) { - if (weakself.isCancelled) { return [self finishOperation] } + if (weakself.isCancelled) { return [self finishOperation]; } [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { if (weakself.isCancelled) { return [weakself finishOperation]; } [weakself sdl_updateSubMenuWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; From 16e9a67ecd1693a9845bb0a9268efe542a95a50b Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 4 Aug 2021 11:40:03 -0400 Subject: [PATCH 059/112] Fix replace operation to properly finish various tasks --- .../private/SDLMenuReplaceOperation.m | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 193094ac0..7fbf03ef8 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -111,12 +111,20 @@ - (void)start { [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; // Upload the artworks, then we will start updating the main menu - __weak typeof(self) weakself = self; + __weak typeof(self) weakSelf = self; [self sdl_uploadMenuArtworksWithCompletionHandler:^(NSError * _Nullable error) { - if (weakself.isCancelled) { return [self finishOperation]; } - [self sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { - if (weakself.isCancelled) { return [weakself finishOperation]; } - [weakself sdl_updateSubMenuWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0]; + __strong typeof(weakSelf) strongSelf = weakSelf; + if (strongSelf.isCancelled) { return [strongSelf finishOperation]; } + if (error != nil) { return [strongSelf finishOperationWithError:error]; } + + [strongSelf sdl_updateMenuWithCellsToDelete:cellsToDelete cellsToAdd:cellsToAdd completionHandler:^(NSError * _Nullable error) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (strongSelf.isCancelled) { return [strongSelf finishOperation]; } + if (error != nil) { return [strongSelf finishOperationWithError:error]; } + + [strongSelf sdl_updateSubMenuWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0 completionHandler:^(NSError * _Nullable error) { + return [strongSelf finishOperationWithError:error]; + }]; }]; }]; } @@ -145,10 +153,11 @@ - (void)sdl_uploadMenuArtworksWithCompletionHandler:(void(^)(NSError *_Nullable /// @param addCells The cells that need to be added /// @param handler A handler called when complete - (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(void(^)(NSError *_Nullable error))handler { - __weak typeof(self) weakself = self; + __weak typeof(self) weakSelf = self; [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { - if (weakself.isCancelled) { return handler(error); } - [weakself sdl_sendNewMenuCells:addCells oldMenu:weakself.currentMenu withCompletionHandler:^(NSError * _Nullable error) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (strongSelf.isCancelled) { return handler(error); } + [strongSelf sdl_sendNewMenuCells:addCells oldMenu:strongSelf.currentMenu withCompletionHandler:^(NSError * _Nullable error) { handler(error); }]; }]; @@ -158,7 +167,7 @@ - (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells ce /// @param oldKeptCells The old kept cells /// @param newKeptCells The new kept cells /// @param startIndex The index of the main menu to use -- (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells newKeptCells:(NSArray *)newKeptCells atIndex:(NSUInteger)startIndex { +- (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells newKeptCells:(NSArray *)newKeptCells atIndex:(NSUInteger)startIndex completionHandler:(void(^)(NSError *_Nullable error))completionHandler { if (oldKeptCells.count == 0 || startIndex >= oldKeptCells.count) { return; } if (oldKeptCells[startIndex].subCells.count > 0) { @@ -176,18 +185,24 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { - if (weakself.isCancelled) { return [weakself finishOperation]; } + if (weakself.isCancelled) { return completionHandler([NSError sdl_menuManager_replaceOperationCancelled]); } + if (error != nil) { return completionHandler(error); } [weakself sdl_sendNewMenuCells:cellsToAdd oldMenu:weakself.currentMenu[startIndex].subCells withCompletionHandler:^(NSError * _Nullable error) { - if (weakself.isCancelled) { return [weakself finishOperation]; } + if (weakself.isCancelled) { return completionHandler([NSError sdl_menuManager_replaceOperationCancelled]); } + if (error != nil) { return completionHandler(error); } // After the first set of submenu cells were added and deleted we must find the next set of subcells until we loop through all the elements - [weakself sdl_updateSubMenuWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; + [weakself sdl_updateSubMenuWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1) completionHandler:^(NSError * _Nullable error) { + completionHandler(error); + }]; }]; }]; } else { // There are no subcells, we can skip to the next index. - [self sdl_updateSubMenuWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1)]; + [self sdl_updateSubMenuWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1) completionHandler:^(NSError * _Nullable error) { + completionHandler(error); + }]; } } @@ -412,6 +427,11 @@ - (void)setCurrentMenu:(NSArray *)currentMenu { #pragma mark - Operation Overrides +- (void)finishOperationWithError:(NSError *)error { + self.internalError = error; + [self finishOperation]; +} + - (void)finishOperation { SDLLogV(@"Finishing menu manager dynamic replace operation"); if (self.isCancelled) { From 35fafa83611a293fa44a47d448211ceb4f0b7739 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 4 Aug 2021 13:00:51 -0400 Subject: [PATCH 060/112] Fix missing files from log module map --- SmartDeviceLink/private/SDLLogFileModuleMap.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SmartDeviceLink/private/SDLLogFileModuleMap.m b/SmartDeviceLink/private/SDLLogFileModuleMap.m index 385ef836d..2ae0a3ab7 100644 --- a/SmartDeviceLink/private/SDLLogFileModuleMap.m +++ b/SmartDeviceLink/private/SDLLogFileModuleMap.m @@ -135,7 +135,7 @@ + (SDLLogFileModule *)sdl_screenManagerAlertModule { } + (SDLLogFileModule *)sdl_screenManagerMenuModule { - return [SDLLogFileModule moduleWithName:@"Screen/Menu" files:[NSSet setWithArray:@[@"SDLMenuManager", @"SDLVoiceCommandManager", @"SDLVoiceCommandUpdateOperation"]]]; + return [SDLLogFileModule moduleWithName:@"Screen/Menu" files:[NSSet setWithArray:@[@"SDLMenuManager", @"SDLMenuReplaceOperation", @"SDLMenuShowOperation", @"SDLMenuConfigurationUpdateOperation", @"SDLVoiceCommandManager", @"SDLVoiceCommandUpdateOperation"]]]; } + (SDLLogFileModule *)sdl_screenManagerChoiceSetModule { From 637f3e5733e55abf7abd197ea7e570080b3dd96a Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 4 Aug 2021 13:13:56 -0400 Subject: [PATCH 061/112] Fix missing completion handler call --- SmartDeviceLink/private/SDLMenuReplaceOperation.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 7fbf03ef8..accd20878 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -168,7 +168,7 @@ - (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells ce /// @param newKeptCells The new kept cells /// @param startIndex The index of the main menu to use - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells newKeptCells:(NSArray *)newKeptCells atIndex:(NSUInteger)startIndex completionHandler:(void(^)(NSError *_Nullable error))completionHandler { - if (oldKeptCells.count == 0 || startIndex >= oldKeptCells.count) { return; } + if (oldKeptCells.count == 0 || startIndex >= oldKeptCells.count) { return completionHandler(nil); } if (oldKeptCells[startIndex].subCells.count > 0) { SDLDynamicMenuUpdateRunScore *tempScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:oldKeptCells[startIndex].subCells updatedMenuCells:newKeptCells[startIndex].subCells]; From cf9b0d93aeb05e5ac6b31b02618fa832ebb8a574 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 4 Aug 2021 14:34:58 -0400 Subject: [PATCH 062/112] Fix menu not using unique titles --- SmartDeviceLink/private/SDLMenuReplaceUtilities.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 61bd3b12d..fb6e4decd 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -157,7 +157,7 @@ + (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFi SDLAddCommand *command = [[SDLAddCommand alloc] init]; SDLMenuParams *params = [[SDLMenuParams alloc] init]; - params.menuName = cell.title; + params.menuName = cell.uniqueTitle; params.secondaryText = [windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandSecondaryText] ? cell.secondaryText : nil; params.tertiaryText = [windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandTertiaryText] ? cell.tertiaryText : nil; params.parentID = (cell.parentCellId != ParentIdNotFound) ? @(cell.parentCellId) : nil; @@ -183,7 +183,7 @@ + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager submenuLayout = defaultSubmenuLayout; } - return [[SDLAddSubMenu alloc] initWithMenuID:cell.cellId menuName:cell.title position:@(position) menuIcon:icon menuLayout:submenuLayout parentID:nil secondaryText:cell.secondaryText tertiaryText:cell.tertiaryText secondaryImage:secondaryIcon]; + return [[SDLAddSubMenu alloc] initWithMenuID:cell.cellId menuName:cell.uniqueTitle position:@(position) menuIcon:icon menuLayout:submenuLayout parentID:nil secondaryText:cell.secondaryText tertiaryText:cell.tertiaryText secondaryImage:secondaryIcon]; } #pragma mark - Updating Menu Cells From 5cb8098798ae392af0c3ec6752bd4b911522ccec Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 4 Aug 2021 14:45:12 -0400 Subject: [PATCH 063/112] Change some debug logs to verbose --- SmartDeviceLink/private/SDLIAPDataSession.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SmartDeviceLink/private/SDLIAPDataSession.m b/SmartDeviceLink/private/SDLIAPDataSession.m index 8c6cc84e8..06d2e1f1d 100644 --- a/SmartDeviceLink/private/SDLIAPDataSession.m +++ b/SmartDeviceLink/private/SDLIAPDataSession.m @@ -60,7 +60,7 @@ - (void)writeDataToSessionStream { [self.sendDataQueue popBuffer]; } else { // Cleave the sent bytes from the data, the remainder will sit at the head of the queue - SDLLogD(@"SDLIAPDataSession writeDataToSessionStream bytes written %ld", (long)bytesWritten); + SDLLogV(@"SDLIAPDataSession writeDataToSessionStream bytes written %ld", (long)bytesWritten); [remainder replaceBytesInRange:NSMakeRange(0, (NSUInteger)bytesWritten) withBytes:NULL length:0]; } } else { @@ -69,7 +69,7 @@ - (void)writeDataToSessionStream { } }]; } else { - SDLLogD(@"No more data to write to data session's output stream for IAPSession %@", self.iapSession); + SDLLogV(@"No more data to write to data session's output stream for IAPSession %@", self.iapSession); return; } } From 04cb08b6aca1b71e3651edd4a0a382343e774677 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 5 Aug 2021 11:38:56 -0400 Subject: [PATCH 064/112] Fix dynamic update algorithm failing in many cases --- SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m | 4 ++-- SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.h | 3 +++ SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.m | 4 ++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m index a69f0453f..23a500988 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m @@ -73,8 +73,8 @@ + (SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)startRun old if (numberOfAdds == 0) { return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:oldMenuStatus updatedStatus:newMenuStatus score:numberOfAdds]; } - // if we havent create the bestScore object or if the current score beats the old score then we will create a new bestScore - if (bestScore == nil || numberOfAdds < bestScore.score) { + // if we haven't create the bestScore object or if the current score beats the old score then we will create a new bestScore + if (bestScore.isEmpty || numberOfAdds < bestScore.score) { bestScore = [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:oldMenuStatus updatedStatus:newMenuStatus score:numberOfAdds]; } } diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.h b/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.h index 9e70fb39a..5ec70a6d8 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.h +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.h @@ -27,6 +27,9 @@ NS_ASSUME_NONNULL_BEGIN */ @property (assign, nonatomic, readonly) NSUInteger score; +/// Contains no old score, new score, or score +@property (assign, nonatomic, readonly) BOOL isEmpty; + - (instancetype)initWithOldStatus:(NSArray *)oldStatus updatedStatus:(NSArray *)updatedStatus score:(NSUInteger)score; @end diff --git a/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.m b/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.m index 568ad5399..c5f8b8608 100644 --- a/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.m +++ b/SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.m @@ -44,6 +44,10 @@ - (NSString *)description { return [mutableStringArray copy]; } +- (BOOL)isEmpty { + return (self.oldStatus.count == 0 && self.updatedStatus.count == 0 && self.score == 0); +} + @end NS_ASSUME_NONNULL_END From a551716694b23906a1961f90d2fe22e2ddaf3137 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 5 Aug 2021 11:39:23 -0400 Subject: [PATCH 065/112] Update menu config update op * Pass back an error if one occurs --- .../private/SDLMenuConfigurationUpdateOperation.h | 2 +- .../private/SDLMenuConfigurationUpdateOperation.m | 2 +- SmartDeviceLink/private/SDLMenuManager.m | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h index 27a220149..ff2318e36 100644 --- a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h @@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN -typedef void(^SDLMenuConfigurationUpdatedBlock)(SDLMenuConfiguration *newMenuConfiguration); +typedef void(^SDLMenuConfigurationUpdatedBlock)(SDLMenuConfiguration *newMenuConfiguration, NSError *_Nullable error); @interface SDLMenuConfigurationUpdateOperation : SDLAsynchronousOperation diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m index 8ede72988..e9bcc76b2 100644 --- a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m @@ -78,7 +78,7 @@ - (void)finishOperation { } if (self.internalError == nil) { - self.menuConfigurationUpdatedBlock(self.updatedMenuConfiguration); + self.menuConfigurationUpdatedBlock(self.updatedMenuConfiguration, self.internalError); } [super finishOperation]; diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index f0621dfd3..a70c6b56a 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -171,7 +171,10 @@ - (void)setMenuConfiguration:(SDLMenuConfiguration *)menuConfiguration { // Create the operation __weak typeof(self) weakself = self; - SDLMenuConfigurationUpdateOperation *configurationUpdateOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:self.connectionManager windowCapability:self.windowCapability newMenuConfiguration:menuConfiguration configurationUpdatedHandler:^(SDLMenuConfiguration * _Nonnull newMenuConfiguration) { + SDLMenuConfigurationUpdateOperation *configurationUpdateOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:self.connectionManager windowCapability:self.windowCapability newMenuConfiguration:menuConfiguration configurationUpdatedHandler:^(SDLMenuConfiguration *newMenuConfiguration, NSError *_Nullable error) { + if (error != nil) { + SDLLogE(@"Error updating menu configuration: %@", error); + } weakself.currentMenuConfiguration = newMenuConfiguration; [weakself sdl_updateMenuReplaceOperationsWithNewMenuConfiguration]; }]; From e13e06038e5152b7a6560842d987d841a70b0fdd Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 5 Aug 2021 11:39:40 -0400 Subject: [PATCH 066/112] Fix menu replace operation warning --- SmartDeviceLink/private/SDLMenuReplaceOperation.m | 7 +++++-- SmartDeviceLink/private/SDLMenuReplaceUtilities.m | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index accd20878..2df9834e4 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -427,8 +427,11 @@ - (void)setCurrentMenu:(NSArray *)currentMenu { #pragma mark - Operation Overrides -- (void)finishOperationWithError:(NSError *)error { - self.internalError = error; +- (void)finishOperationWithError:(nullable NSError *)error { + if (error != nil) { + self.internalError = error; + } + [self finishOperation]; } diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index fb6e4decd..2e0aa7ace 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -189,6 +189,7 @@ + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager #pragma mark - Updating Menu Cells #pragma mark Remove Cell + + (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId { for (SDLMenuCell *menuCell in menuCellList) { if (menuCell.cellId == commandId) { @@ -210,6 +211,7 @@ + (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList wit } #pragma mark Inserting Cell + + (BOOL)addMenuRequestWithCommandId:(UInt32)commandId position:(UInt16)position fromNewMenuList:(NSArray *)newMenuList toMainMenuList:(NSMutableArray *)mainMenuList { SDLMenuCell *addedCell = nil; for (SDLMenuCell *cell in newMenuList) { From aa04b9c5766547aa37dd5d6df8875ab7b8b3f737 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 5 Aug 2021 11:39:48 -0400 Subject: [PATCH 067/112] Update tests for changes --- .../SDLMenuConfigurationUpdateOperationSpec.m | 37 +++++++++---------- .../DevAPISpecs/SDLMenuManagerSpec.m | 14 +++---- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 14 +++---- .../SDLMenuReplaceUtilitiesSpecHelpers.m | 15 +++++--- .../DevAPISpecs/SDLMenuShowOperationSpec.m | 14 +++++-- .../SDLMenuUpdateAlgorithmSpec.m | 21 +++++------ 6 files changed, 61 insertions(+), 54 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m index 1f199b58e..78bb19aeb 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m @@ -25,28 +25,26 @@ SDLMenuConfiguration *testMenuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutList defaultSubmenuLayout:SDLMenuLayoutTiles]; __block SDLMenuConfigurationUpdatedBlock testUpdatedBlock = nil; - __block SDLMenuConfiguration *updatedBlockMenuConfiguration = nil; + __block SDLMenuConfiguration *resultMenuConfiguration = nil; + __block NSError *resultError = nil; beforeEach(^{ testConnectionManager = [[TestConnectionManager alloc] init]; testFileManager = OCMClassMock([SDLFileManager class]); - testWindowCapability = nil; + testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[] dynamicUpdateCapabilities:nil keyboardCapabilities:nil]; - updatedBlockMenuConfiguration = nil; - testUpdatedBlock = ^(SDLMenuConfiguration *newConfiguration) { - updatedBlockMenuConfiguration = newConfiguration; + resultMenuConfiguration = nil; + resultError = nil; + testUpdatedBlock = ^(SDLMenuConfiguration *newConfiguration, NSError *_Nullable error) { + resultMenuConfiguration = newConfiguration; + resultError = error; }; }); // when the layout check fails describe(@"when the layout check fails", ^{ - // when there are no known menu layouts context(@"when there are no known menu layouts", ^{ - beforeEach(^{ - testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[] dynamicUpdateCapabilities:nil]; - }); - it(@"should return an error and finish", ^{ testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock]; [testOp start]; @@ -54,14 +52,14 @@ expect(testOp.error).toNot(beNil()); expect(testConnectionManager.receivedRequests).to(beEmpty()); expect(testOp.isFinished).to(beTrue()); - expect(updatedBlockMenuConfiguration).to(beNil()); + expect(resultMenuConfiguration).to(beNil()); }); }); // when the set main menu layout is not available context(@"when the set main menu layout is not available", ^{ beforeEach(^{ - testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[SDLMenuLayoutTiles] dynamicUpdateCapabilities:nil]; + testWindowCapability.menuLayoutsAvailable = @[SDLMenuLayoutTiles]; }); it(@"should return an error and finish", ^{ @@ -71,14 +69,14 @@ expect(testOp.error).toNot(beNil()); expect(testConnectionManager.receivedRequests).to(beEmpty()); expect(testOp.isFinished).to(beTrue()); - expect(updatedBlockMenuConfiguration).to(beNil()); + expect(resultMenuConfiguration).to(beNil()); }); }); // when the set default submenu layout is not available context(@"when the set default submenu layout is not available", ^{ beforeEach(^{ - testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[SDLMenuLayoutList] dynamicUpdateCapabilities:nil]; + testWindowCapability.menuLayoutsAvailable = @[SDLMenuLayoutList]; }); it(@"should return an error and finish", ^{ @@ -88,7 +86,7 @@ expect(testOp.error).toNot(beNil()); expect(testConnectionManager.receivedRequests).to(beEmpty()); expect(testOp.isFinished).to(beTrue()); - expect(updatedBlockMenuConfiguration).to(beNil()); + expect(resultMenuConfiguration).to(beNil()); }); }); }); @@ -98,7 +96,7 @@ __block SDLSetGlobalPropertiesResponse *response = [[SDLSetGlobalPropertiesResponse alloc] init]; beforeEach(^{ - testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[SDLMenuLayoutList, SDLMenuLayoutTiles] dynamicUpdateCapabilities:nil]; + testWindowCapability.menuLayoutsAvailable = @[SDLMenuLayoutList, SDLMenuLayoutTiles]; testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock]; [testOp start]; @@ -109,7 +107,8 @@ expect(testOp.error).to(beNil()); expect(testConnectionManager.receivedRequests).toNot(beEmpty()); expect(testOp.isFinished).to(beFalse()); - expect(updatedBlockMenuConfiguration).to(beNil()); + expect(resultMenuConfiguration).to(beNil()); + expect(resultError).to(beNil()); SDLSetGlobalProperties *receivedSGP = (SDLSetGlobalProperties *)testConnectionManager.receivedRequests[0]; expect(receivedSGP.menuLayout).to(equal(testMenuConfiguration.mainMenuLayout)); @@ -128,7 +127,7 @@ expect(testOp.error).toNot(beNil()); expect(testConnectionManager.receivedRequests).toNot(beEmpty()); expect(testOp.isFinished).to(beTrue()); - expect(updatedBlockMenuConfiguration).to(beNil()); + expect(resultMenuConfiguration).to(beNil()); }); }); @@ -145,7 +144,7 @@ expect(testOp.error).to(beNil()); expect(testConnectionManager.receivedRequests).toNot(beEmpty()); expect(testOp.isFinished).to(beTrue()); - expect(updatedBlockMenuConfiguration).to(equal(testMenuConfiguration)); + expect(resultMenuConfiguration).to(equal(testMenuConfiguration)); }); }); }); diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index d7533c526..88d1e325c 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -49,8 +49,8 @@ @interface SDLMenuManager() __block SDLMenuCell *submenuCell = nil; beforeEach(^{ - textOnlyCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" icon:nil submenuLayout:nil subCells:@[textOnlyCell]]; + textOnlyCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[textOnlyCell]]; testMenuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutTiles defaultSubmenuLayout:SDLMenuLayoutList]; @@ -156,8 +156,8 @@ @interface SDLMenuManager() // containing duplicate VR commands context(@"containing duplicate VR commands", ^{ - __block SDLMenuCell *textAndVRCell1 = [[SDLMenuCell alloc] initWithTitle:@"Test 1" icon:nil voiceCommands:@[@"Cat", @"Turtle"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - __block SDLMenuCell *textAndVRCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 3" icon:nil voiceCommands:@[@"Cat", @"Dog"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + __block SDLMenuCell *textAndVRCell1 = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"Cat", @"Turtle"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + __block SDLMenuCell *textAndVRCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"Cat", @"Dog"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; it(@"should fail when menu items have duplicate vr commands", ^{ testManager.menuCells = @[textAndVRCell1, textAndVRCell2]; @@ -353,7 +353,7 @@ @interface SDLMenuManager() // on a main menu cell context(@"on a main menu cell", ^{ beforeEach(^{ - cellWithHandler = [[SDLMenuCell alloc] initWithTitle:@"Hello" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { + cellWithHandler = [[SDLMenuCell alloc] initWithTitle:@"Hello" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { cellCalled = YES; testTriggerSource = triggerSource; }]; @@ -377,12 +377,12 @@ @interface SDLMenuManager() // on a submenu menu cell context(@"on a submenu menu cell", ^{ beforeEach(^{ - cellWithHandler = [[SDLMenuCell alloc] initWithTitle:@"Hello" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { + cellWithHandler = [[SDLMenuCell alloc] initWithTitle:@"Hello" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { cellCalled = YES; testTriggerSource = triggerSource; }]; - SDLMenuCell *submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Submenu" icon:nil submenuLayout:SDLMenuLayoutTiles subCells:@[cellWithHandler]]; + SDLMenuCell *submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Submenu" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:SDLMenuLayoutTiles subCells:@[cellWithHandler]]; testManager.menuCells = @[submenuCell]; }); diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 5eea34c80..037f2f9cb 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -51,11 +51,11 @@ testArtwork3 = [[SDLArtwork alloc] initWithData:[@"Test data 3" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO]; testArtwork3.overwrite = YES; - textOnlyCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" icon:testArtwork voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" icon:nil submenuLayout:nil subCells:@[textOnlyCell, textAndImageCell]]; - submenuImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 4" icon:testArtwork2 submenuLayout:SDLMenuLayoutTiles subCells:@[textOnlyCell]]; - textOnlyCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 5" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + textOnlyCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:testArtwork secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[textOnlyCell, textAndImageCell]]; + submenuImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 4" secondaryText:nil tertiaryText:nil icon:testArtwork2 secondaryArtwork:nil submenuLayout:SDLMenuLayoutTiles subCells:@[textOnlyCell]]; + textOnlyCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 5" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; addCommandSuccessResponse = [[SDLAddCommandResponse alloc] init]; addCommandSuccessResponse.success = @YES; @@ -67,7 +67,7 @@ SDLImageField *commandImageField = [[SDLImageField alloc] initWithName:SDLImageFieldNameCommandIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil]; SDLImageField *submenuImageField = [[SDLImageField alloc] initWithName:SDLImageFieldNameSubMenuIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil]; - testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:@[commandImageField, submenuImageField] imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil]; + testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:@[commandImageField, submenuImageField] imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil keyboardCapabilities:nil]; testMenuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutList defaultSubmenuLayout:SDLMenuLayoutList]; testCurrentMenu = @[]; testNewMenu = nil; @@ -114,7 +114,7 @@ OCMStub([testFileManager fileNeedsUpload:[OCMArg any]]).andReturn(NO); }); - fit(@"should properly send the RPCs and finish the operation", ^{ + it(@"should properly send the RPCs and finish the operation", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; [testOp start]; diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m index 7537dec28..f34f720b3 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m @@ -16,16 +16,19 @@ @implementation SDLMenuReplaceUtilitiesSpecHelpers + (NSArray *)topLevelMenuOnly { NSData *cellArtData = [@"testart" dataUsingEncoding:NSUTF8StringEncoding]; NSData *cellArtData2 = [@"testart2" dataUsingEncoding:NSUTF8StringEncoding]; + SDLArtwork *artwork1 = [[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO]; + return @[ - [[SDLMenuCell alloc] initWithTitle:@"Item 1" icon:[[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO] voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], - [[SDLMenuCell alloc] initWithTitle:@"Item 2" icon:[[SDLArtwork alloc] initWithData:cellArtData2 name:@"Test Art 1" fileExtension:@"png" persistent:NO] voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], - [[SDLMenuCell alloc] initWithTitle:@"Item 3" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] + [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], + [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], + [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] ]; } + (NSArray *)deepMenu { NSData *cellArtData = [@"testart" dataUsingEncoding:NSUTF8StringEncoding]; NSData *cellArtData2 = [@"testart2" dataUsingEncoding:NSUTF8StringEncoding]; + SDLArtwork *artwork1 = [[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO]; NSArray *subList1SubList1SubList1 = @[ ]; @@ -43,9 +46,9 @@ @implementation SDLMenuReplaceUtilitiesSpecHelpers ]; return @[ - [[SDLMenuCell alloc] initWithTitle:@"Item 1" icon:[[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO] submenuLayout:nil subCells:subList1], - [[SDLMenuCell alloc] initWithTitle:@"Item 2" icon:[[SDLArtwork alloc] initWithData:cellArtData2 name:@"Test Art 1" fileExtension:@"png" persistent:NO] voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], - [[SDLMenuCell alloc] initWithTitle:@"Item 3" icon:nil submenuLayout:nil subCells:subList2] + [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil submenuLayout:nil subCells:subList1], + [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], + [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:subList2] ]; } diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m index 60a8db2bf..ec8b8ef18 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m @@ -19,9 +19,11 @@ describe(@"the show menu operation", ^{ __block SDLMenuShowOperation *testOp = nil; __block TestConnectionManager *testConnectionManager = nil; + __block NSError *resultError = nil; beforeEach(^{ testConnectionManager = [[TestConnectionManager alloc] init]; + resultError = nil; }); afterEach(^{ @@ -31,7 +33,9 @@ // opening to the main menu context(@"opening to the main menu", ^{ beforeEach(^{ - testOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:testConnectionManager toMenuCell:nil]; + testOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:testConnectionManager toMenuCell:nil completionHandler:^(NSError * _Nullable error) { + resultError = error; + }]; [testOp start]; }); @@ -93,9 +97,11 @@ __block SDLMenuCell *openToCell = nil; __block SDLMenuCell *subcell = nil; beforeEach(^{ - subcell = [[SDLMenuCell alloc] initWithTitle:@"Subcell" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { }]; - openToCell = [[SDLMenuCell alloc] initWithTitle:@"Test submenu" icon:nil submenuLayout:nil subCells:@[subcell]]; - testOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:testConnectionManager toMenuCell:openToCell]; + subcell = [[SDLMenuCell alloc] initWithTitle:@"Subcell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { }]; + openToCell = [[SDLMenuCell alloc] initWithTitle:@"Test submenu" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[subcell]]; + testOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:testConnectionManager toMenuCell:openToCell completionHandler:^(NSError * _Nullable error) { + resultError = error; + }]; [testOp start]; }); diff --git a/SmartDeviceLinkTests/SDLMenuUpdateAlgorithmSpec.m b/SmartDeviceLinkTests/SDLMenuUpdateAlgorithmSpec.m index 63eb97f95..92f3e9abb 100644 --- a/SmartDeviceLinkTests/SDLMenuUpdateAlgorithmSpec.m +++ b/SmartDeviceLinkTests/SDLMenuUpdateAlgorithmSpec.m @@ -22,7 +22,7 @@ typedef NS_ENUM(NSUInteger, MenuCellState) { MenuCellStateKeep }; -describe(@"menuUpdateAlgorithm", ^{ +describe(@"The menu update algorithm", ^{ __block SDLDynamicMenuUpdateRunScore *runScore = nil; __block SDLMenuCell *oldCell1 = nil; @@ -39,7 +39,7 @@ typedef NS_ENUM(NSUInteger, MenuCellState) { __block SDLMenuCell *newCell5 = nil; __block SDLMenuCell *newCell6 = nil; - // 0 = Delete 1 = Add 2 = Keep + // 0 = Delete, 1 = Add, 2 = Keep describe(@"compare old and new menu cells", ^{ beforeEach(^{ oldCell1 = [[SDLMenuCell alloc] initWithTitle:@"Cell 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; @@ -58,11 +58,10 @@ typedef NS_ENUM(NSUInteger, MenuCellState) { }); it(@"should have a new menu status of 22221 and an old menu status of 2222 on best run", ^{ - NSArray *oldMenuCells = @[oldCell1, oldCell2, oldCell3, oldCell4]; NSArray *updatedMenuCells = @[newCell1, newCell2, newCell3, newCell4, newCell5]; - runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; + runScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; expect(runScore.updatedStatus.count).to(equal(5)); expect(runScore.oldStatus.count).to(equal(4)); @@ -85,7 +84,7 @@ typedef NS_ENUM(NSUInteger, MenuCellState) { NSArray *oldMenuCells = @[oldCell1, oldCell2, oldCell3, oldCell4]; NSArray *updatedMenuCells = @[newCell1, newCell2, newCell3]; - runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; + runScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; expect(runScore.updatedStatus.count).to(equal(3)); expect(runScore.oldStatus.count).to(equal(4)); @@ -105,7 +104,7 @@ typedef NS_ENUM(NSUInteger, MenuCellState) { NSArray *oldMenuCells = @[oldCell1, oldCell2, oldCell3]; NSArray *updatedMenuCells = @[newCell4, newCell5, newCell6]; - runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; + runScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; expect(runScore.updatedStatus.count).to(equal(3)); expect(runScore.oldStatus.count).to(equal(3)); @@ -124,7 +123,7 @@ typedef NS_ENUM(NSUInteger, MenuCellState) { NSArray *oldMenuCells = @[oldCell1, oldCell2, oldCell3, oldCell4]; NSArray *updatedMenuCells = @[oldCell2, oldCell1, oldCell4, oldCell3 ]; - runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; + runScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; expect(runScore.updatedStatus.count).to(equal(4)); expect(runScore.oldStatus.count).to(equal(4)); @@ -145,7 +144,7 @@ typedef NS_ENUM(NSUInteger, MenuCellState) { NSArray *oldMenuCells = @[oldCell1, oldCell2, oldCell3, oldCell4]; NSArray *updatedMenuCells = @[]; - runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; + runScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; expect(runScore.updatedStatus.count).to(equal(0)); expect(runScore.oldStatus.count).to(equal(4)); @@ -161,7 +160,7 @@ typedef NS_ENUM(NSUInteger, MenuCellState) { NSArray *oldMenuCells = @[]; NSArray *updatedMenuCells = @[oldCell1, oldCell2, oldCell3, oldCell4]; - runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; + runScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; expect(runScore.updatedStatus.count).to(equal(4)); expect(runScore.oldStatus.count).to(equal(0)); @@ -178,9 +177,9 @@ typedef NS_ENUM(NSUInteger, MenuCellState) { NSArray *oldMenuCells = @[]; NSArray *updatedMenuCells = @[]; - runScore = [SDLDynamicMenuUpdateAlgorithm compareOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; + runScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:oldMenuCells updatedMenuCells:updatedMenuCells]; - expect(runScore).to(beNil()); + expect(runScore.isEmpty).to(beTrue()); }); }); }); From c84b402cd0e4a1c651b1204d3eac7fe0710e9b49 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 5 Aug 2021 12:04:51 -0400 Subject: [PATCH 068/112] More test fixes --- SmartDeviceLink/private/SDLMenuReplaceOperation.m | 2 +- SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 2df9834e4..a47e9f1c1 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -98,7 +98,7 @@ - (void)start { } // If both old and new cells are empty, nothing needs to happen - if ((runScore.oldStatus.count == 0) && (runScore.updatedStatus.count == 0)) { return [self finishOperation]; } + if (runScore.isEmpty) { return [self finishOperation]; } // Drop the cells into buckets based on the run score NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:runScore.oldStatus]; diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 037f2f9cb..28cb0e8b3 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -232,6 +232,7 @@ deleteCommandResponse.success = @YES; deleteCommandResponse.resultCode = SDLResultSuccess; [testConnectionManager respondToLastRequestWithResponse:deleteCommandResponse]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; From 51b020db50e28bb3fd174df088fd97d17c0618b9 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 5 Aug 2021 12:59:28 -0400 Subject: [PATCH 069/112] Current state of tests pass --- SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index 88d1e325c..b1b99bee5 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -193,8 +193,6 @@ @interface SDLMenuManager() it(@"should properly prepare and queue the transaction", ^{ testManager.menuCells = @[textOnlyCell]; - // Assign proper cell id - expect(textOnlyCell.cellId).to(equal(1)); expect(testManager.transactionQueue.operationCount).to(equal(1)); expect(testManager.transactionQueue.operations[0]).to(beAnInstanceOf([SDLMenuReplaceOperation class])); From bc30d8065918e9ae563105bee57d9ecedb8f68b1 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 5 Aug 2021 13:48:25 -0400 Subject: [PATCH 070/112] Fix menu config not calling back in some cases --- .../SDLMenuConfigurationUpdateOperation.m | 5 +--- .../SDLMenuConfigurationUpdateOperationSpec.m | 27 ++++++++++++++++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m index e9bcc76b2..262e55198 100644 --- a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m @@ -76,10 +76,7 @@ - (void)finishOperation { if (self.isCancelled) { self.internalError = [NSError sdl_menuManager_configurationOperationCancelled]; } - - if (self.internalError == nil) { - self.menuConfigurationUpdatedBlock(self.updatedMenuConfiguration, self.internalError); - } + self.menuConfigurationUpdatedBlock(self.updatedMenuConfiguration, self.internalError); [super finishOperation]; } diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m index 78bb19aeb..919d1aeb0 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m @@ -49,10 +49,11 @@ testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock]; [testOp start]; - expect(testOp.error).toNot(beNil()); expect(testConnectionManager.receivedRequests).to(beEmpty()); expect(testOp.isFinished).to(beTrue()); expect(resultMenuConfiguration).to(beNil()); + expect(resultError).toNot(beNil()); + expect(testOp.error).toNot(beNil()); }); }); @@ -66,10 +67,11 @@ testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock]; [testOp start]; - expect(testOp.error).toNot(beNil()); expect(testConnectionManager.receivedRequests).to(beEmpty()); expect(testOp.isFinished).to(beTrue()); expect(resultMenuConfiguration).to(beNil()); + expect(resultError).toNot(beNil()); + expect(testOp.error).toNot(beNil()); }); }); @@ -83,10 +85,11 @@ testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock]; [testOp start]; - expect(testOp.error).toNot(beNil()); expect(testConnectionManager.receivedRequests).to(beEmpty()); expect(testOp.isFinished).to(beTrue()); expect(resultMenuConfiguration).to(beNil()); + expect(resultError).toNot(beNil()); + expect(testOp.error).toNot(beNil()); }); }); }); @@ -145,9 +148,27 @@ expect(testConnectionManager.receivedRequests).toNot(beEmpty()); expect(testOp.isFinished).to(beTrue()); expect(resultMenuConfiguration).to(equal(testMenuConfiguration)); + expect(resultError).to(beNil()); }); }); }); + + describe(@"cancelling the operation", ^{ + testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock]; + + beforeEach(^{ + [testOp cancel]; + [testOp start]; + }); + + it(@"should finish with an error", ^{ + expect(testOp.error).toNot(beNil()); + expect(testConnectionManager.receivedRequests).to(beEmpty()); + expect(testOp.isFinished).to(beTrue()); + expect(resultMenuConfiguration).to(beNil()); + expect(resultError).toNot(beNil()); + }); + }); }); QuickSpecEnd From 4127a5b7a10e4539c203b32a17c91427552e454c Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 5 Aug 2021 14:24:02 -0400 Subject: [PATCH 071/112] Fix menu config operation callback --- SmartDeviceLink/private/SDLError.h | 1 - SmartDeviceLink/private/SDLError.m | 8 -------- .../private/SDLMenuConfigurationUpdateOperation.h | 2 +- .../private/SDLMenuConfigurationUpdateOperation.m | 7 ++++--- .../DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m | 8 ++++---- 5 files changed, 9 insertions(+), 17 deletions(-) diff --git a/SmartDeviceLink/private/SDLError.h b/SmartDeviceLink/private/SDLError.h index 59530245d..34b96247c 100644 --- a/SmartDeviceLink/private/SDLError.h +++ b/SmartDeviceLink/private/SDLError.h @@ -58,7 +58,6 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark Menu Manager -+ (NSError *)sdl_menuManager_configurationOperationCancelled; + (NSError *)sdl_menuManager_configurationOperationLayoutsNotSupported; + (NSError *)sdl_menuManager_configurationOperationFailed:(SDLMenuConfiguration *)failedConfiguration; + (NSError *)sdl_menuManager_openMenuOperationCancelled; diff --git a/SmartDeviceLink/private/SDLError.m b/SmartDeviceLink/private/SDLError.m index 21626d37a..765e16760 100644 --- a/SmartDeviceLink/private/SDLError.m +++ b/SmartDeviceLink/private/SDLError.m @@ -271,14 +271,6 @@ + (NSError *)sdl_subscribeButtonManager_notSubscribed { #pragma mark Menu Manager -+ (NSError *)sdl_menuManager_configurationOperationCancelled { - return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorOperationCancelled userInfo:@{ - NSLocalizedDescriptionKey: @"Menu Manager - Configuration Update Cancelled", - NSLocalizedFailureReasonErrorKey: @"The menu manager was probably stopped or another configuration update was requested.", - NSLocalizedRecoverySuggestionErrorKey: @"This error probably does not need recovery." - }]; -} - + (NSError *)sdl_menuManager_configurationOperationLayoutsNotSupported { return [NSError errorWithDomain:SDLErrorDomainMenuManager code:SDLMenuManagerErrorConfigurationUpdateLayoutNotSupported userInfo:@{ NSLocalizedDescriptionKey: @"Menu Manager - Configuration Update Failed", diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h index ff2318e36..9482ccfe1 100644 --- a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h @@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN -typedef void(^SDLMenuConfigurationUpdatedBlock)(SDLMenuConfiguration *newMenuConfiguration, NSError *_Nullable error); +typedef void(^SDLMenuConfigurationUpdatedBlock)(SDLMenuConfiguration *_Nullable newMenuConfiguration, NSError *_Nullable error); @interface SDLMenuConfigurationUpdateOperation : SDLAsynchronousOperation diff --git a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m index 262e55198..39482643f 100644 --- a/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m +++ b/SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.m @@ -73,10 +73,11 @@ - (void)start { - (void)finishOperation { SDLLogV(@"Finishing menu manager configuration update operation"); - if (self.isCancelled) { - self.internalError = [NSError sdl_menuManager_configurationOperationCancelled]; + if (self.internalError != nil) { + self.menuConfigurationUpdatedBlock(nil, self.internalError); + } else { + self.menuConfigurationUpdatedBlock(self.updatedMenuConfiguration, nil); } - self.menuConfigurationUpdatedBlock(self.updatedMenuConfiguration, self.internalError); [super finishOperation]; } diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m index 919d1aeb0..3678d4ecc 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m @@ -153,7 +153,7 @@ }); }); - describe(@"cancelling the operation", ^{ + describe(@"cancelling the operation before it starts", ^{ testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock]; beforeEach(^{ @@ -161,12 +161,12 @@ [testOp start]; }); - it(@"should finish with an error", ^{ - expect(testOp.error).toNot(beNil()); + it(@"should finish without any callbacks", ^{ + expect(testOp.error).to(beNil()); expect(testConnectionManager.receivedRequests).to(beEmpty()); expect(testOp.isFinished).to(beTrue()); expect(resultMenuConfiguration).to(beNil()); - expect(resultError).toNot(beNil()); + expect(resultError).to(beNil()); }); }); }); From b66bc00305fcb2e52bd6a92d63a07c258addd579 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 5 Aug 2021 14:24:10 -0400 Subject: [PATCH 072/112] Menu show operation test updates --- .../DevAPISpecs/SDLMenuShowOperationSpec.m | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m index ec8b8ef18..d79f2b90f 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m @@ -14,28 +14,33 @@ #import "SDLMenuShowOperation.h" #import "TestConnectionManager.h" +@interface SDLMenuShowOperation () + +@property (strong, nonatomic, nullable) SDLMenuCell *submenuCell; + +@end + QuickSpecBegin(SDLMenuShowOperationSpec) describe(@"the show menu operation", ^{ __block SDLMenuShowOperation *testOp = nil; __block TestConnectionManager *testConnectionManager = nil; __block NSError *resultError = nil; + __block BOOL callbackCalled = NO; beforeEach(^{ testConnectionManager = [[TestConnectionManager alloc] init]; + testOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:testConnectionManager toMenuCell:nil completionHandler:^(NSError * _Nullable error) { + resultError = error; + callbackCalled = YES; + }]; resultError = nil; - }); - - afterEach(^{ - testOp = nil; + callbackCalled = NO; }); // opening to the main menu context(@"opening to the main menu", ^{ beforeEach(^{ - testOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:testConnectionManager toMenuCell:nil completionHandler:^(NSError * _Nullable error) { - resultError = error; - }]; [testOp start]; }); @@ -54,8 +59,9 @@ }); it(@"should set the error and finish", ^{ - expect(testOp.error).toNot((beNil())); expect(testOp.isFinished).to(beTrue()); + expect(resultError).toNot(beNil()); + expect(callbackCalled).to(beTrue()); }); }); @@ -70,8 +76,9 @@ }); it(@"should not set the error and finish", ^{ - expect(testOp.error).to((beNil())); expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(callbackCalled).to(beTrue()); }); }); @@ -86,8 +93,9 @@ }); it(@"should not set the error and finish", ^{ - expect(testOp.error).to((beNil())); expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(callbackCalled).to(beTrue()); }); }); }); @@ -99,16 +107,10 @@ beforeEach(^{ subcell = [[SDLMenuCell alloc] initWithTitle:@"Subcell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { }]; openToCell = [[SDLMenuCell alloc] initWithTitle:@"Test submenu" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[subcell]]; - testOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:testConnectionManager toMenuCell:openToCell completionHandler:^(NSError * _Nullable error) { - resultError = error; - }]; + testOp.submenuCell = openToCell; [testOp start]; }); - it(@"should send the RPC request", ^{ - expect(testConnectionManager.receivedRequests).to(haveCount(1)); - }); - // when the response is not SUCCESS or WARNINGS context(@"when the response is not SUCCESS or WARNINGS", ^{ beforeEach(^{ @@ -120,7 +122,8 @@ }); it(@"should set the error and finish", ^{ - expect(testOp.error).toNot((beNil())); + expect(resultError).toNot(beNil()); + expect(callbackCalled).to(beTrue()); expect(testOp.isFinished).to(beTrue()); }); }); @@ -136,7 +139,8 @@ }); it(@"should not set the error and finish", ^{ - expect(testOp.error).to((beNil())); + expect(resultError).to(beNil()); + expect(callbackCalled).to(beTrue()); expect(testOp.isFinished).to(beTrue()); }); }); @@ -152,7 +156,8 @@ }); it(@"should not set the error and finish", ^{ - expect(testOp.error).to((beNil())); + expect(resultError).to(beNil()); + expect(callbackCalled).to(beTrue()); expect(testOp.isFinished).to(beTrue()); }); }); From ad941f2476c0f0e752df138e5b0b672d1644b4ad Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 6 Aug 2021 09:17:36 -0400 Subject: [PATCH 073/112] Fix #1923 Empty string bug in menu cell texts --- SmartDeviceLink/private/SDLMenuReplaceUtilities.m | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 2e0aa7ace..0dcba464a 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -158,8 +158,8 @@ + (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFi SDLMenuParams *params = [[SDLMenuParams alloc] init]; params.menuName = cell.uniqueTitle; - params.secondaryText = [windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandSecondaryText] ? cell.secondaryText : nil; - params.tertiaryText = [windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandTertiaryText] ? cell.tertiaryText : nil; + params.secondaryText = ([windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandSecondaryText] && cell.secondaryText.length > 0) ? cell.secondaryText : nil; + params.tertiaryText = ([windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandTertiaryText] && cell.tertiaryText.length > 0) ? cell.tertiaryText : nil; params.parentID = (cell.parentCellId != ParentIdNotFound) ? @(cell.parentCellId) : nil; params.position = @(position); @@ -173,6 +173,8 @@ + (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFi } + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager position:(UInt16)position windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { + NSString *secondaryText = ([windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandSecondaryText] && cell.secondaryText.length > 0) ? cell.secondaryText : nil; + NSString *tertiaryText = ([windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandTertiaryText] && cell.tertiaryText.length > 0) ? cell.tertiaryText : nil; SDLImage *icon = [self sdl_shouldCellIncludeImageFromCell:cell fileManager:fileManager windowCapability:windowCapability] ? cell.icon.imageRPC : nil; SDLImage *secondaryIcon = [self sdl_shouldCellIncludeSecondaryImageFromCell:cell fileManager:fileManager windowCapability:windowCapability] ? cell.secondaryArtwork.imageRPC : nil; @@ -183,7 +185,7 @@ + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager submenuLayout = defaultSubmenuLayout; } - return [[SDLAddSubMenu alloc] initWithMenuID:cell.cellId menuName:cell.uniqueTitle position:@(position) menuIcon:icon menuLayout:submenuLayout parentID:nil secondaryText:cell.secondaryText tertiaryText:cell.tertiaryText secondaryImage:secondaryIcon]; + return [[SDLAddSubMenu alloc] initWithMenuID:cell.cellId menuName:cell.uniqueTitle position:@(position) menuIcon:icon menuLayout:submenuLayout parentID:nil secondaryText:secondaryText tertiaryText:tertiaryText secondaryImage:secondaryIcon]; } #pragma mark - Updating Menu Cells From 7031f683fe9c90c322d3aaf91919963daefaafba Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 6 Aug 2021 11:05:55 -0400 Subject: [PATCH 074/112] Menu submenu cell conditions update * A menu cell is now a subcell when `.subcells != nil` instead of `.subcells.count > 0` --- SmartDeviceLink/private/SDLMenuReplaceUtilities.m | 14 +++++++------- SmartDeviceLink/public/SDLMenuCell.m | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 0dcba464a..123b050a7 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -54,13 +54,13 @@ @implementation SDLMenuReplaceUtilities /// If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image + (BOOL)sdl_shouldCellIncludeImageFromCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { - BOOL supportsImage = (cell.subCells.count > 0) ? [windowCapability hasImageFieldOfName:SDLImageFieldNameSubMenuIcon] : [windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]; + BOOL supportsImage = (cell.subCells != nil) ? [windowCapability hasImageFieldOfName:SDLImageFieldNameSubMenuIcon] : [windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]; return (cell.icon != nil) && supportsImage && ([fileManager hasUploadedFile:cell.icon] || cell.icon.isStaticIcon); } /// If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image + (BOOL)sdl_shouldCellIncludeSecondaryImageFromCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { - BOOL supportsImage = (cell.subCells.count > 0) ? [windowCapability hasImageFieldOfName:SDLImageFieldNameMenuSubMenuSecondaryImage] : [windowCapability hasImageFieldOfName:SDLImageFieldNameMenuCommandSecondaryImage]; + BOOL supportsImage = (cell.subCells != nil) ? [windowCapability hasImageFieldOfName:SDLImageFieldNameMenuSubMenuSecondaryImage] : [windowCapability hasImageFieldOfName:SDLImageFieldNameMenuCommandSecondaryImage]; return (cell.secondaryArtwork != nil) && supportsImage && ([fileManager hasUploadedFile:cell.secondaryArtwork] || cell.secondaryArtwork.isStaticIcon); } @@ -95,7 +95,7 @@ + (UInt16)positionForRPCRequest:(SDLRPCRequest *)request { + (NSArray *)deleteCommandsForCells:(NSArray *)cells { NSMutableArray *mutableDeletes = [NSMutableArray array]; for (SDLMenuCell *cell in cells) { - if (cell.subCells.count == 0) { + if (cell.subCells != nil) { SDLDeleteCommand *delete = [[SDLDeleteCommand alloc] initWithId:cell.cellId]; [mutableDeletes addObject:delete]; } else { @@ -112,7 +112,7 @@ + (UInt16)positionForRPCRequest:(SDLRPCRequest *)request { for (NSUInteger menuInteger = 0; menuInteger < menu.count; menuInteger++) { for (NSUInteger updateCellsIndex = 0; updateCellsIndex < cells.count; updateCellsIndex++) { if ([menu[menuInteger] isEqual:cells[updateCellsIndex]]) { - if (cells[updateCellsIndex].subCells.count > 0) { + if (cells[updateCellsIndex].subCells == nil) { [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[updateCellsIndex] fileManager:fileManager position:(UInt16)menuInteger windowCapability:windowCapability defaultSubmenuLayout:defaultSubmenuLayout]]; } else { [mutableCommands addObject:[self sdl_commandForMenuCell:cells[updateCellsIndex] fileManager:fileManager windowCapability:windowCapability position:(UInt16)menuInteger]]; @@ -128,7 +128,7 @@ + (UInt16)positionForRPCRequest:(SDLRPCRequest *)request { + (NSArray *)subMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { NSMutableArray *mutableCommands = [NSMutableArray array]; for (SDLMenuCell *cell in cells) { - if (cell.subCells.count > 0) { + if (cell.subCells != nil) { [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cell.subCells fileManager:fileManager windowCapability:windowCapability defaultSubmenuLayout:defaultSubmenuLayout]]; } } @@ -142,7 +142,7 @@ + (UInt16)positionForRPCRequest:(SDLRPCRequest *)request { NSMutableArray *mutableCommands = [NSMutableArray array]; for (NSUInteger cellIndex = 0; cellIndex < cells.count; cellIndex++) { - if (cells[cellIndex].subCells.count > 0) { + if (cells[cellIndex].subCells != nil) { [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[cellIndex] fileManager:fileManager position:(UInt16)cellIndex windowCapability:windowCapability defaultSubmenuLayout:defaultSubmenuLayout]]; [mutableCommands addObjectsFromArray:[self sdl_allCommandsForCells:cells[cellIndex].subCells fileManager:fileManager windowCapability:windowCapability defaultSubmenuLayout:defaultSubmenuLayout]]; } else { @@ -270,7 +270,7 @@ + (BOOL)sdl_addMenuCell:(SDLMenuCell *)cell toList:(NSMutableArray *)cellList atPosition:(UInt16)position { SDLMenuCell *cellToInsert = cell; - if (cellToInsert.subCells.count > 0) { + if (cellToInsert.subCells != nil) { cellToInsert = [cell copy]; cellToInsert.subCells = @[]; } diff --git a/SmartDeviceLink/public/SDLMenuCell.m b/SmartDeviceLink/public/SDLMenuCell.m index 737a6049c..a66812032 100644 --- a/SmartDeviceLink/public/SDLMenuCell.m +++ b/SmartDeviceLink/public/SDLMenuCell.m @@ -97,7 +97,7 @@ - (NSUInteger)hash { return NSUIntRotateCell(self.title.hash, NSUIntBitCell / 2) ^ NSUIntRotateCell(self.icon.name.hash, NSUIntBitCell / 3) ^ NSUIntRotateCell(self.voiceCommands.dynamicHash, NSUIntBitCell / 4) - ^ NSUIntRotateCell((self.subCells.count != 0), NSUIntBitCell / 5) + ^ NSUIntRotateCell((self.subCells != nil), NSUIntBitCell / 5) ^ NSUIntRotateCell(self.secondaryText.hash, NSUIntBitCell / 6) ^ NSUIntRotateCell(self.tertiaryText.hash, NSUIntBitCell / 7) ^ NSUIntRotateCell(self.secondaryArtwork.name.hash, NSUIntBitCell / 8) From f25932a4ec67cf0c7b0b86164a44dca9f74736cc Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 6 Aug 2021 11:14:54 -0400 Subject: [PATCH 075/112] Add test for cell equality with subcells empty vs nil --- SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m index f6d982a57..4958e7674 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m @@ -108,6 +108,13 @@ expect([testCell isEqual:testCell2]).to(beFalse()); }); + it(@"should compare cells and return false if one cell has subcells empty and another has subcells nil", ^{ + testCell = [[SDLMenuCell alloc] initWithTitle:someTitle secondaryText:someSecondaryTitle tertiaryText:someTertiaryTitle icon:nil secondaryArtwork:someSecondaryArtwork submenuLayout:testLayout subCells:nil]; + testCell2 = [[SDLMenuCell alloc] initWithTitle:someTitle secondaryText:someSecondaryTitle tertiaryText:someTertiaryTitle icon:nil secondaryArtwork:someSecondaryArtwork submenuLayout:testLayout subCells:@[]]; + + expect([testCell isEqual:testCell2]).to(beFalse()); + }); + it(@"should compare cells and return true if cells equal", ^{ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" From 6ea3b3614c8273ff5597edcedfb0e63013aea61d Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 6 Aug 2021 11:54:22 -0400 Subject: [PATCH 076/112] Add more to menu replace utilities spec --- .../SDLMenuReplaceUtilitiesSpecHelpers.m | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m index f34f720b3..51b23c3f8 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m @@ -17,11 +17,12 @@ @implementation SDLMenuReplaceUtilitiesSpecHelpers NSData *cellArtData = [@"testart" dataUsingEncoding:NSUTF8StringEncoding]; NSData *cellArtData2 = [@"testart2" dataUsingEncoding:NSUTF8StringEncoding]; SDLArtwork *artwork1 = [[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO]; + SDLArtwork *artwork2 = [[SDLArtwork alloc] initWithData:cellArtData2 name:@"Test Art 2" fileExtension:@"png" persistent:NO]; return @[ [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], - [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] + [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:artwork2 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] ]; } @@ -29,20 +30,21 @@ @implementation SDLMenuReplaceUtilitiesSpecHelpers NSData *cellArtData = [@"testart" dataUsingEncoding:NSUTF8StringEncoding]; NSData *cellArtData2 = [@"testart2" dataUsingEncoding:NSUTF8StringEncoding]; SDLArtwork *artwork1 = [[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO]; - - NSArray *subList1SubList1SubList1 = @[ - ]; - - NSArray *subList1SubList1SubList2 = @[ - ]; + SDLArtwork *artwork2 = [[SDLArtwork alloc] initWithData:cellArtData2 name:@"Test Art 2" fileExtension:@"png" persistent:NO]; NSArray *subList1SubList1 = @[ + [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 1" icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], + [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 2" icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] ]; NSArray *subList1 = @[ + [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork1 secondaryArtwork:nil submenuLayout:nil subCells:subList1SubList1], + [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] ]; NSArray *subList2 = @[ + [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], + [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork2 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] ]; return @[ From b5bc5537cb86b9313b8ea035372a57c73147f246 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 6 Aug 2021 11:54:36 -0400 Subject: [PATCH 077/112] Fix creating wrong RPCs for cell --- SmartDeviceLink/private/SDLMenuReplaceUtilities.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 123b050a7..1003cc7e2 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -96,10 +96,10 @@ + (UInt16)positionForRPCRequest:(SDLRPCRequest *)request { NSMutableArray *mutableDeletes = [NSMutableArray array]; for (SDLMenuCell *cell in cells) { if (cell.subCells != nil) { - SDLDeleteCommand *delete = [[SDLDeleteCommand alloc] initWithId:cell.cellId]; + SDLDeleteSubMenu *delete = [[SDLDeleteSubMenu alloc] initWithId:cell.cellId]; [mutableDeletes addObject:delete]; } else { - SDLDeleteSubMenu *delete = [[SDLDeleteSubMenu alloc] initWithId:cell.cellId]; + SDLDeleteCommand *delete = [[SDLDeleteCommand alloc] initWithId:cell.cellId]; [mutableDeletes addObject:delete]; } } @@ -112,7 +112,7 @@ + (UInt16)positionForRPCRequest:(SDLRPCRequest *)request { for (NSUInteger menuInteger = 0; menuInteger < menu.count; menuInteger++) { for (NSUInteger updateCellsIndex = 0; updateCellsIndex < cells.count; updateCellsIndex++) { if ([menu[menuInteger] isEqual:cells[updateCellsIndex]]) { - if (cells[updateCellsIndex].subCells == nil) { + if (cells[updateCellsIndex].subCells != nil) { [mutableCommands addObject:[self sdl_subMenuCommandForMenuCell:cells[updateCellsIndex] fileManager:fileManager position:(UInt16)menuInteger windowCapability:windowCapability defaultSubmenuLayout:defaultSubmenuLayout]]; } else { [mutableCommands addObject:[self sdl_commandForMenuCell:cells[updateCellsIndex] fileManager:fileManager windowCapability:windowCapability position:(UInt16)menuInteger]]; From e37c26fcfaab20aee2abb7a753244fd4a5f92487 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 9 Aug 2021 08:53:32 -0400 Subject: [PATCH 078/112] Stubbing out additional tests --- .../DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m index 5b12083b0..3fd3004de 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m @@ -24,29 +24,73 @@ @interface SDLMenuCell() __block NSArray *testMenuCells = nil; __block SDLFileManager *mockFileManager = nil; __block SDLWindowCapability *testWindowCapability = nil; + __block NSArray *allSupportedTextFields = nil; + __block NSArray *allSupportedImageFields = nil; beforeEach(^{ mockFileManager = OCMClassMock([SDLFileManager class]); }); + + context(@"when all the files are uploaded", ^{ + beforeEach(^{ + OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]); + }); + + context(@"when the window capability doesn't support the primary image", ^{ + beforeEach(^{ + + }); + }); + + context(@"when the window capability supports primary but not secondary image", ^{ + + }); + + context(@"when the window capability supports both images", ^{ + + }); + }); + + context(@"when no files are uploaded", ^{ + + }); +}); + +describe(@"retrieving a commandId", ^{ + +}); + +describe(@"retrieving a position", ^{ + }); describe(@"generating RPCs", ^{ + context(@"delete commands", ^{ + }); + + context(@"main menu commands", ^{ + + }); + + context(@"sub menu commands", ^{ + + }); }); // updating menu cells -describe(@"updating menu cells", ^{ +describe(@"updating menu cell lists", ^{ __block NSArray *testNewMenuCells = nil; __block UInt32 testCommandId = 0; // removeMenuCellFromList:withCmdId: - describe(@"removeMenuCellFromList:withCmdId:", ^{ + describe(@"removing commands from a list", ^{ context(@"the list only has one level", ^{ }); }); - describe(@"addMenuRequestWithCommandId:position:fromNewMenuList:toMainMenuList:", ^{ + describe(@"add commands to the list", ^{ __block NSMutableArray *testMenuCells = nil; __block UInt16 testPosition = 0; }); From f7f5aad1a8ca5a285b1240b9dbc79ed1ce543dab Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 9 Aug 2021 15:35:34 -0400 Subject: [PATCH 079/112] In progress test updates --- .../private/SDLMenuReplaceUtilities.m | 9 +- .../DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m | 198 +++++++++++++++++- .../SDLMenuReplaceUtilitiesSpecHelpers.h | 4 +- .../SDLMenuReplaceUtilitiesSpecHelpers.m | 2 +- 4 files changed, 196 insertions(+), 17 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 1003cc7e2..08f855ebb 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -65,6 +65,7 @@ + (BOOL)sdl_shouldCellIncludeSecondaryImageFromCell:(SDLMenuCell *)cell fileMana } #pragma mark - RPC Commands +#pragma mark Retrieving Values + (UInt32)commandIdForRPCRequest:(SDLRPCRequest *)request { UInt32 commandId = 0; @@ -92,6 +93,8 @@ + (UInt16)positionForRPCRequest:(SDLRPCRequest *)request { return position; } +#pragma mark Generating RPCs + + (NSArray *)deleteCommandsForCells:(NSArray *)cells { NSMutableArray *mutableDeletes = [NSMutableArray array]; for (SDLMenuCell *cell in cells) { @@ -188,9 +191,7 @@ + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager return [[SDLAddSubMenu alloc] initWithMenuID:cell.cellId menuName:cell.uniqueTitle position:@(position) menuIcon:icon menuLayout:submenuLayout parentID:nil secondaryText:secondaryText tertiaryText:tertiaryText secondaryImage:secondaryIcon]; } -#pragma mark - Updating Menu Cells - -#pragma mark Remove Cell +#pragma mark - Updating Menu Cells In Lists + (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId { for (SDLMenuCell *menuCell in menuCellList) { @@ -212,8 +213,6 @@ + (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList wit return NO; } -#pragma mark Inserting Cell - + (BOOL)addMenuRequestWithCommandId:(UInt32)commandId position:(UInt16)position fromNewMenuList:(NSArray *)newMenuList toMainMenuList:(NSMutableArray *)mainMenuList { SDLMenuCell *addedCell = nil; for (SDLMenuCell *cell in newMenuList) { diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m index 3fd3004de..4b72308ec 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m @@ -20,61 +20,241 @@ @interface SDLMenuCell() QuickSpecBegin(SDLMenuReplaceUtilitiesSpec) -describe(@"finding all artworks from cells", ^{ - __block NSArray *testMenuCells = nil; - __block SDLFileManager *mockFileManager = nil; - __block SDLWindowCapability *testWindowCapability = nil; - __block NSArray *allSupportedTextFields = nil; - __block NSArray *allSupportedImageFields = nil; +__block NSArray *testMenuCells = nil; +__block SDLFileManager *mockFileManager = nil; +__block SDLWindowCapability *testWindowCapability = nil; +__block NSArray *allSupportedTextFields = @[ + [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuCommandSecondaryText characterSet:SDLCharacterSetUtf8 width:100 rows:1], + [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuCommandTertiaryText characterSet:SDLCharacterSetUtf8 width:100 rows:1], + [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuSubMenuSecondaryText characterSet:SDLCharacterSetUtf8 width:100 rows:1], + [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuSubMenuTertiaryText characterSet:SDLCharacterSetUtf8 width:100 rows:1] +]; +__block NSArray *allSupportedImageFields = @[ + [[SDLImageField alloc] initWithName:SDLImageFieldNameCommandIcon imageTypeSupported:@[SDLImageTypeDynamic] imageResolution:nil], + [[SDLImageField alloc] initWithName:SDLImageFieldNameMenuCommandSecondaryImage imageTypeSupported:@[SDLImageTypeDynamic] imageResolution:nil], + [[SDLImageField alloc] initWithName:SDLImageFieldNameSubMenuIcon imageTypeSupported:@[SDLImageTypeDynamic] imageResolution:nil], + [[SDLImageField alloc] initWithName:SDLImageFieldNameMenuSubMenuSecondaryImage imageTypeSupported:@[SDLImageTypeDynamic] imageResolution:nil] +]; +describe(@"finding all artworks from cells", ^{ beforeEach(^{ mockFileManager = OCMClassMock([SDLFileManager class]); + testWindowCapability = [[SDLWindowCapability alloc] init]; }); - context(@"when all the files are uploaded", ^{ + context(@"when all the files need to be uploaded", ^{ beforeEach(^{ - OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]); + OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(NO); }); context(@"when the window capability doesn't support the primary image", ^{ beforeEach(^{ + testWindowCapability.textFields = allSupportedTextFields; + }); + it(@"should return an empty list of artworks to upload", ^{ + NSArray *artworksToUpload = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu fileManager:mockFileManager windowCapability:testWindowCapability]; + + expect(artworksToUpload).to(beEmpty()); }); }); context(@"when the window capability supports primary but not secondary image", ^{ + beforeEach(^{ + testWindowCapability.textFields = allSupportedTextFields; + testWindowCapability.imageFields = @[allSupportedImageFields[0], allSupportedImageFields[2]]; + }); + + it(@"should only return primary images to upload", ^{ + NSArray *artworksToUpload = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu fileManager:mockFileManager windowCapability:testWindowCapability]; + expect(artworksToUpload).to(haveCount(1)); + }); }); context(@"when the window capability supports both images", ^{ + beforeEach(^{ + testWindowCapability.textFields = allSupportedTextFields; + testWindowCapability.imageFields = allSupportedImageFields; + }); + + context(@"with a shallow menu", ^{ + it(@"should only return all images to upload", ^{ + NSArray *artworksToUpload = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu fileManager:mockFileManager windowCapability:testWindowCapability]; + + expect(artworksToUpload).to(haveCount(2)); + }); + }); + + context(@"with a deep menu", ^{ + it(@"should only return all images to upload", ^{ + NSArray *artworksToUpload = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:SDLMenuReplaceUtilitiesSpecHelpers.deepMenu fileManager:mockFileManager windowCapability:testWindowCapability]; + expect(artworksToUpload).to(haveCount(2)); + }); + }); }); }); - context(@"when no files are uploaded", ^{ + context(@"when no files need to be uploaded", ^{ + beforeEach(^{ + OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(YES); + }); + + context(@"when the window capability supports both images", ^{ + beforeEach(^{ + testWindowCapability.textFields = allSupportedTextFields; + testWindowCapability.imageFields = allSupportedImageFields; + }); + it(@"should not return any images to upload", ^{ + NSArray *artworksToUpload = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu fileManager:mockFileManager windowCapability:testWindowCapability]; + + expect(artworksToUpload).to(beEmpty()); + }); + }); }); }); describe(@"retrieving a commandId", ^{ + context(@"with an AddCommand", ^{ + it(@"should return the command id", ^{ + SDLAddCommand *rpc = [[SDLAddCommand alloc] init]; + rpc.cmdID = @12345; + expect([SDLMenuReplaceUtilities commandIdForRPCRequest:rpc]).to(equal(12345)); + }); + }); + + context(@"with an AddSubMenu", ^{ + it(@"should return the command id", ^{ + SDLAddSubMenu *rpc = [[SDLAddSubMenu alloc] init]; + rpc.menuID = @12345; + expect([SDLMenuReplaceUtilities commandIdForRPCRequest:rpc]).to(equal(12345)); + }); + }); + + context(@"with a DeleteCommand", ^{ + it(@"should return the command id", ^{ + SDLDeleteCommand *rpc = [[SDLDeleteCommand alloc] init]; + rpc.cmdID = @12345; + expect([SDLMenuReplaceUtilities commandIdForRPCRequest:rpc]).to(equal(12345)); + }); + }); + context(@"with a DeleteSubMenu", ^{ + it(@"should return the command id", ^{ + SDLDeleteSubMenu *rpc = [[SDLDeleteSubMenu alloc] init]; + rpc.menuID = @12345; + expect([SDLMenuReplaceUtilities commandIdForRPCRequest:rpc]).to(equal(12345)); + }); + }); + + context(@"with an Alert", ^{ + it(@"should return 0", ^{ + SDLAlert *rpc = [[SDLAlert alloc] init]; + expect([SDLMenuReplaceUtilities commandIdForRPCRequest:rpc]).to(equal(0)); + }); + }); }); describe(@"retrieving a position", ^{ + context(@"with an AddCommand", ^{ + it(@"should return the position", ^{ + SDLAddCommand *rpc = [[SDLAddCommand alloc] init]; + rpc.menuParams.position = @12345; + expect(@([SDLMenuReplaceUtilities positionForRPCRequest:rpc])).to(equal(@12345)); + }); + }); + context(@"with an AddSubMenu", ^{ + it(@"should return the command id", ^{ + SDLAddSubMenu *rpc = [[SDLAddSubMenu alloc] init]; + rpc.position = @12345; + expect(@([SDLMenuReplaceUtilities positionForRPCRequest:rpc])).to(equal(@12345)); + }); + }); }); describe(@"generating RPCs", ^{ + __block SDLMenuLayout testMenuLayout = SDLMenuLayoutList; + + beforeEach(^{ + mockFileManager = OCMClassMock([SDLFileManager class]); + testWindowCapability = [[SDLWindowCapability alloc] init]; + }); + context(@"delete commands", ^{ + context(@"shallow menu", ^{ + beforeEach(^{ + testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu; + }); + + it(@"should generate the correct RPCs", ^{ + NSArray *requests = [SDLMenuReplaceUtilities deleteCommandsForCells:testMenuCells]; + expect(requests).to(haveCount(3)); + expect(requests[0]).to(beAnInstanceOf(SDLDeleteCommand.class)); + expect(requests[1]).to(beAnInstanceOf(SDLDeleteCommand.class)); + expect(requests[2]).to(beAnInstanceOf(SDLDeleteCommand.class)); + }); + }); + + context(@"deep menu", ^{ + beforeEach(^{ + testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu; + }); + it(@"should generate the correct RPCs", ^{ + NSArray *requests = [SDLMenuReplaceUtilities deleteCommandsForCells:testMenuCells]; + expect(requests).to(haveCount(6)); + expect(requests[0]).to(beAnInstanceOf(SDLDeleteSubMenu.class)); + expect(requests[1]).to(beAnInstanceOf(SDLDeleteCommand.class)); + expect(requests[2]).to(beAnInstanceOf(SDLDeleteSubMenu.class)); + }); + }); }); context(@"main menu commands", ^{ + context(@"shallow menu", ^{ + beforeEach(^{ + testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu; + }); + + it(@"should generate the correct RPCs", ^{ + NSArray *requests = [SDLMenuReplaceUtilities mainMenuCommandsForCells:testMenuCells fileManager:mockFileManager usingIndexesFrom:@[] windowCapability:testWindowCapability defaultSubmenuLayout:testMenuLayout]; + expect(requests).to(haveCount(3)); + expect(requests[0]).to(beAnInstanceOf(SDLAddCommand.class)); + expect(requests[1]).to(beAnInstanceOf(SDLAddCommand.class)); + expect(requests[2]).to(beAnInstanceOf(SDLAddCommand.class)); + }); + }); + + context(@"deep menu", ^{ + beforeEach(^{ + testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu; + }); + it(@"should generate the correct RPCs", ^{ + NSArray *requests = [SDLMenuReplaceUtilities mainMenuCommandsForCells:testMenuCells fileManager:mockFileManager usingIndexesFrom:@[] windowCapability:testWindowCapability defaultSubmenuLayout:testMenuLayout]; + expect(requests).to(haveCount(3)); + expect(requests[0]).to(beAnInstanceOf(SDLAddSubMenu.class)); + expect(requests[1]).to(beAnInstanceOf(SDLAddCommand.class)); + expect(requests[2]).to(beAnInstanceOf(SDLAddSubMenu.class)); + }); + }); }); context(@"sub menu commands", ^{ + context(@"shallow menu", ^{ + beforeEach(^{ + testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu; + }); + }); + context(@"deep menu", ^{ + beforeEach(^{ + testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu; + }); + }); }); }); diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h index ebe68f865..908e6b59d 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h @@ -14,8 +14,8 @@ NS_ASSUME_NONNULL_BEGIN @interface SDLMenuReplaceUtilitiesSpecHelpers : NSObject -@property (nonatomic, readonly) NSArray *topLevelOnlyMenu; -@property (nonatomic, readonly) NSArray *deepMenu; +@property (class, nonatomic, readonly) NSArray *topLevelOnlyMenu; +@property (class, nonatomic, readonly) NSArray *deepMenu; @end diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m index 51b23c3f8..6d6122654 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m @@ -13,7 +13,7 @@ @implementation SDLMenuReplaceUtilitiesSpecHelpers -+ (NSArray *)topLevelMenuOnly { ++ (NSArray *)topLevelOnlyMenu { NSData *cellArtData = [@"testart" dataUsingEncoding:NSUTF8StringEncoding]; NSData *cellArtData2 = [@"testart2" dataUsingEncoding:NSUTF8StringEncoding]; SDLArtwork *artwork1 = [[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO]; From bff7993e5195749c2164dbda8c20620511d0ee24 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 9 Aug 2021 15:51:54 -0400 Subject: [PATCH 080/112] Test fixes --- .../DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m index 4b72308ec..11f475611 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m @@ -161,16 +161,17 @@ @interface SDLMenuCell() context(@"with an AddCommand", ^{ it(@"should return the position", ^{ SDLAddCommand *rpc = [[SDLAddCommand alloc] init]; - rpc.menuParams.position = @12345; - expect(@([SDLMenuReplaceUtilities positionForRPCRequest:rpc])).to(equal(@12345)); + rpc.menuParams = [[SDLMenuParams alloc] init]; + rpc.menuParams.position = @123; + expect(@([SDLMenuReplaceUtilities positionForRPCRequest:rpc])).to(equal(@123)); }); }); context(@"with an AddSubMenu", ^{ it(@"should return the command id", ^{ SDLAddSubMenu *rpc = [[SDLAddSubMenu alloc] init]; - rpc.position = @12345; - expect(@([SDLMenuReplaceUtilities positionForRPCRequest:rpc])).to(equal(@12345)); + rpc.position = @123; + expect(@([SDLMenuReplaceUtilities positionForRPCRequest:rpc])).to(equal(@123)); }); }); }); @@ -220,7 +221,7 @@ @interface SDLMenuCell() }); it(@"should generate the correct RPCs", ^{ - NSArray *requests = [SDLMenuReplaceUtilities mainMenuCommandsForCells:testMenuCells fileManager:mockFileManager usingIndexesFrom:@[] windowCapability:testWindowCapability defaultSubmenuLayout:testMenuLayout]; + NSArray *requests = [SDLMenuReplaceUtilities mainMenuCommandsForCells:testMenuCells fileManager:mockFileManager usingIndexesFrom:testMenuCells windowCapability:testWindowCapability defaultSubmenuLayout:testMenuLayout]; expect(requests).to(haveCount(3)); expect(requests[0]).to(beAnInstanceOf(SDLAddCommand.class)); expect(requests[1]).to(beAnInstanceOf(SDLAddCommand.class)); @@ -234,7 +235,7 @@ @interface SDLMenuCell() }); it(@"should generate the correct RPCs", ^{ - NSArray *requests = [SDLMenuReplaceUtilities mainMenuCommandsForCells:testMenuCells fileManager:mockFileManager usingIndexesFrom:@[] windowCapability:testWindowCapability defaultSubmenuLayout:testMenuLayout]; + NSArray *requests = [SDLMenuReplaceUtilities mainMenuCommandsForCells:testMenuCells fileManager:mockFileManager usingIndexesFrom:testMenuCells windowCapability:testWindowCapability defaultSubmenuLayout:testMenuLayout]; expect(requests).to(haveCount(3)); expect(requests[0]).to(beAnInstanceOf(SDLAddSubMenu.class)); expect(requests[1]).to(beAnInstanceOf(SDLAddCommand.class)); From b0c4e8abdedaa2fa499934b21264afbbc4340e06 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 10 Aug 2021 09:04:58 -0400 Subject: [PATCH 081/112] Fix not finding secondary artworks to upload --- SmartDeviceLink/private/SDLMenuReplaceUtilities.m | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 08f855ebb..5b899eb70 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -44,6 +44,10 @@ @implementation SDLMenuReplaceUtilities [mutableArtworks addObject:cell.icon]; } + if ((cell.secondaryArtwork != nil) && [fileManager fileNeedsUpload:cell.secondaryArtwork]) { + [mutableArtworks addObject:cell.secondaryArtwork]; + } + if (cell.subCells.count > 0) { [mutableArtworks addObjectsFromArray:[self findAllArtworksToBeUploadedFromCells:cell.subCells fileManager:fileManager windowCapability:windowCapability]]; } @@ -53,7 +57,7 @@ @implementation SDLMenuReplaceUtilities } /// If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image -+ (BOOL)sdl_shouldCellIncludeImageFromCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { ++ (BOOL)sdl_shouldCellIncludePrimaryImageFromCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { BOOL supportsImage = (cell.subCells != nil) ? [windowCapability hasImageFieldOfName:SDLImageFieldNameSubMenuIcon] : [windowCapability hasImageFieldOfName:SDLImageFieldNameCommandIcon]; return (cell.icon != nil) && supportsImage && ([fileManager hasUploadedFile:cell.icon] || cell.icon.isStaticIcon); } @@ -168,7 +172,7 @@ + (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFi command.menuParams = params; command.vrCommands = (cell.voiceCommands.count == 0) ? nil : cell.voiceCommands; - command.cmdIcon = [self sdl_shouldCellIncludeImageFromCell:cell fileManager:fileManager windowCapability:windowCapability] ? cell.icon.imageRPC : nil; + command.cmdIcon = [self sdl_shouldCellIncludePrimaryImageFromCell:cell fileManager:fileManager windowCapability:windowCapability] ? cell.icon.imageRPC : nil; command.secondaryImage = [self sdl_shouldCellIncludeSecondaryImageFromCell:cell fileManager:fileManager windowCapability:windowCapability] ? cell.secondaryArtwork.imageRPC : nil; command.cmdID = @(cell.cellId); @@ -178,7 +182,7 @@ + (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFi + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager:(SDLFileManager *)fileManager position:(UInt16)position windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { NSString *secondaryText = ([windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandSecondaryText] && cell.secondaryText.length > 0) ? cell.secondaryText : nil; NSString *tertiaryText = ([windowCapability hasTextFieldOfName:SDLTextFieldNameMenuCommandTertiaryText] && cell.tertiaryText.length > 0) ? cell.tertiaryText : nil; - SDLImage *icon = [self sdl_shouldCellIncludeImageFromCell:cell fileManager:fileManager windowCapability:windowCapability] ? cell.icon.imageRPC : nil; + SDLImage *icon = [self sdl_shouldCellIncludePrimaryImageFromCell:cell fileManager:fileManager windowCapability:windowCapability] ? cell.icon.imageRPC : nil; SDLImage *secondaryIcon = [self sdl_shouldCellIncludeSecondaryImageFromCell:cell fileManager:fileManager windowCapability:windowCapability] ? cell.secondaryArtwork.imageRPC : nil; SDLMenuLayout submenuLayout = nil; From 63e70a946890fb119ee02ef62606fd40730c6f5c Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 10 Aug 2021 09:05:04 -0400 Subject: [PATCH 082/112] Fix tests --- .../DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m | 10 +++++----- .../SDLMenuReplaceUtilitiesSpecHelpers.m | 14 +++++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m index 11f475611..3f5e2c2e5 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m @@ -44,7 +44,7 @@ @interface SDLMenuCell() context(@"when all the files need to be uploaded", ^{ beforeEach(^{ - OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(NO); + OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(YES); }); context(@"when the window capability doesn't support the primary image", ^{ @@ -68,7 +68,7 @@ @interface SDLMenuCell() it(@"should only return primary images to upload", ^{ NSArray *artworksToUpload = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu fileManager:mockFileManager windowCapability:testWindowCapability]; - expect(artworksToUpload).to(haveCount(1)); + expect(artworksToUpload).to(haveCount(2)); }); }); @@ -90,7 +90,7 @@ @interface SDLMenuCell() it(@"should only return all images to upload", ^{ NSArray *artworksToUpload = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:SDLMenuReplaceUtilitiesSpecHelpers.deepMenu fileManager:mockFileManager windowCapability:testWindowCapability]; - expect(artworksToUpload).to(haveCount(2)); + expect(artworksToUpload).to(haveCount(4)); }); }); }); @@ -98,7 +98,7 @@ @interface SDLMenuCell() context(@"when no files need to be uploaded", ^{ beforeEach(^{ - OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(YES); + OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(NO); }); context(@"when the window capability supports both images", ^{ @@ -206,7 +206,7 @@ @interface SDLMenuCell() it(@"should generate the correct RPCs", ^{ NSArray *requests = [SDLMenuReplaceUtilities deleteCommandsForCells:testMenuCells]; - expect(requests).to(haveCount(6)); + expect(requests).to(haveCount(3)); expect(requests[0]).to(beAnInstanceOf(SDLDeleteSubMenu.class)); expect(requests[1]).to(beAnInstanceOf(SDLDeleteCommand.class)); expect(requests[2]).to(beAnInstanceOf(SDLDeleteSubMenu.class)); diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m index 6d6122654..8c71d8dc5 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m @@ -29,27 +29,31 @@ @implementation SDLMenuReplaceUtilitiesSpecHelpers + (NSArray *)deepMenu { NSData *cellArtData = [@"testart" dataUsingEncoding:NSUTF8StringEncoding]; NSData *cellArtData2 = [@"testart2" dataUsingEncoding:NSUTF8StringEncoding]; + NSData *cellArtData3 = [@"testart3" dataUsingEncoding:NSUTF8StringEncoding]; + NSData *cellArtData4 = [@"testart4" dataUsingEncoding:NSUTF8StringEncoding]; SDLArtwork *artwork1 = [[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO]; SDLArtwork *artwork2 = [[SDLArtwork alloc] initWithData:cellArtData2 name:@"Test Art 2" fileExtension:@"png" persistent:NO]; + SDLArtwork *artwork3 = [[SDLArtwork alloc] initWithData:cellArtData3 name:@"Test Art 3" fileExtension:@"png" persistent:NO]; + SDLArtwork *artwork4 = [[SDLArtwork alloc] initWithData:cellArtData4 name:@"Test Art 4" fileExtension:@"png" persistent:NO]; NSArray *subList1SubList1 = @[ - [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 1" icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], + [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 1" icon:nil secondaryArtwork:artwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 2" icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] ]; NSArray *subList1 = @[ - [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork1 secondaryArtwork:nil submenuLayout:nil subCells:subList1SubList1], - [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] + [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork4 secondaryArtwork:nil submenuLayout:nil subCells:subList1SubList1], + [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork2 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] ]; NSArray *subList2 = @[ - [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], + [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork4 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork2 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] ]; return @[ [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil submenuLayout:nil subCells:subList1], - [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], + [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:subList2] ]; } From df856a454990e5ffdcb3f7bb8a14c9d53373e3fc Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 10 Aug 2021 13:08:18 -0400 Subject: [PATCH 083/112] Finish menu replace tests --- .../private/SDLMenuReplaceOperation.m | 6 +- .../private/SDLMenuReplaceUtilities.h | 4 +- .../private/SDLMenuReplaceUtilities.m | 10 +- .../DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m | 170 +++++++++++++++++- .../SDLMenuReplaceUtilitiesSpecHelpers.h | 4 +- .../SDLMenuReplaceUtilitiesSpecHelpers.m | 72 +++++--- 6 files changed, 219 insertions(+), 47 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index a47e9f1c1..b15663695 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -222,7 +222,7 @@ - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuC } else if (response.success.boolValue) { // Find the id of the successful request and remove it from the current menu list wherever it may have been UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; - [SDLMenuReplaceUtilities removeMenuCellFromList:self.mutableCurrentMenu withCmdId:commandId]; + [SDLMenuReplaceUtilities removeCellFromList:self.mutableCurrentMenu withCellId:commandId]; } } completionHandler:^(BOOL success) { if (!success) { @@ -257,7 +257,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA // Find the id of the successful request and add it from the current menu list wherever it needs to be UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; - [SDLMenuReplaceUtilities addMenuRequestWithCommandId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; + [SDLMenuReplaceUtilities addCellWithCellId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; } } completionHandler:^(BOOL success) { if (!success) { @@ -272,7 +272,7 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA // Find the id of the successful request and add it from the current menu list wherever it needs to be UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; - [SDLMenuReplaceUtilities addMenuRequestWithCommandId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; + [SDLMenuReplaceUtilities addCellWithCellId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; } } completionHandler:^(BOOL success) { if (!success) { diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index 239fb5623..c90845382 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -64,7 +64,7 @@ NS_ASSUME_NONNULL_BEGIN /// @param menuCellList The list to mutate and remove the item from /// @param commandId The id of the cell to find and remove /// @return YES if the cell was found and removed successfully, NO if it was not -+ (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId; ++ (BOOL)removeCellFromList:(NSMutableArray *)menuCellList withCellId:(UInt32)commandId; /// Finds a menu cell from newMenuList with the given commandId and inserts it into the main menu list (or a subcell list) at the given position /// @param commandId The command id for the cell to be found @@ -72,7 +72,7 @@ NS_ASSUME_NONNULL_BEGIN /// @param newMenuList The complete requested new menu list. We will find the cell to insert from this list. /// @param mainMenuList The mutable main menu list. The place to insert the cell will be in this list or one of its cell's subcell list (or one of it's cell's subcell's subcell's list, etc.) /// @return YES if the cell was added successfully, NO if the cell was not -+ (BOOL)addMenuRequestWithCommandId:(UInt32)commandId position:(UInt16)position fromNewMenuList:(NSArray *)newMenuList toMainMenuList:(NSMutableArray *)mainMenuList; ++ (BOOL)addCellWithCellId:(UInt32)commandId position:(UInt16)position fromNewMenuList:(NSArray *)newMenuList toMainMenuList:(NSMutableArray *)mainMenuList; @end diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 5b899eb70..3412b1b92 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -197,7 +197,7 @@ + (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell fileManager #pragma mark - Updating Menu Cells In Lists -+ (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList withCmdId:(UInt32)commandId { ++ (BOOL)removeCellFromList:(NSMutableArray *)menuCellList withCellId:(UInt32)commandId { for (SDLMenuCell *menuCell in menuCellList) { if (menuCell.cellId == commandId) { // If the cell id matches the command id, remove it from the list and return @@ -206,7 +206,7 @@ + (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList wit } else if (menuCell.subCells.count > 0) { // If the menu cell has subcells, we need to recurse and check the subcells NSMutableArray *newList = [menuCell.subCells mutableCopy]; - BOOL foundAndRemovedItem = [self removeMenuCellFromList:newList withCmdId:commandId]; + BOOL foundAndRemovedItem = [self removeCellFromList:newList withCellId:commandId]; if (foundAndRemovedItem) { menuCell.subCells = [newList copy]; return YES; @@ -217,14 +217,14 @@ + (BOOL)removeMenuCellFromList:(NSMutableArray *)menuCellList wit return NO; } -+ (BOOL)addMenuRequestWithCommandId:(UInt32)commandId position:(UInt16)position fromNewMenuList:(NSArray *)newMenuList toMainMenuList:(NSMutableArray *)mainMenuList { ++ (BOOL)addCellWithCellId:(UInt32)cellId position:(UInt16)position fromNewMenuList:(NSArray *)newMenuList toMainMenuList:(NSMutableArray *)mainMenuList { SDLMenuCell *addedCell = nil; for (SDLMenuCell *cell in newMenuList) { - if (cell.cellId == commandId) { + if (cell.cellId == cellId) { addedCell = cell; break; } else if (cell.subCells.count > 0) { - BOOL success = [self addMenuRequestWithCommandId:commandId position:position fromNewMenuList:cell.subCells toMainMenuList:mainMenuList]; + BOOL success = [self addCellWithCellId:cellId position:position fromNewMenuList:cell.subCells toMainMenuList:mainMenuList]; if (success) { return YES; } } } diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m index 3f5e2c2e5..fd1a19b01 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m @@ -20,7 +20,7 @@ @interface SDLMenuCell() QuickSpecBegin(SDLMenuReplaceUtilitiesSpec) -__block NSArray *testMenuCells = nil; +__block NSMutableArray *testMenuCells = nil; __block SDLFileManager *mockFileManager = nil; __block SDLWindowCapability *testWindowCapability = nil; __block NSArray *allSupportedTextFields = @[ @@ -261,19 +261,173 @@ @interface SDLMenuCell() // updating menu cells describe(@"updating menu cell lists", ^{ - __block NSArray *testNewMenuCells = nil; __block UInt32 testCommandId = 0; - // removeMenuCellFromList:withCmdId: - describe(@"removing commands from a list", ^{ - context(@"the list only has one level", ^{ + describe(@"removing commands", ^{ + context(@"from a shallow list", ^{ + beforeEach(^{ + testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu; + }); + + context(@"when the cell is in the menu", ^{ + beforeEach(^{ + testCommandId = testMenuCells[1].cellId; + }); + + it(@"should return the menu without the cell and return YES", ^{ + NSMutableArray *testMutableMenuCells = [testMenuCells mutableCopy]; + BOOL foundItem = [SDLMenuReplaceUtilities removeCellFromList:testMutableMenuCells withCellId:testCommandId]; + + expect(foundItem).to(beTrue()); + expect(testMutableMenuCells).to(haveCount(2)); + expect(testMutableMenuCells[0]).to(equal(testMenuCells[0])); + expect(testMutableMenuCells[1]).to(equal(testMenuCells[2])); + }); + }); + + context(@"when the cell is not in the menu", ^{ + beforeEach(^{ + testCommandId = 100; + }); + + it(@"should return the menu with all cells and return NO", ^{ + NSMutableArray *testMutableMenuCells = [testMenuCells mutableCopy]; + BOOL foundItem = [SDLMenuReplaceUtilities removeCellFromList:testMutableMenuCells withCellId:testCommandId]; + + expect(foundItem).to(beFalse()); + expect(testMutableMenuCells).to(haveCount(3)); + }); + }); + }); + + context(@"from a deep list", ^{ + beforeEach(^{ + testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu; + }); + + context(@"when the cell is in the top menu", ^{ + beforeEach(^{ + testCommandId = testMenuCells[1].cellId; + }); + + it(@"should return the menu without the cell and return YES", ^{ + NSMutableArray *testMutableMenuCells = [testMenuCells mutableCopy]; + BOOL foundItem = [SDLMenuReplaceUtilities removeCellFromList:testMutableMenuCells withCellId:testCommandId]; + + expect(foundItem).to(beTrue()); + expect(testMutableMenuCells).to(haveCount(2)); + expect(testMutableMenuCells[0]).to(equal(testMenuCells[0])); + expect(testMutableMenuCells[1]).to(equal(testMenuCells[2])); + }); + }); + + context(@"when the cell is in the submenu", ^{ + beforeEach(^{ + testCommandId = 5; + }); + + it(@"should return the menu without the cell and return YES", ^{ + NSMutableArray *testMutableMenuCells = [testMenuCells mutableCopy]; + BOOL foundItem = [SDLMenuReplaceUtilities removeCellFromList:testMutableMenuCells withCellId:testCommandId]; + + expect(foundItem).to(beTrue()); + expect(testMutableMenuCells).to(haveCount(3)); + expect(testMutableMenuCells[2].subCells).to(haveCount(1)); + }); + }); + + context(@"when the cell is not in the menu", ^{ + beforeEach(^{ + testCommandId = 100; + }); + + it(@"should return the menu with all cells and return NO", ^{ + NSMutableArray *testMutableMenuCells = [testMenuCells mutableCopy]; + BOOL foundItem = [SDLMenuReplaceUtilities removeCellFromList:testMutableMenuCells withCellId:testCommandId]; + expect(foundItem).to(beFalse()); + expect(testMutableMenuCells).to(haveCount(3)); + expect(testMutableMenuCells[0].subCells).to(haveCount(2)); + expect(testMutableMenuCells[2].subCells).to(haveCount(2)); + }); + }); }); }); - describe(@"add commands to the list", ^{ - __block NSMutableArray *testMenuCells = nil; - __block UInt16 testPosition = 0; + describe(@"add commands to the main list", ^{ + __block NSMutableArray *newCellList = nil; + + context(@"from a shallow list", ^{ + beforeEach(^{ + testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu; + + SDLMenuCell *newCell = [[SDLMenuCell alloc] initWithTitle:@"New Cell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + newCell.cellId = 99; + newCellList = [@[newCell] mutableCopy]; + }); + + describe(@"if the cell is not in the cell list", ^{ + beforeEach(^{ + newCellList = [[NSMutableArray alloc] init]; + }); + + it(@"should return NO", ^{ + BOOL didAddCell = [SDLMenuReplaceUtilities addCellWithCellId:99 position:0 fromNewMenuList:newCellList toMainMenuList:testMenuCells]; + + expect(didAddCell).to(beFalse()); + }); + }); + + context(@"at the beginning", ^{ + it(@"should return YES and the cell should be included", ^{ + BOOL didAddCell = [SDLMenuReplaceUtilities addCellWithCellId:newCellList[0].cellId position:0 fromNewMenuList:newCellList toMainMenuList:testMenuCells]; + + expect(didAddCell).to(beTrue()); + expect(testMenuCells).to(haveCount(4)); + expect(testMenuCells[0]).to(equal(newCellList[0])); + }); + }); + + context(@"in the middle", ^{ + it(@"should return YES and the cell should be included", ^{ + BOOL didAddCell = [SDLMenuReplaceUtilities addCellWithCellId:newCellList[0].cellId position:1 fromNewMenuList:newCellList toMainMenuList:testMenuCells]; + + expect(didAddCell).to(beTrue()); + expect(testMenuCells).to(haveCount(4)); + expect(testMenuCells[1]).to(equal(newCellList[0])); + }); + }); + + context(@"at the end", ^{ + it(@"should return YES and the cell should be included", ^{ + BOOL didAddCell = [SDLMenuReplaceUtilities addCellWithCellId:newCellList[0].cellId position:3 fromNewMenuList:newCellList toMainMenuList:testMenuCells]; + + expect(didAddCell).to(beTrue()); + expect(testMenuCells).to(haveCount(4)); + expect(testMenuCells[3]).to(equal(newCellList[0])); + }); + }); + }); + + context(@"from a deep list", ^{ + beforeEach(^{ + testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu; + + SDLMenuCell *subCell = [[SDLMenuCell alloc] initWithTitle:@"New SubCell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + subCell.cellId = 98; + SDLMenuCell *newCell = [[SDLMenuCell alloc] initWithTitle:@"New Cell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[subCell]]; + newCell.cellId = 99; + newCellList = [@[newCell] mutableCopy]; + }); + + it(@"should properly add the subcell to the list", ^{ + BOOL didAddCell = [SDLMenuReplaceUtilities addCellWithCellId:98 position:0 fromNewMenuList:newCellList toMainMenuList:testMenuCells]; + + expect(didAddCell).to(beTrue()); + expect(testMenuCells).to(haveCount(4)); + expect(testMenuCells[0]).to(equal(newCellList[0].subCells[0])); + }); + }); }); }); diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h index 908e6b59d..7106cb567 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h @@ -14,8 +14,8 @@ NS_ASSUME_NONNULL_BEGIN @interface SDLMenuReplaceUtilitiesSpecHelpers : NSObject -@property (class, nonatomic, readonly) NSArray *topLevelOnlyMenu; -@property (class, nonatomic, readonly) NSArray *deepMenu; +@property (class, nonatomic, readonly) NSMutableArray *topLevelOnlyMenu; +@property (class, nonatomic, readonly) NSMutableArray *deepMenu; @end diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m index 8c71d8dc5..2b9bef2b9 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m @@ -11,22 +11,34 @@ #import "SDLArtwork.h" #import "SDLMenuCell.h" +@interface SDLMenuCell() + +@property (assign, nonatomic) UInt32 parentCellId; +@property (assign, nonatomic) UInt32 cellId; + +@end + @implementation SDLMenuReplaceUtilitiesSpecHelpers -+ (NSArray *)topLevelOnlyMenu { ++ (NSMutableArray *)topLevelOnlyMenu { NSData *cellArtData = [@"testart" dataUsingEncoding:NSUTF8StringEncoding]; NSData *cellArtData2 = [@"testart2" dataUsingEncoding:NSUTF8StringEncoding]; SDLArtwork *artwork1 = [[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO]; SDLArtwork *artwork2 = [[SDLArtwork alloc] initWithData:cellArtData2 name:@"Test Art 2" fileExtension:@"png" persistent:NO]; - return @[ - [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], - [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], - [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:artwork2 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] - ]; + SDLMenuCell *cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + cell1.cellId = 1; + + SDLMenuCell *cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + cell2.cellId = 2; + + SDLMenuCell *cell3 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:artwork2 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + cell3.cellId = 3; + + return [@[cell1, cell2, cell3] mutableCopy]; } -+ (NSArray *)deepMenu { ++ (NSMutableArray *)deepMenu { NSData *cellArtData = [@"testart" dataUsingEncoding:NSUTF8StringEncoding]; NSData *cellArtData2 = [@"testart2" dataUsingEncoding:NSUTF8StringEncoding]; NSData *cellArtData3 = [@"testart3" dataUsingEncoding:NSUTF8StringEncoding]; @@ -36,26 +48,32 @@ @implementation SDLMenuReplaceUtilitiesSpecHelpers SDLArtwork *artwork3 = [[SDLArtwork alloc] initWithData:cellArtData3 name:@"Test Art 3" fileExtension:@"png" persistent:NO]; SDLArtwork *artwork4 = [[SDLArtwork alloc] initWithData:cellArtData4 name:@"Test Art 4" fileExtension:@"png" persistent:NO]; - NSArray *subList1SubList1 = @[ - [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 1" icon:nil secondaryArtwork:artwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], - [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 2" icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] - ]; - - NSArray *subList1 = @[ - [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork4 secondaryArtwork:nil submenuLayout:nil subCells:subList1SubList1], - [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork2 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] - ]; - - NSArray *subList2 = @[ - [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork4 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], - [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork2 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}] - ]; - - return @[ - [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil submenuLayout:nil subCells:subList1], - [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}], - [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:subList2] - ]; + SDLMenuCell *subList1SubList1Cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 1" icon:nil secondaryArtwork:artwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + subList1SubList1Cell1.cellId = 1; + SDLMenuCell *subList1SubList1Cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 2" icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + subList1SubList1Cell2.cellId = 2; + NSArray *subList1SubList1 = @[subList1SubList1Cell1, subList1SubList1Cell2]; + + SDLMenuCell *subList1Cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork4 secondaryArtwork:nil submenuLayout:nil subCells:subList1SubList1]; + subList1Cell1.cellId = 3; + SDLMenuCell *subList1Cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork2 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + subList1Cell2.cellId = 4; + NSArray *subList1 = @[subList1Cell1, subList1Cell2]; + + SDLMenuCell *subList2Cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork4 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + subList2Cell1.cellId = 5; + SDLMenuCell *subList2Cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork2 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + subList2Cell2.cellId = 6; + NSArray *subList2 = @[subList2Cell1, subList2Cell2]; + + SDLMenuCell *topListCell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil submenuLayout:nil subCells:subList1]; + topListCell1.cellId = 7; + SDLMenuCell *topListCell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + topListCell2.cellId = 8; + SDLMenuCell *topListCell3 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:subList2]; + topListCell3.cellId = 9; + + return [@[topListCell1, topListCell2, topListCell3] mutableCopy]; } @end From 8d24f021d85d592ba67ade0c6171ae8f78ab70a7 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 10 Aug 2021 16:05:31 -0400 Subject: [PATCH 084/112] Work on replace operation spec --- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 91 ++++++++++++++----- 1 file changed, 68 insertions(+), 23 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 28cb0e8b3..b3b0c53da 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -15,9 +15,15 @@ #import "SDLMenuReplaceUtilities.h" #import "TestConnectionManager.h" +@interface SDLMenuCell () + +@property (assign, nonatomic) UInt32 cellId; + +@end + QuickSpecBegin(SDLMenuReplaceOperationSpec) -describe(@"a menu replace operation", ^{ +fdescribe(@"a menu replace operation", ^{ __block SDLMenuReplaceOperation *testOp = nil; __block TestConnectionManager *testConnectionManager = nil; @@ -38,9 +44,12 @@ __block SDLMenuCell *submenuImageCell = nil; __block SDLAddCommandResponse *addCommandSuccessResponse = nil; + __block SDLAddSubMenuResponse *addSubMenuSuccessResponse = nil; + __block SDLDeleteCommandResponse *deleteCommandSuccessResponse = nil; + __block SDLDeleteSubMenuResponse *deleteSubMenuSuccessResponse = nil; - __block NSArray *receivedMenuCells = nil; - __block NSError *receivedError = nil; + __block NSArray *resultMenuCells = nil; + __block NSError *resultError = nil; __block SDLCurrentMenuUpdatedBlock testCurrentMenuUpdatedBlock = nil; __block SDLMenuReplaceUtilities *mockReplaceUtilities = nil; @@ -52,14 +61,28 @@ testArtwork3.overwrite = YES; textOnlyCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + textOnlyCell.cellId = 1; textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:testArtwork secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + textAndImageCell.cellId = 2; submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[textOnlyCell, textAndImageCell]]; + submenuCell.cellId = 3; submenuImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 4" secondaryText:nil tertiaryText:nil icon:testArtwork2 secondaryArtwork:nil submenuLayout:SDLMenuLayoutTiles subCells:@[textOnlyCell]]; + submenuImageCell.cellId = 4; textOnlyCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 5" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + textOnlyCell2.cellId = 5; addCommandSuccessResponse = [[SDLAddCommandResponse alloc] init]; addCommandSuccessResponse.success = @YES; addCommandSuccessResponse.resultCode = SDLResultSuccess; + addSubMenuSuccessResponse = [[SDLAddSubMenuResponse alloc] init]; + addSubMenuSuccessResponse.success = @YES; + addSubMenuSuccessResponse.resultCode = SDLResultSuccess; + deleteCommandSuccessResponse = [[SDLDeleteCommandResponse alloc] init]; + deleteCommandSuccessResponse.success = @YES; + deleteCommandSuccessResponse.resultCode = SDLResultSuccess; + deleteSubMenuSuccessResponse = [[SDLDeleteSubMenuResponse alloc] init]; + deleteSubMenuSuccessResponse.success = @YES; + deleteSubMenuSuccessResponse.resultCode = SDLResultSuccess; testOp = nil; testConnectionManager = [[TestConnectionManager alloc] init]; @@ -72,19 +95,17 @@ testCurrentMenu = @[]; testNewMenu = nil; - receivedMenuCells = nil; - receivedError = nil; + resultMenuCells = nil; + resultError = nil; testCurrentMenuUpdatedBlock = ^(NSArray *currentMenuCells, NSError *error) { - receivedMenuCells = currentMenuCells; - receivedError = error; + resultMenuCells = currentMenuCells; + resultError = error; }; mockReplaceUtilities = OCMClassMock([SDLMenuReplaceUtilities class]); }); - // sending initial batch of cells describe(@"sending initial batch of cells", ^{ - // when setting no cells context(@"when setting no cells", ^{ it(@"should finish without doing anything", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; @@ -92,10 +113,11 @@ expect(testConnectionManager.receivedRequests).to(beEmpty()); expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(beEmpty()); }); }); - // when starting while cancelled context(@"when starting while cancelled", ^{ it(@"should finish without doing anything", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; @@ -104,10 +126,11 @@ expect(testConnectionManager.receivedRequests).to(beEmpty()); expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(beNil()); }); }); - // when uploading a text-only cell context(@"when uploading a text-only cell", ^{ beforeEach(^{ testNewMenu = @[textOnlyCell]; @@ -132,10 +155,11 @@ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(1)); }); }); - // when uploading text and image cell context(@"when uploading text and image cell", ^{ beforeEach(^{ testNewMenu = @[textAndImageCell]; @@ -151,6 +175,9 @@ }); it(@"should properly update an image cell", ^{ + OCMReject([testFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]); + + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; [testOp start]; @@ -160,7 +187,13 @@ expect(add).to(haveCount(1)); expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name)); - OCMReject([testFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]); + + [testConnectionManager respondToLastRequestWithResponse:addCommandSuccessResponse]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(1)); }); }); @@ -178,30 +211,33 @@ OCMVerify([testFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]); NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]]; - NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - expect(deletes).to(beEmpty()); + NSArray *deletesArray = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + expect(deletesArray).to(beEmpty()); NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; - NSArray *add = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; - expect(add).toNot(beEmpty()); + NSArray *addsArray = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + expect(addsArray).toNot(beEmpty()); + + [testConnectionManager respondToLastRequestWithResponse:addCommandSuccessResponse]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(1)); }); }); }); - // when uploading a cell with subcells context(@"when uploading a cell with subcells", ^{ beforeEach(^{ testNewMenu = @[submenuCell]; }); - it(@"should send an appropriate number of AddSubmenu and AddCommandRequests", ^{ + fit(@"should send an appropriate number of AddSubmenu and AddCommandRequests", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; [testOp start]; - SDLAddSubMenuResponse *response = [[SDLAddSubMenuResponse alloc] init]; - response.success = @YES; - response.resultCode = SDLResultSuccess; - [testConnectionManager respondToLastRequestWithResponse:response]; + [testConnectionManager respondToLastRequestWithResponse:addSubMenuSuccessResponse]; [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]]; @@ -212,6 +248,15 @@ expect(adds).to(haveCount(2)); expect(submenus).to(haveCount(1)); + + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil]; + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:2 error:nil]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(1)); + expect(resultMenuCells[0].subCells).to(haveCount(2)); }); }); }); From 24905c8223f133d8d867650e98e3b884c3846c86 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 11 Aug 2021 08:42:04 -0400 Subject: [PATCH 085/112] Fix not using the stripped menu --- .../private/SDLMenuReplaceOperation.m | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index b15663695..531ba6294 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -53,8 +53,6 @@ @interface SDLMenuReplaceOperation () @property (copy, nonatomic) SDLCurrentMenuUpdatedBlock currentMenuUpdatedHandler; // Internal properties -@property (strong, nonatomic) NSArray *currentStrippedMenu; -@property (strong, nonatomic) NSArray *updatedStrippedMenu; @property (copy, nonatomic, nullable) NSError *internalError; @end @@ -81,20 +79,24 @@ - (void)start { [super start]; if (self.isCancelled) { return; } - self.updatedStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.updatedMenu basedOnWindowCapability:self.windowCapability]; - self.currentStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.mutableCurrentMenu basedOnWindowCapability:self.windowCapability]; - - BOOL supportsMenuUniqueness = [[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithMajor:7 minor:1 patch:0]]; - [self.class sdl_addUniqueNamesToCells:self.updatedStrippedMenu supportsMenuUniqueness:supportsMenuUniqueness]; - [self.class sdl_applyUniqueNamesOnCells:self.updatedStrippedMenu toCells:self.updatedMenu]; - SDLDynamicMenuUpdateRunScore *runScore = nil; if (self.compatibilityModeEnabled) { SDLLogV(@"Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); runScore = [SDLDynamicMenuUpdateAlgorithm compatibilityRunScoreWithOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; } else { SDLLogV(@"Dynamic menu update active. Running the algorithm to find the best way to delete / add cells."); - runScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:self.currentMenu updatedMenuCells:self.updatedStrippedMenu]; + + // Strip the "current menu" and the new menu of properties that are not displayed on the head unit + NSArray *updatedStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.updatedMenu basedOnWindowCapability:self.windowCapability]; + NSArray *currentStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.mutableCurrentMenu basedOnWindowCapability:self.windowCapability]; + + // Generate unique names and ensure that all menus we are tracking have them so that we can properly compare when using the dynamic algorithm + BOOL supportsMenuUniqueness = [[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithMajor:7 minor:1 patch:0]]; + [self.class sdl_generateUniqueNamesForCells:updatedStrippedMenu supportsMenuUniqueness:supportsMenuUniqueness]; + [self.class sdl_applyUniqueNamesOnCells:updatedStrippedMenu toCells:self.updatedMenu]; + [self.class sdl_applyUniqueNamesOnCells:self.currentMenu toCells:currentStrippedMenu]; + + runScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:currentStrippedMenu updatedMenuCells:updatedStrippedMenu]; } // If both old and new cells are empty, nothing needs to happen @@ -380,7 +382,7 @@ - (void)sdl_transferCellIDsFromOldCells:(NSArray *)oldCells toKep return removePropertiesCopy; } -+ (void)sdl_addUniqueNamesToCells:(NSArray *)menuCells supportsMenuUniqueness:(BOOL)supportsMenuUniqueness { ++ (void)sdl_generateUniqueNamesForCells:(NSArray *)menuCells supportsMenuUniqueness:(BOOL)supportsMenuUniqueness { // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary NSMutableDictionary, NSNumber *> *dictCounter = [[NSMutableDictionary alloc] init]; for (NSUInteger i = 0; i < menuCells.count; i++) { @@ -399,7 +401,7 @@ + (void)sdl_addUniqueNamesToCells:(NSArray *)menuCells supportsMe } if (menuCells[i].subCells.count > 0) { - [self sdl_addUniqueNamesToCells:menuCells[i].subCells supportsMenuUniqueness:supportsMenuUniqueness]; + [self sdl_generateUniqueNamesForCells:menuCells[i].subCells supportsMenuUniqueness:supportsMenuUniqueness]; } } } From d0ddd9a546bf09451d721a3670ecfd1315871759 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 11 Aug 2021 15:53:55 -0400 Subject: [PATCH 086/112] Menu manager fixes * Fix inserting cells with subcells into list * Cell Ids now handled by operations * A whole host of test updates --- SmartDeviceLink/private/SDLMenuManager.m | 24 ----- .../private/SDLMenuReplaceOperation.m | 3 + .../private/SDLMenuReplaceUtilities.h | 4 + .../private/SDLMenuReplaceUtilities.m | 90 +++++++++++------ .../DevAPISpecs/SDLMenuManagerSpec.m | 4 - .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 98 ++++++++++++------- .../DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m | 66 +++++++++++-- .../SDLMenuReplaceUtilitiesSpecHelpers.m | 18 ++-- 8 files changed, 196 insertions(+), 111 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index a70c6b56a..3d7047eb3 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -80,19 +80,14 @@ @interface SDLMenuManager() @property (copy, nonatomic) NSArray *currentMenuCells; @property (strong, nonatomic, nullable) SDLMenuConfiguration *currentMenuConfiguration; -@property (assign, nonatomic) UInt32 lastMenuId; - @end -UInt32 const MenuCellIdMin = 1; - @implementation SDLMenuManager - (instancetype)init { self = [super init]; if (!self) { return nil; } - _lastMenuId = MenuCellIdMin; _menuConfiguration = [[SDLMenuConfiguration alloc] init]; _menuCells = @[]; _currentMenuCells = @[]; @@ -121,7 +116,6 @@ - (void)start { } - (void)stop { - _lastMenuId = MenuCellIdMin; _menuCells = @[]; _currentMenuCells = @[]; _transactionQueue = [self sdl_newTransactionQueue]; @@ -200,7 +194,6 @@ - (void)setMenuCells:(NSArray *)menuCells { } _menuCells = [[NSArray alloc] initWithArray:menuCells copyItems:YES]; - [self sdl_updateIdsOnMenuCells:self.menuCells parentId:ParentIdNotFound]; __weak typeof(self) weakself = self; SDLMenuReplaceOperation *menuReplaceOperation = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells compatibilityModeEnabled:(![self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) currentMenuUpdatedHandler:^(NSArray * _Nonnull currentMenuCells, NSError *error) { @@ -336,23 +329,6 @@ - (BOOL)sdl_menuCellsAreUnique:(NSArray *)cells allVoiceCommands: return YES; } -#pragma mark IDs - -/// Assign cell ids on an array of menu cells given a parent id (or no parent id) -/// @param menuCells The array of menu cells to update -/// @param parentId The parent id to assign if needed -- (void)sdl_updateIdsOnMenuCells:(NSArray *)menuCells parentId:(UInt32)parentId { - for (SDLMenuCell *cell in menuCells) { - cell.cellId = self.lastMenuId++; - if (parentId != ParentIdNotFound) { - cell.parentCellId = parentId; - } - if (cell.subCells.count > 0) { - [self sdl_updateIdsOnMenuCells:cell.subCells parentId:cell.cellId]; - } - } -} - #pragma mark - Calling handlers /// Call a handler for a currently displayed SDLMenuCell based on the incoming SDLOnCommand notification diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 531ba6294..5de4a6dd6 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -18,6 +18,7 @@ #import "SDLLogMacros.h" #import "SDLMenuCell.h" #import "SDLMenuConfiguration.h" +#import "SDLMenuManagerPrivateConstants.h" #import "SDLTextFieldName.h" #import "SDLVersion.h" #import "SDLWindowCapability.h" @@ -79,6 +80,8 @@ - (void)start { [super start]; if (self.isCancelled) { return; } + [SDLMenuReplaceUtilities updateIdsOnMenuCells:self.updatedMenu parentId:ParentIdNotFound]; + SDLDynamicMenuUpdateRunScore *runScore = nil; if (self.compatibilityModeEnabled) { SDLLogV(@"Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index c90845382..35defac10 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -21,6 +21,10 @@ NS_ASSUME_NONNULL_BEGIN @interface SDLMenuReplaceUtilities : NSObject +#pragma mark - Ids + ++ (void)updateIdsOnMenuCells:(NSArray *)menuCells parentId:(UInt32)parentId; + #pragma mark - Artworks /// Finds all artworks that need to be uploaded from the given list of menu cells diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 3412b1b92..22ab00762 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -23,7 +23,7 @@ #import "SDLWindowCapability.h" #import "SDLWindowCapability+ScreenManagerExtensions.h" -@interface SDLMenuCell() +@interface SDLMenuCell () @property (assign, nonatomic) UInt32 parentCellId; @property (assign, nonatomic) UInt32 cellId; @@ -31,7 +31,39 @@ @interface SDLMenuCell() @end +@interface SDLMenuReplaceUtilities () + +@property (class, assign, nonatomic) UInt32 nextMenuId; + +@end + @implementation SDLMenuReplaceUtilities +static UInt32 _menuId = 0; + +#pragma mark Ids + ++ (void)setNextMenuId:(UInt32)nextMenuId { + _menuId = nextMenuId; +} + ++ (UInt32)nextMenuId { + return ++_menuId; +} + +/// Assign cell ids on an array of menu cells given a parent id (or no parent id) +/// @param menuCells The array of menu cells to update +/// @param parentId The parent id to assign if needed ++ (void)updateIdsOnMenuCells:(NSArray *)menuCells parentId:(UInt32)parentId { + for (SDLMenuCell *cell in menuCells) { + cell.cellId = self.class.nextMenuId; + if (parentId != ParentIdNotFound) { + cell.parentCellId = parentId; + } + if (cell.subCells.count > 0) { + [self updateIdsOnMenuCells:cell.subCells parentId:cell.cellId]; + } + } +} #pragma mark Artworks @@ -237,38 +269,38 @@ + (BOOL)addCellWithCellId:(UInt32)cellId position:(UInt16)position fromNewMenuLi } + (BOOL)sdl_addMenuCell:(SDLMenuCell *)cell toList:(NSMutableArray *)menuCellList atPosition:(UInt16)position { - if (cell.parentCellId != ParentIdNotFound) { - // If the cell has a parent id, we need to find the cell with a matching cell id and insert it into its submenu - for (SDLMenuCell *menuCell in menuCellList) { - if (menuCell.cellId == cell.parentCellId) { - // If we found the correct submenu, insert it into that submenu - NSMutableArray *newList = nil; - if (menuCell.subCells != nil) { - newList = [menuCell.subCells mutableCopy]; - } else { - newList = [NSMutableArray array]; - } + if (cell.parentCellId == ParentIdNotFound) { + // The cell does not have a parent id, just insert it into the main menu + [self sdl_insertMenuCell:cell intoList:menuCellList atPosition:position]; + return YES; + } - [self sdl_insertMenuCell:cell intoList:newList atPosition:position]; + // If the cell has a parent id, we need to find the cell with a matching cell id and insert it into its submenu + for (SDLMenuCell *menuCell in menuCellList) { + if (menuCell.cellId == cell.parentCellId) { + // If we found the correct submenu, insert it into that submenu + NSMutableArray *newList = nil; + if (menuCell.subCells != nil) { + newList = [menuCell.subCells mutableCopy]; + } else { + newList = [NSMutableArray array]; + } + + [self sdl_insertMenuCell:cell intoList:newList atPosition:position]; + menuCell.subCells = [newList copy]; + return YES; + } else if (menuCell.subCells.count > 0) { + // Check the subcells of this cell to see if any of those have cell ids that match the parent cell id + NSMutableArray *newList = [menuCell.subCells mutableCopy]; + BOOL foundAndAddedItem = [self sdl_addMenuCell:cell toList:newList atPosition:position]; + if (foundAndAddedItem) { menuCell.subCells = [newList copy]; return YES; - } else if (menuCell.subCells.count > 0) { - // Check the subcells of this cell to see if any of those have cell ids that match the parent cell id - NSMutableArray *newList = [menuCell.subCells mutableCopy]; - BOOL foundAndAddedItem = [self sdl_addMenuCell:cell toList:newList atPosition:position]; - if (foundAndAddedItem) { - menuCell.subCells = [newList copy]; - return YES; - } } } - - return NO; - } else { - // The cell does not have a parent id, just insert it into the main menu - [self sdl_insertMenuCell:cell intoList:menuCellList atPosition:position]; - return YES; } + + return NO; } + (void)sdl_insertMenuCell:(SDLMenuCell *)cell intoList:(NSMutableArray *)cellList atPosition:(UInt16)position { @@ -279,9 +311,9 @@ + (void)sdl_insertMenuCell:(SDLMenuCell *)cell intoList:(NSMutableArray cellList.count) { - [cellList addObject:cell]; + [cellList addObject:cellToInsert]; } else { - [cellList insertObject:cell atIndex:position]; + [cellList insertObject:cellToInsert atIndex:position]; } } diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index b1b99bee5..f0623ab49 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -31,8 +31,6 @@ @interface SDLMenuManager() @property (copy, nonatomic) NSArray *currentMenuCells; @property (strong, nonatomic, nullable) SDLMenuConfiguration *currentMenuConfiguration; -@property (assign, nonatomic) UInt32 lastMenuId; - @end QuickSpecBegin(SDLMenuManagerSpec) @@ -83,7 +81,6 @@ @interface SDLMenuManager() expect(testManager.currentSystemContext).to(beNil()); expect(testManager.currentMenuCells).to(beEmpty()); expect(testManager.currentMenuConfiguration).to(beNil()); - expect(testManager.lastMenuId).to(equal(1)); }); // when the manager stops @@ -105,7 +102,6 @@ @interface SDLMenuManager() expect(testManager.currentSystemContext).to(beNil()); expect(testManager.currentMenuCells).to(beEmpty()); expect(testManager.currentMenuConfiguration).to(beNil()); - expect(testManager.lastMenuId).to(equal(1)); }); }); diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index b3b0c53da..9fb4a7883 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -17,13 +17,14 @@ @interface SDLMenuCell () +@property (assign, nonatomic) UInt32 parentCellId; @property (assign, nonatomic) UInt32 cellId; @end QuickSpecBegin(SDLMenuReplaceOperationSpec) -fdescribe(@"a menu replace operation", ^{ +describe(@"a menu replace operation", ^{ __block SDLMenuReplaceOperation *testOp = nil; __block TestConnectionManager *testConnectionManager = nil; @@ -61,15 +62,10 @@ @interface SDLMenuCell () testArtwork3.overwrite = YES; textOnlyCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - textOnlyCell.cellId = 1; + textOnlyCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 5" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:testArtwork secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - textAndImageCell.cellId = 2; submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[textOnlyCell, textAndImageCell]]; - submenuCell.cellId = 3; submenuImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 4" secondaryText:nil tertiaryText:nil icon:testArtwork2 secondaryArtwork:nil submenuLayout:SDLMenuLayoutTiles subCells:@[textOnlyCell]]; - submenuImageCell.cellId = 4; - textOnlyCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 5" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - textOnlyCell2.cellId = 5; addCommandSuccessResponse = [[SDLAddCommandResponse alloc] init]; addCommandSuccessResponse.success = @YES; @@ -233,7 +229,7 @@ @interface SDLMenuCell () testNewMenu = @[submenuCell]; }); - fit(@"should send an appropriate number of AddSubmenu and AddCommandRequests", ^{ + it(@"should send an appropriate number of AddSubmenu and AddCommandRequests", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; [testOp start]; @@ -261,7 +257,6 @@ @interface SDLMenuCell () }); }); - // updating a menu without dynamic updates describe(@"updating a menu without dynamic updates", ^{ context(@"adding a text cell", ^{ beforeEach(^{ @@ -273,10 +268,7 @@ @interface SDLMenuCell () testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; [testOp start]; - SDLDeleteCommandResponse *deleteCommandResponse = [[SDLDeleteCommandResponse alloc] init]; - deleteCommandResponse.success = @YES; - deleteCommandResponse.resultCode = SDLResultSuccess; - [testConnectionManager respondToLastRequestWithResponse:deleteCommandResponse]; + [testConnectionManager respondToLastRequestWithResponse:deleteCommandSuccessResponse]; [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; @@ -287,6 +279,16 @@ @interface SDLMenuCell () expect(deletes).to(haveCount(1)); expect(adds).to(haveCount(2)); + + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil]; + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:2 error:nil]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(2)); + expect(resultMenuCells[0]).to(equal(textOnlyCell)); + expect(resultMenuCells[1]).to(equal(textOnlyCell2)); }); }); }); @@ -311,33 +313,57 @@ @interface SDLMenuCell () expect(deletes).to(haveCount(0)); expect(adds).to(haveCount(1)); + + [testConnectionManager respondToLastRequestWithResponse:addCommandSuccessResponse]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(2)); + }); + }); + + context(@"rearranging cells with subcells", ^{ + beforeEach(^{ + testCurrentMenu = @[textOnlyCell, submenuCell, submenuImageCell]; + testNewMenu = @[submenuCell, submenuImageCell, textOnlyCell]; + + OCMStub([testFileManager uploadArtworks:[OCMArg any] progressHandler:([OCMArg invokeBlockWithArgs:textAndImageCell.icon.name, @1.0, [NSNull null], nil]) completionHandler:([OCMArg invokeBlockWithArgs: @[textAndImageCell.icon.name], [NSNull null], nil])]); + }); + + it(@"should send dynamic deletes first then dynamic adds case with 2 submenu cells", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + // Delete textOnlyCell + [testConnectionManager respondToLastRequestWithResponse:deleteCommandSuccessResponse]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + expect(testOp.currentMenu).toNot(contain(textOnlyCell)); + + // Add textOnlyCell + [testConnectionManager respondToLastRequestWithResponse:addCommandSuccessResponse]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; + NSArray *submenu = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; + + expect(deletes).to(haveCount(1)); + expect(adds).to(haveCount(1)); + expect(submenu).to(haveCount(0)); + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(3)); }); }); }); -// it(@"should send dynamic deletes first then dynamic adds case with 2 submenu cells", ^{ -// testManager.menuCells = @[textOnlyCell, submenuCell, submenuImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[submenuCell, submenuImageCell, textOnlyCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// -// expect(deletes).to(haveCount(1)); -// expect(adds).to(haveCount(5)); -// expect(submenu).to(haveCount(2)); -// }); // // it(@"should send dynamic deletes first then dynamic adds when removing one submenu cell", ^{ // testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m index fd1a19b01..e3a28a796 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m @@ -7,6 +7,7 @@ #import "SDLFileManager.h" #import "SDLMenuCell.h" #import "SDLMenuReplaceUtilitiesSpecHelpers.h" +#import "SDLMenuManagerPrivateConstants.h" #import "SDLWindowCapability.h" #import "TestConnectionManager.h" @@ -18,6 +19,12 @@ @interface SDLMenuCell() @end +@interface SDLMenuReplaceUtilities () + +@property (class, assign, nonatomic) UInt32 nextMenuId; + +@end + QuickSpecBegin(SDLMenuReplaceUtilitiesSpec) __block NSMutableArray *testMenuCells = nil; @@ -36,6 +43,37 @@ @interface SDLMenuCell() [[SDLImageField alloc] initWithName:SDLImageFieldNameMenuSubMenuSecondaryImage imageTypeSupported:@[SDLImageTypeDynamic] imageResolution:nil] ]; +describe(@"adding ids", ^{ + it(@"should properly add ids", ^{ + SDLMenuReplaceUtilities.nextMenuId = 0; + testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu; + + [SDLMenuReplaceUtilities updateIdsOnMenuCells:testMenuCells parentId:ParentIdNotFound]; + + expect(testMenuCells[0].cellId).to(equal(1)); + expect(testMenuCells[1].cellId).to(equal(6)); + expect(testMenuCells[2].cellId).to(equal(7)); + + NSArray *subCellList1 = testMenuCells[0].subCells; + expect(subCellList1[0].cellId).to(equal(2)); + expect(subCellList1[0].parentCellId).to(equal(1)); + expect(subCellList1[1].cellId).to(equal(5)); + expect(subCellList1[1].parentCellId).to(equal(1)); + + NSArray *subCell1SubCellList1 = subCellList1[0].subCells; + expect(subCell1SubCellList1[0].cellId).to(equal(3)); + expect(subCell1SubCellList1[0].parentCellId).to(equal(2)); + expect(subCell1SubCellList1[1].cellId).to(equal(4)); + expect(subCell1SubCellList1[1].parentCellId).to(equal(2)); + + NSArray *subCellList2 = testMenuCells[2].subCells; + expect(subCellList2[0].cellId).to(equal(8)); + expect(subCellList2[0].parentCellId).to(equal(7)); + expect(subCellList2[1].cellId).to(equal(9)); + expect(subCellList2[1].parentCellId).to(equal(7)); + }); +}); + describe(@"finding all artworks from cells", ^{ beforeEach(^{ mockFileManager = OCMClassMock([SDLFileManager class]); @@ -267,6 +305,7 @@ @interface SDLMenuCell() context(@"from a shallow list", ^{ beforeEach(^{ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu; + [SDLMenuReplaceUtilities updateIdsOnMenuCells:testMenuCells parentId:ParentIdNotFound]; }); context(@"when the cell is in the menu", ^{ @@ -303,6 +342,7 @@ @interface SDLMenuCell() context(@"from a deep list", ^{ beforeEach(^{ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu; + [SDLMenuReplaceUtilities updateIdsOnMenuCells:testMenuCells parentId:ParentIdNotFound]; }); context(@"when the cell is in the top menu", ^{ @@ -360,6 +400,7 @@ @interface SDLMenuCell() context(@"from a shallow list", ^{ beforeEach(^{ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu; + [SDLMenuReplaceUtilities updateIdsOnMenuCells:testMenuCells parentId:ParentIdNotFound]; SDLMenuCell *newCell = [[SDLMenuCell alloc] initWithTitle:@"New Cell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; newCell.cellId = 99; @@ -409,23 +450,30 @@ @interface SDLMenuCell() }); }); - context(@"from a deep list", ^{ + fcontext(@"from a deep list", ^{ + __block SDLMenuCell *subCell = nil; + __block NSMutableArray *newMenu = nil; + beforeEach(^{ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu; + [SDLMenuReplaceUtilities updateIdsOnMenuCells:testMenuCells parentId:ParentIdNotFound]; - SDLMenuCell *subCell = [[SDLMenuCell alloc] initWithTitle:@"New SubCell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - subCell.cellId = 98; - SDLMenuCell *newCell = [[SDLMenuCell alloc] initWithTitle:@"New Cell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[subCell]]; - newCell.cellId = 99; - newCellList = [@[newCell] mutableCopy]; + newMenu = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu.mutableCopy; + NSMutableArray *subMenuToUpdate = newMenu[0].subCells.mutableCopy; + subCell = [[SDLMenuCell alloc] initWithTitle:@"New SubCell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + [subMenuToUpdate insertObject:subCell atIndex:0]; + newMenu[0].subCells = subMenuToUpdate.copy; + + [SDLMenuReplaceUtilities updateIdsOnMenuCells:newMenu parentId:ParentIdNotFound]; }); it(@"should properly add the subcell to the list", ^{ - BOOL didAddCell = [SDLMenuReplaceUtilities addCellWithCellId:98 position:0 fromNewMenuList:newCellList toMainMenuList:testMenuCells]; + BOOL didAddCell = [SDLMenuReplaceUtilities addCellWithCellId:newMenu[0].subCells[0].cellId position:0 fromNewMenuList:newMenu toMainMenuList:testMenuCells]; expect(didAddCell).to(beTrue()); - expect(testMenuCells).to(haveCount(4)); - expect(testMenuCells[0]).to(equal(newCellList[0].subCells[0])); + expect(testMenuCells).to(haveCount(3)); + expect(testMenuCells[0].subCells).to(haveCount(3)); + expect(testMenuCells[0].subCells[0]).to(equal(subCell)); }); }); }); diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m index 2b9bef2b9..acb885b67 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m @@ -49,29 +49,29 @@ @implementation SDLMenuReplaceUtilitiesSpecHelpers SDLArtwork *artwork4 = [[SDLArtwork alloc] initWithData:cellArtData4 name:@"Test Art 4" fileExtension:@"png" persistent:NO]; SDLMenuCell *subList1SubList1Cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 1" icon:nil secondaryArtwork:artwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - subList1SubList1Cell1.cellId = 1; +// subList1SubList1Cell1.cellId = 1; SDLMenuCell *subList1SubList1Cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 2" icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - subList1SubList1Cell2.cellId = 2; +// subList1SubList1Cell2.cellId = 2; NSArray *subList1SubList1 = @[subList1SubList1Cell1, subList1SubList1Cell2]; SDLMenuCell *subList1Cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork4 secondaryArtwork:nil submenuLayout:nil subCells:subList1SubList1]; - subList1Cell1.cellId = 3; +// subList1Cell1.cellId = 3; SDLMenuCell *subList1Cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork2 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - subList1Cell2.cellId = 4; +// subList1Cell2.cellId = 4; NSArray *subList1 = @[subList1Cell1, subList1Cell2]; SDLMenuCell *subList2Cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork4 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - subList2Cell1.cellId = 5; +// subList2Cell1.cellId = 5; SDLMenuCell *subList2Cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork2 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - subList2Cell2.cellId = 6; +// subList2Cell2.cellId = 6; NSArray *subList2 = @[subList2Cell1, subList2Cell2]; SDLMenuCell *topListCell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil submenuLayout:nil subCells:subList1]; - topListCell1.cellId = 7; +// topListCell1.cellId = 7; SDLMenuCell *topListCell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - topListCell2.cellId = 8; +// topListCell2.cellId = 8; SDLMenuCell *topListCell3 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:subList2]; - topListCell3.cellId = 9; +// topListCell3.cellId = 9; return [@[topListCell1, topListCell2, topListCell3] mutableCopy]; } From 2a86bb3bb553c0a3bfbb4db1ce042bb95ddad85e Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 12 Aug 2021 10:06:59 -0400 Subject: [PATCH 087/112] Fix unit tests --- .../DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m index e3a28a796..aba198781 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m @@ -363,7 +363,7 @@ @interface SDLMenuReplaceUtilities () context(@"when the cell is in the submenu", ^{ beforeEach(^{ - testCommandId = 5; + testCommandId = testMenuCells[0].subCells[0].cellId; }); it(@"should return the menu without the cell and return YES", ^{ @@ -372,7 +372,7 @@ @interface SDLMenuReplaceUtilities () expect(foundItem).to(beTrue()); expect(testMutableMenuCells).to(haveCount(3)); - expect(testMutableMenuCells[2].subCells).to(haveCount(1)); + expect(testMutableMenuCells[0].subCells).to(haveCount(1)); }); }); @@ -450,21 +450,22 @@ @interface SDLMenuReplaceUtilities () }); }); - fcontext(@"from a deep list", ^{ + context(@"from a deep list", ^{ __block SDLMenuCell *subCell = nil; __block NSMutableArray *newMenu = nil; beforeEach(^{ - testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu; + testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu.copy; [SDLMenuReplaceUtilities updateIdsOnMenuCells:testMenuCells parentId:ParentIdNotFound]; - newMenu = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu.mutableCopy; + newMenu = [[NSMutableArray alloc] initWithArray:testMenuCells copyItems:YES]; NSMutableArray *subMenuToUpdate = newMenu[0].subCells.mutableCopy; + subCell = [[SDLMenuCell alloc] initWithTitle:@"New SubCell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + subCell.cellId = 98; + subCell.parentCellId = newMenu[0].cellId; [subMenuToUpdate insertObject:subCell atIndex:0]; newMenu[0].subCells = subMenuToUpdate.copy; - - [SDLMenuReplaceUtilities updateIdsOnMenuCells:newMenu parentId:ParentIdNotFound]; }); it(@"should properly add the subcell to the list", ^{ From c312e17ab5f35a718272a28ae3df1fa79c7a2143 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 12 Aug 2021 10:07:46 -0400 Subject: [PATCH 088/112] Compatibility mode needs unique cells --- .../private/SDLMenuReplaceOperation.m | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 5de4a6dd6..22e522336 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -82,23 +82,22 @@ - (void)start { [SDLMenuReplaceUtilities updateIdsOnMenuCells:self.updatedMenu parentId:ParentIdNotFound]; + // Strip the "current menu" and the new menu of properties that are not displayed on the head unit + NSArray *updatedStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.updatedMenu basedOnWindowCapability:self.windowCapability]; + NSArray *currentStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.mutableCurrentMenu basedOnWindowCapability:self.windowCapability]; + + // Generate unique names and ensure that all menus we are tracking have them so that we can properly compare when using the dynamic algorithm + BOOL supportsMenuUniqueness = [[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithMajor:7 minor:1 patch:0]]; + [self.class sdl_generateUniqueNamesForCells:updatedStrippedMenu supportsMenuUniqueness:supportsMenuUniqueness]; + [self.class sdl_applyUniqueNamesOnCells:updatedStrippedMenu toCells:self.updatedMenu]; + [self.class sdl_applyUniqueNamesOnCells:self.currentMenu toCells:currentStrippedMenu]; + SDLDynamicMenuUpdateRunScore *runScore = nil; if (self.compatibilityModeEnabled) { SDLLogV(@"Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); - runScore = [SDLDynamicMenuUpdateAlgorithm compatibilityRunScoreWithOldMenuCells:self.currentMenu updatedMenuCells:self.updatedMenu]; + runScore = [SDLDynamicMenuUpdateAlgorithm compatibilityRunScoreWithOldMenuCells:currentStrippedMenu updatedMenuCells:updatedStrippedMenu]; } else { SDLLogV(@"Dynamic menu update active. Running the algorithm to find the best way to delete / add cells."); - - // Strip the "current menu" and the new menu of properties that are not displayed on the head unit - NSArray *updatedStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.updatedMenu basedOnWindowCapability:self.windowCapability]; - NSArray *currentStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.mutableCurrentMenu basedOnWindowCapability:self.windowCapability]; - - // Generate unique names and ensure that all menus we are tracking have them so that we can properly compare when using the dynamic algorithm - BOOL supportsMenuUniqueness = [[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithMajor:7 minor:1 patch:0]]; - [self.class sdl_generateUniqueNamesForCells:updatedStrippedMenu supportsMenuUniqueness:supportsMenuUniqueness]; - [self.class sdl_applyUniqueNamesOnCells:updatedStrippedMenu toCells:self.updatedMenu]; - [self.class sdl_applyUniqueNamesOnCells:self.currentMenu toCells:currentStrippedMenu]; - runScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:currentStrippedMenu updatedMenuCells:updatedStrippedMenu]; } From 57b3e141823a661033543b4b3c4e37a9d080df02 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 12 Aug 2021 12:05:05 -0400 Subject: [PATCH 089/112] Lots of additional menu replace operation tests --- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 402 ++++++++---------- 1 file changed, 184 insertions(+), 218 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 9fb4a7883..80f988916 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -11,7 +11,9 @@ #import #import +#import "SDLGlobals.h" #import "SDLMenuReplaceOperation.h" +#import "SDLMenuManagerPrivateConstants.h" #import "SDLMenuReplaceUtilities.h" #import "TestConnectionManager.h" @@ -56,6 +58,8 @@ @interface SDLMenuCell () __block SDLMenuReplaceUtilities *mockReplaceUtilities = nil; beforeEach(^{ + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:7 minor:1 patch:0]; + testArtwork = [[SDLArtwork alloc] initWithData:[@"Test data" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO]; testArtwork2 = [[SDLArtwork alloc] initWithData:[@"Test data 2" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name 2" fileExtension:@"png" persistent:NO]; testArtwork3 = [[SDLArtwork alloc] initWithData:[@"Test data 3" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO]; @@ -257,44 +261,125 @@ @interface SDLMenuCell () }); }); - describe(@"updating a menu without dynamic updates", ^{ - context(@"adding a text cell", ^{ - beforeEach(^{ - testCurrentMenu = @[textOnlyCell]; - testNewMenu = @[textOnlyCell, textOnlyCell2]; + context(@"updating a menu without dynamic updates", ^{ + describe(@"basic cell updates", ^{ + context(@"adding a text cell", ^{ + beforeEach(^{ + testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell] copyItems:YES]; + [SDLMenuReplaceUtilities updateIdsOnMenuCells:testCurrentMenu parentId:ParentIdNotFound]; + + testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2] copyItems:YES]; + }); + + it(@"should send a delete and two adds", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + [testConnectionManager respondToLastRequestWithResponse:deleteCommandSuccessResponse]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil]; + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:2 error:nil]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(1)); + expect(adds).to(haveCount(2)); + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(2)); + expect(resultMenuCells[0]).to(equal(textOnlyCell)); + expect(resultMenuCells[1]).to(equal(textOnlyCell2)); + }); }); - it(@"should send a delete and two adds", ^{ - testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; - [testOp start]; + context(@"when all cells remain the same", ^{ + beforeEach(^{ + testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES]; + [SDLMenuReplaceUtilities updateIdsOnMenuCells:testCurrentMenu parentId:ParentIdNotFound]; - [testConnectionManager respondToLastRequestWithResponse:deleteCommandSuccessResponse]; - [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES]; + }); - NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; - NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + it(@"should delete all cells and add the new ones", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; - NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; - NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + [testConnectionManager respondToRequestWithResponse:deleteCommandSuccessResponse requestNumber:0 error:nil]; + [testConnectionManager respondToRequestWithResponse:deleteCommandSuccessResponse requestNumber:1 error:nil]; + [testConnectionManager respondToRequestWithResponse:deleteCommandSuccessResponse requestNumber:2 error:nil]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - expect(deletes).to(haveCount(1)); - expect(adds).to(haveCount(2)); + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:3 error:nil]; + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:4 error:nil]; + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:5 error:nil]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; - [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil]; - [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:2 error:nil]; - [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; - expect(testOp.isFinished).to(beTrue()); - expect(resultError).to(beNil()); - expect(resultMenuCells).to(haveCount(2)); - expect(resultMenuCells[0]).to(equal(textOnlyCell)); - expect(resultMenuCells[1]).to(equal(textOnlyCell2)); + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(3)); + expect(adds).to(haveCount(3)); + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(3)); + }); + }); + }); + + describe(@"unique cell updates", ^{ + context(@"with cell uniqueness", ^{ + beforeEach(^{ + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:7 minor:1 patch:0]; + }); + + context(@"when cells have the same name but are unique", ^{ + + }); + + context(@"when cells are completely identical", ^{ + beforeEach(^{ + testCurrentMenu = @[]; + testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell] copyItems:YES]; + }); + }); + + context(@"when cells are unique but are identical when stripped", ^{ + + }); + }); + + context(@"without cell uniqueness", ^{ + beforeEach(^{ + [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:7 minor:0 patch:0]; + }); + + context(@"when cells have the same name but are unique", ^{ + + }); + + context(@"when cells are completely identical", ^{ + + }); + + context(@"when cells are unique but are identical when stripped", ^{ + + }); }); }); }); - // updating a menu with dynamic updates - describe(@"updating a menu with dynamic updates", ^{ + context(@"updating a menu with dynamic updates", ^{ context(@"adding a text cell", ^{ beforeEach(^{ testCurrentMenu = @[textOnlyCell]; @@ -325,8 +410,10 @@ @interface SDLMenuCell () context(@"rearranging cells with subcells", ^{ beforeEach(^{ - testCurrentMenu = @[textOnlyCell, submenuCell, submenuImageCell]; - testNewMenu = @[submenuCell, submenuImageCell, textOnlyCell]; + testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, submenuCell, submenuImageCell] copyItems:YES]; + [SDLMenuReplaceUtilities updateIdsOnMenuCells:testCurrentMenu parentId:ParentIdNotFound]; + + testNewMenu = [[NSArray alloc] initWithArray:@[submenuCell, submenuImageCell, textOnlyCell] copyItems:YES]; OCMStub([testFileManager uploadArtworks:[OCMArg any] progressHandler:([OCMArg invokeBlockWithArgs:textAndImageCell.icon.name, @1.0, [NSNull null], nil]) completionHandler:([OCMArg invokeBlockWithArgs: @[textAndImageCell.icon.name], [NSNull null], nil])]); }); @@ -362,197 +449,76 @@ @interface SDLMenuCell () expect(resultMenuCells).to(haveCount(3)); }); }); - }); -// -// it(@"should send dynamic deletes first then dynamic adds when removing one submenu cell", ^{ -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; -// NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// -// expect(deletes).to(haveCount(0)); -// expect(subDeletes).to(haveCount(1)); -// expect(adds).to(haveCount(5)); -// expect(submenu).to(haveCount(2)); -// }); -// -// it(@"should send dynamic deletes first then dynamic adds when adding one new cell", ^{ -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell, textOnlyCell2]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// -// expect(deletes).to(haveCount(0)); -// expect(adds).to(haveCount(6)); -// expect(submenu).to(haveCount(2)); -// }); -// -// it(@"should send dynamic deletes first then dynamic adds when cells stay the same", ^{ -// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// expect(deletes).to(haveCount(0)); -// expect(adds).to(haveCount(3)); -// }); -// }); -// -// describe(@"updating when a menu already exists with dynamic updates off", ^{ -// beforeEach(^{ -// testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff; -// OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]); -// }); -// -// it(@"should send deletes first", ^{ -// testManager.menuCells = @[textOnlyCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// expect(deletes).to(haveCount(1)); -// expect(adds).to(haveCount(2)); -// }); -// -// it(@"should deletes first case 2", ^{ -// testManager.menuCells = @[textOnlyCell, textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textAndImageCell, textOnlyCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// expect(deletes).to(haveCount(2)); -// expect(adds).to(haveCount(4)); -// }); -// -// it(@"should send deletes first case 3", ^{ -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; -// NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// -// expect(deletes).to(haveCount(2)); -// expect(subDeletes).to(haveCount(2)); -// expect(adds).to(haveCount(9)); -// expect(submenu).to(haveCount(3)); -// }); -// -// it(@"should send deletes first case 4", ^{ -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, textOnlyCell2]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; -// NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; -// -// NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; -// NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; -// -// expect(deletes).to(haveCount(2)); -// expect(adds).to(haveCount(9)); -// expect(submenu).to(haveCount(2)); -// expect(subDeletes).to(haveCount(1)); -// }); -// -// it(@"should deletes first case 5", ^{ -// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; -// -// NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; -// NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; -// -// NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; -// NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; -// -// expect(deletes).to(haveCount(3)); -// expect(adds).to(haveCount(6)); -// }); -// }); + context(@"removing a cell with subcells", ^{ + beforeEach(^{ + testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell] copyItems:YES]; + [SDLMenuReplaceUtilities updateIdsOnMenuCells:testCurrentMenu parentId:ParentIdNotFound]; + + testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textAndImageCell, submenuCell] copyItems:YES]; + + OCMStub([testFileManager uploadArtworks:[OCMArg any] progressHandler:([OCMArg invokeBlockWithArgs:textAndImageCell.icon.name, @1.0, [NSNull null], nil]) completionHandler:([OCMArg invokeBlockWithArgs: @[textAndImageCell.icon.name], [NSNull null], nil])]); + }); + + it(@"should send one deletion", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + // Delete submenuImageCell + [testConnectionManager respondToLastRequestWithResponse:deleteSubMenuSuccessResponse]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + expect(testOp.currentMenu).toNot(contain(submenuImageCell)); + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; + NSArray *subDeletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; + NSArray *submenu = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; + + expect(deletes).to(haveCount(0)); + expect(subDeletes).to(haveCount(1)); + expect(adds).to(haveCount(0)); + expect(submenu).to(haveCount(0)); + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(3)); + }); + }); + + context(@"when cells remain the same", ^{ + beforeEach(^{ + testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES]; + [SDLMenuReplaceUtilities updateIdsOnMenuCells:testCurrentMenu parentId:ParentIdNotFound]; + + testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES]; + }); + + it(@"should send dynamic deletes first then dynamic adds when cells stay the same", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(0)); + expect(adds).to(haveCount(0)); + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(3)); + }); + }); + }); }); QuickSpecEnd From d0c153190cbd82d6a51f61115b0869358ebdab66 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 12 Aug 2021 14:39:34 -0400 Subject: [PATCH 090/112] Added more tests to replace operation spec --- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 160 ++++++++++++++++-- 1 file changed, 148 insertions(+), 12 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 80f988916..0913d7226 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -21,6 +21,16 @@ @interface SDLMenuCell () @property (assign, nonatomic) UInt32 parentCellId; @property (assign, nonatomic) UInt32 cellId; +@property (strong, nonatomic, readwrite) NSString *uniqueTitle; + +@property (copy, nonatomic, readwrite) NSString *title; +@property (strong, nonatomic, readwrite, nullable) SDLArtwork *icon; +@property (copy, nonatomic, readwrite, nullable) NSArray *voiceCommands; +@property (copy, nonatomic, readwrite, nullable) NSString *secondaryText; +@property (copy, nonatomic, readwrite, nullable) NSString *tertiaryText; +@property (strong, nonatomic, readwrite, nullable) SDLArtwork *secondaryArtwork; +@property (copy, nonatomic, readwrite, nullable) NSArray *subCells; +@property (copy, nonatomic, readwrite, nullable) SDLMenuCellSelectionHandler handler; @end @@ -31,11 +41,19 @@ @interface SDLMenuCell () __block TestConnectionManager *testConnectionManager = nil; __block SDLFileManager *testFileManager = nil; - __block SDLWindowCapability *testWindowCapability = nil; __block SDLMenuConfiguration *testMenuConfiguration = nil; __block NSArray *testCurrentMenu = nil; __block NSArray *testNewMenu = nil; + SDLTextField *commandSecondaryTextField = [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuCommandSecondaryText characterSet:SDLCharacterSetUtf8 width:200 rows:1]; + SDLTextField *commandTertiaryTextField = [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuCommandTertiaryText characterSet:SDLCharacterSetUtf8 width:200 rows:1]; + SDLTextField *submenuSecondaryTextField = [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuSubMenuSecondaryText characterSet:SDLCharacterSetUtf8 width:200 rows:1]; + SDLTextField *submenuTertiaryTextField = [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuSubMenuTertiaryText characterSet:SDLCharacterSetUtf8 width:200 rows:1]; + SDLImageField *commandImageField = [[SDLImageField alloc] initWithName:SDLImageFieldNameCommandIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil]; + SDLImageField *submenuImageField = [[SDLImageField alloc] initWithName:SDLImageFieldNameSubMenuIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil]; + __block SDLWindowCapability *testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:@[commandSecondaryTextField, commandTertiaryTextField, submenuSecondaryTextField, submenuTertiaryTextField] imageFields:@[commandImageField, submenuImageField] imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil keyboardCapabilities:nil]; + __block SDLWindowCapability *testTitleOnlyWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:@[] imageFields:@[commandImageField, submenuImageField] imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil keyboardCapabilities:nil]; + __block SDLArtwork *testArtwork = nil; __block SDLArtwork *testArtwork2 = nil; __block SDLArtwork *testArtwork3 = nil; @@ -88,9 +106,6 @@ @interface SDLMenuCell () testConnectionManager = [[TestConnectionManager alloc] init]; testFileManager = OCMClassMock([SDLFileManager class]); - SDLImageField *commandImageField = [[SDLImageField alloc] initWithName:SDLImageFieldNameCommandIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil]; - SDLImageField *submenuImageField = [[SDLImageField alloc] initWithName:SDLImageFieldNameSubMenuIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil]; - testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:@[commandImageField, submenuImageField] imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil keyboardCapabilities:nil]; testMenuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutList defaultSubmenuLayout:SDLMenuLayoutList]; testCurrentMenu = @[]; testNewMenu = nil; @@ -343,19 +358,78 @@ @interface SDLMenuCell () [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:7 minor:1 patch:0]; }); - context(@"when cells have the same name but are unique", ^{ + context(@"when cells have the same title but are unique", ^{ + beforeEach(^{ + testCurrentMenu = @[]; + + SDLMenuCell *textOnlyCellDupe = [textOnlyCell copy]; + textOnlyCellDupe.secondaryText = @"Secondary Text"; + + testNewMenu = @[textOnlyCell, textOnlyCellDupe]; + }); + + it(@"should send the cells unchanged", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(0)); + expect(adds).to(haveCount(2)); + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:0 error:nil]; + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(2)); + expect(resultMenuCells[0].uniqueTitle).to(equal(textOnlyCell.uniqueTitle)); + expect(resultMenuCells[0].secondaryText).to(beNil()); + expect(resultMenuCells[1].uniqueTitle).to(equal(textOnlyCell.uniqueTitle)); + expect(resultMenuCells[1].secondaryText).toNot(beNil()); + }); }); - context(@"when cells are completely identical", ^{ + context(@"when cells are unique but are identical when stripped", ^{ beforeEach(^{ testCurrentMenu = @[]; - testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell] copyItems:YES]; + + SDLMenuCell *textOnlyCellDupe = [textOnlyCell copy]; + textOnlyCellDupe.secondaryText = @"Secondary Text"; + + testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCellDupe] copyItems:YES]; }); - }); - context(@"when cells are unique but are identical when stripped", ^{ + it(@"should change the second cell's title", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testTitleOnlyWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(0)); + expect(adds).to(haveCount(2)); + + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:0 error:nil]; + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(2)); + expect(resultMenuCells[0].uniqueTitle).to(equal(textOnlyCell.uniqueTitle)); + expect(resultMenuCells[0].secondaryText).to(beNil()); + expect(resultMenuCells[1].uniqueTitle).toNot(equal(textOnlyCell.uniqueTitle)); + expect(resultMenuCells[1].secondaryText).toNot(beNil()); + }); }); }); @@ -364,16 +438,78 @@ @interface SDLMenuCell () [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:7 minor:0 patch:0]; }); - context(@"when cells have the same name but are unique", ^{ + context(@"when cells have the same title but are unique", ^{ + beforeEach(^{ + testCurrentMenu = @[]; + + SDLMenuCell *textOnlyCellDupe = [textOnlyCell copy]; + textOnlyCellDupe.secondaryText = @"Secondary Text"; - }); + testNewMenu = @[textOnlyCell, textOnlyCellDupe]; + }); - context(@"when cells are completely identical", ^{ + it(@"should change the second cell's title", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(0)); + expect(adds).to(haveCount(2)); + + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:0 error:nil]; + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(2)); + expect(resultMenuCells[0].uniqueTitle).to(equal(textOnlyCell.uniqueTitle)); + expect(resultMenuCells[0].secondaryText).to(beNil()); + expect(resultMenuCells[1].uniqueTitle).toNot(equal(textOnlyCell.uniqueTitle)); + expect(resultMenuCells[1].secondaryText).toNot(beNil()); + }); }); context(@"when cells are unique but are identical when stripped", ^{ + beforeEach(^{ + testCurrentMenu = @[]; + + SDLMenuCell *textOnlyCellDupe = [textOnlyCell copy]; + textOnlyCellDupe.secondaryText = @"Secondary Text"; + + testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCellDupe] copyItems:YES]; + }); + + it(@"should change the second cell's title", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testTitleOnlyWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + expect(deletes).to(haveCount(0)); + expect(adds).to(haveCount(2)); + + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:0 error:nil]; + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(2)); + expect(resultMenuCells[0].uniqueTitle).to(equal(textOnlyCell.uniqueTitle)); + expect(resultMenuCells[0].secondaryText).to(beNil()); + expect(resultMenuCells[1].uniqueTitle).toNot(equal(textOnlyCell.uniqueTitle)); + expect(resultMenuCells[1].secondaryText).toNot(beNil()); + }); }); }); }); From 540f08253263d51a48cf752f6748779e9dcd484e Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 13 Aug 2021 10:22:55 -0400 Subject: [PATCH 091/112] No need to apply unique names on current menu --- SmartDeviceLink/private/SDLMenuReplaceOperation.m | 1 - 1 file changed, 1 deletion(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 22e522336..25c3d9b0c 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -90,7 +90,6 @@ - (void)start { BOOL supportsMenuUniqueness = [[SDLGlobals sharedGlobals].rpcVersion isGreaterThanOrEqualToVersion:[SDLVersion versionWithMajor:7 minor:1 patch:0]]; [self.class sdl_generateUniqueNamesForCells:updatedStrippedMenu supportsMenuUniqueness:supportsMenuUniqueness]; [self.class sdl_applyUniqueNamesOnCells:updatedStrippedMenu toCells:self.updatedMenu]; - [self.class sdl_applyUniqueNamesOnCells:self.currentMenu toCells:currentStrippedMenu]; SDLDynamicMenuUpdateRunScore *runScore = nil; if (self.compatibilityModeEnabled) { From 1da1b9ddad80f4d08ee51798b039a8eeff5012f6 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 13 Aug 2021 10:23:03 -0400 Subject: [PATCH 092/112] Update menu manager spec --- .../DevAPISpecs/SDLMenuManagerSpec.m | 48 +++++++------------ 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index f0623ab49..296c4c933 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -67,7 +67,6 @@ @interface SDLMenuManager() testManager.windowCapability = windowCapability; }); - // should instantiate correctly it(@"should instantiate correctly", ^{ expect(testManager.menuCells).to(beEmpty()); @@ -83,7 +82,6 @@ @interface SDLMenuManager() expect(testManager.currentMenuConfiguration).to(beNil()); }); - // when the manager stops describe(@"when the manager stops", ^{ beforeEach(^{ [testManager stop]; @@ -105,14 +103,13 @@ @interface SDLMenuManager() }); }); - // when in HMI NONE context(@"when in HMI NONE", ^{ beforeEach(^{ SDLOnHMIStatus *noneStatus = [[SDLOnHMIStatus alloc] initWithHMILevel:SDLHMILevelNone systemContext:SDLSystemContextMain audioStreamingState:SDLAudioStreamingStateNotAudible videoStreamingState:nil windowID:nil]; [[NSNotificationCenter defaultCenter] postNotification:[[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:noneStatus]]; }); - it(@"should not update", ^{ + it(@"should not suspend the transaction queue", ^{ expect(testManager.transactionQueue.isSuspended).to(beTrue()); }); @@ -127,41 +124,38 @@ @interface SDLMenuManager() [[NSNotificationCenter defaultCenter] postNotification:testSystemContextNotification]; }); - it(@"should update", ^{ + it(@"should run the transaction queue", ^{ expect(testManager.transactionQueue.isSuspended).to(beFalse()); }); }); }); - // when the HMI is ready context(@"when the HMI is ready", ^{ beforeEach(^{ testManager.currentHMILevel = SDLHMILevelFull; testManager.currentSystemContext = SDLSystemContextMain; }); - // updating menu cells - describe(@"updating menu cells", ^{ - // containing duplicate titles + describe(@"setting new menu cells", ^{ context(@"containing duplicate titles", ^{ - it(@"should fail with a duplicate title", ^{ + it(@"should not start an operation", ^{ testManager.menuCells = @[textOnlyCell, textOnlyCell]; - expect(testManager.menuCells).to(beEmpty()); // TODO: Fix, add op count + expect(testManager.menuCells).to(beEmpty()); + expect(testManager.transactionQueue.operationCount).to(equal(0)); }); }); - // containing duplicate VR commands context(@"containing duplicate VR commands", ^{ - __block SDLMenuCell *textAndVRCell1 = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"Cat", @"Turtle"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - __block SDLMenuCell *textAndVRCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"Cat", @"Dog"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + SDLMenuCell *textAndVRCell1 = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"Cat", @"Turtle"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; + SDLMenuCell *textAndVRCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"Cat", @"Dog"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - it(@"should fail when menu items have duplicate vr commands", ^{ + it(@"should not start an operation", ^{ testManager.menuCells = @[textAndVRCell1, textAndVRCell2]; - expect(testManager.menuCells).to(beEmpty()); // TODO Fix, add op count + expect(testManager.menuCells).to(beEmpty()); + expect(testManager.transactionQueue.operationCount).to(equal(0)); }); }); - // if the new menu cells are identical to the old menu cells context(@"if the new menu cells are identical to the old menu cells", ^{ it(@"should only queue one transaction", ^{ testManager.menuCells = @[textOnlyCell]; @@ -172,7 +166,6 @@ @interface SDLMenuManager() }); }); - // when a second menu cells update is queued before the first is done context(@"when a second menu cells update is queued before the first is done", ^{ it(@"should cancel the first operation", ^{ testManager.menuCells = @[textOnlyCell]; @@ -184,7 +177,6 @@ @interface SDLMenuManager() }); }); - // if cells are formed properly context(@"if cells are formed properly", ^{ it(@"should properly prepare and queue the transaction", ^{ testManager.menuCells = @[textOnlyCell]; @@ -204,14 +196,12 @@ @interface SDLMenuManager() }); }); - // updating the menu configuration describe(@"updating the menu configuration", ^{ beforeEach(^{ testManager.currentHMILevel = SDLHMILevelFull; testManager.currentSystemContext = SDLSystemContextMain; }); - // if the connection RPC version is less than 6.0.0 context(@"if the connection RPC version is less than 6.0.0", ^{ beforeEach(^{ [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.0.0"]; @@ -225,7 +215,6 @@ @interface SDLMenuManager() }); }); - // if the connection RPC version is greater than or equal to 6.0.0 context(@"if the connection RPC version is greater than or equal to 6.0.0", ^{ beforeEach(^{ [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"6.0.0"]; @@ -238,7 +227,6 @@ @interface SDLMenuManager() expect(testManager.transactionQueue.operationCount).to(equal(1)); }); - // when queueing a second task after the first context(@"when queueing a second task after the first", ^{ it(@"should cancel the first task", ^{ testManager.menuConfiguration = testMenuConfiguration; @@ -251,14 +239,12 @@ @interface SDLMenuManager() }); }); - // opening the menu describe(@"opening the menu", ^{ beforeEach(^{ testManager.currentHMILevel = SDLHMILevelFull; testManager.currentSystemContext = SDLSystemContextMain; }); - // when open menu RPC can be sent context(@"when open menu RPC can be sent", ^{ beforeEach(^{ SDLVersion *oldVersion = [SDLVersion versionWithMajor:6 minor:0 patch:0]; @@ -293,9 +279,7 @@ @interface SDLMenuManager() }); }); - // when open menu RPC can not be sent context(@"when the open menu RPC can not be sent", ^{ - // should not queue an open menu operation when cell has no subcells it(@"should not queue an open menu operation when cell has no subcells", ^ { BOOL canSendRPC = [testManager openMenu:textOnlyCell]; @@ -303,7 +287,6 @@ @interface SDLMenuManager() expect(canSendRPC).to(equal(NO)); }); - // should not queue an open menu operation when RPC version is not at least 6.0.0 it(@"should not queue an open menu operation when RPC version is not at least 6.0.0", ^ { SDLVersion *oldVersion = [SDLVersion versionWithMajor:5 minor:0 patch:0]; id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]); @@ -315,7 +298,6 @@ @interface SDLMenuManager() expect(canSendRPC).to(equal(NO)); }); - // should not queue an open menu operation when the cell is not in the menu array it(@"should not queue an open menu operation when the cell is not in the menu array", ^ { SDLVersion *oldVersion = [SDLVersion versionWithMajor:6 minor:0 patch:0]; id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]); @@ -330,7 +312,6 @@ @interface SDLMenuManager() }); }); - // running menu cell handlers describe(@"running menu cell handlers", ^{ __block SDLMenuCell *cellWithHandler = nil; __block BOOL cellCalled = NO; @@ -344,13 +325,13 @@ @interface SDLMenuManager() testTriggerSource = nil; }); - // on a main menu cell context(@"on a main menu cell", ^{ beforeEach(^{ cellWithHandler = [[SDLMenuCell alloc] initWithTitle:@"Hello" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { cellCalled = YES; testTriggerSource = triggerSource; }]; + cellWithHandler.cellId = 1; testManager.menuCells = @[cellWithHandler]; }); @@ -368,15 +349,18 @@ @interface SDLMenuManager() }); }); - // on a submenu menu cell context(@"on a submenu menu cell", ^{ beforeEach(^{ cellWithHandler = [[SDLMenuCell alloc] initWithTitle:@"Hello" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { cellCalled = YES; testTriggerSource = triggerSource; }]; + cellWithHandler.cellId = 2; SDLMenuCell *submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Submenu" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:SDLMenuLayoutTiles subCells:@[cellWithHandler]]; + submenuCell.cellId = 1; + + cellWithHandler.parentCellId = 1; testManager.menuCells = @[submenuCell]; }); From 9cdf9c22bc00425296c9607b4cf7f9c8ef982714 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 13 Aug 2021 11:56:09 -0400 Subject: [PATCH 093/112] Temporarily remove things that may not be necessary --- .../SmartDeviceLink-Example-Swift.xcscheme | 78 +++++++++++++++++++ .../private/SDLMenuReplaceOperation.m | 11 +-- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 2 +- 3 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-Swift.xcscheme diff --git a/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-Swift.xcscheme b/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-Swift.xcscheme new file mode 100644 index 000000000..9d72e5789 --- /dev/null +++ b/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-Swift.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 25c3d9b0c..612c9ce41 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -111,7 +111,7 @@ - (void)start { NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. - [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; +// [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; // Upload the artworks, then we will start updating the main menu __weak typeof(self) weakSelf = self; @@ -181,10 +181,11 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; - NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; - NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; - - [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; + // TODO: These will be necessary once we do subcells of subcells +// NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; +// NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; +// +// [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 0913d7226..d83e9615b 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -120,7 +120,7 @@ @interface SDLMenuCell () mockReplaceUtilities = OCMClassMock([SDLMenuReplaceUtilities class]); }); - describe(@"sending initial batch of cells", ^{ + context(@"sending initial batch of cells", ^{ context(@"when setting no cells", ^{ it(@"should finish without doing anything", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; From 9afd2015f7270bd41293b31dc19688ca0ff8eb75 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 13 Aug 2021 13:33:08 -0400 Subject: [PATCH 094/112] Revert "Temporarily remove things that may not be necessary" This reverts commit 9cdf9c22bc00425296c9607b4cf7f9c8ef982714. --- .../SmartDeviceLink-Example-Swift.xcscheme | 78 ------------------- .../private/SDLMenuReplaceOperation.m | 11 ++- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 2 +- 3 files changed, 6 insertions(+), 85 deletions(-) delete mode 100644 SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-Swift.xcscheme diff --git a/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-Swift.xcscheme b/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-Swift.xcscheme deleted file mode 100644 index 9d72e5789..000000000 --- a/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-Swift.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 612c9ce41..25c3d9b0c 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -111,7 +111,7 @@ - (void)start { NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. -// [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; + [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; // Upload the artworks, then we will start updating the main menu __weak typeof(self) weakSelf = self; @@ -181,11 +181,10 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; - // TODO: These will be necessary once we do subcells of subcells -// NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; -// NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; -// -// [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; + NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; + NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; + + [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index d83e9615b..0913d7226 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -120,7 +120,7 @@ @interface SDLMenuCell () mockReplaceUtilities = OCMClassMock([SDLMenuReplaceUtilities class]); }); - context(@"sending initial batch of cells", ^{ + describe(@"sending initial batch of cells", ^{ context(@"when setting no cells", ^{ it(@"should finish without doing anything", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; From 5f0c618a47239939cdcd171d64052a2f896b7ae3 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 13 Aug 2021 13:38:39 -0400 Subject: [PATCH 095/112] Revert "Revert "Temporarily remove things that may not be necessary"" This reverts commit 9afd2015f7270bd41293b31dc19688ca0ff8eb75. --- .../SmartDeviceLink-Example-Swift.xcscheme | 78 +++++++++++++++++++ .../private/SDLMenuReplaceOperation.m | 11 +-- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 2 +- 3 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-Swift.xcscheme diff --git a/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-Swift.xcscheme b/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-Swift.xcscheme new file mode 100644 index 000000000..9d72e5789 --- /dev/null +++ b/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-Swift.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 25c3d9b0c..612c9ce41 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -111,7 +111,7 @@ - (void)start { NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. - [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; +// [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; // Upload the artworks, then we will start updating the main menu __weak typeof(self) weakSelf = self; @@ -181,10 +181,11 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; - NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; - NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; - - [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; + // TODO: These will be necessary once we do subcells of subcells +// NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; +// NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; +// +// [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; __weak typeof(self) weakself = self; [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 0913d7226..d83e9615b 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -120,7 +120,7 @@ @interface SDLMenuCell () mockReplaceUtilities = OCMClassMock([SDLMenuReplaceUtilities class]); }); - describe(@"sending initial batch of cells", ^{ + context(@"sending initial batch of cells", ^{ context(@"when setting no cells", ^{ it(@"should finish without doing anything", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; From f59205a2c9e22cad365423f6833d427717cd5b98 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 17 Aug 2021 08:43:56 -0400 Subject: [PATCH 096/112] Let the menu algorithm determine equality --- SmartDeviceLink/private/SDLMenuManager.m | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 3d7047eb3..8d9a6d2ed 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -185,10 +185,7 @@ - (void)setMenuConfiguration:(SDLMenuConfiguration *)menuConfiguration { } - (void)setMenuCells:(NSArray *)menuCells { - if ([self.menuCells isEqualToArray:menuCells]) { - SDLLogD(@"The set menu cells are identical to previously set menu cells. Skipping..."); - return; - } else if (![self sdl_menuCellsAreUnique:menuCells allVoiceCommands:[NSMutableArray array]]) { + if (![self sdl_menuCellsAreUnique:menuCells allVoiceCommands:[NSMutableArray array]]) { SDLLogE(@"Not all set menu cells are unique, but that is required"); return; } From f49850b3f53651d5e7c559e5af4342f03dbe29de Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 17 Aug 2021 08:54:03 -0400 Subject: [PATCH 097/112] In progress menu test --- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index d83e9615b..ee00e4a7a 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -62,6 +62,7 @@ @interface SDLMenuCell () __block SDLMenuCell *textOnlyCell2 = nil; __block SDLMenuCell *textAndImageCell = nil; __block SDLMenuCell *submenuCell = nil; + __block SDLMenuCell *submenuCellReversed = nil; __block SDLMenuCell *submenuImageCell = nil; __block SDLAddCommandResponse *addCommandSuccessResponse = nil; @@ -69,6 +70,8 @@ @interface SDLMenuCell () __block SDLDeleteCommandResponse *deleteCommandSuccessResponse = nil; __block SDLDeleteSubMenuResponse *deleteSubMenuSuccessResponse = nil; + __block NSMutableArray *basicCellArray = [NSMutableArray array]; + __block NSArray *resultMenuCells = nil; __block NSError *resultError = nil; __block SDLCurrentMenuUpdatedBlock testCurrentMenuUpdatedBlock = nil; @@ -83,11 +86,19 @@ @interface SDLMenuCell () testArtwork3 = [[SDLArtwork alloc] initWithData:[@"Test data 3" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO]; testArtwork3.overwrite = YES; + for (int i = 0; i < 49; i++) { + NSString *cellTitle = [NSString stringWithFormat:@"Cell %@", @(i)]; + [basicCellArray addObject:[[SDLMenuCell alloc] initWithTitle:cellTitle secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[cellTitle] handler:^(SDLTriggerSource _Nonnull triggerSource) { + NSLog(@"%@ pressed", cellTitle); + }]]; + } + textOnlyCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; textOnlyCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 5" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:testArtwork secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; - submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[textOnlyCell, textAndImageCell]]; - submenuImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 4" secondaryText:nil tertiaryText:nil icon:testArtwork2 secondaryArtwork:nil submenuLayout:SDLMenuLayoutTiles subCells:@[textOnlyCell]]; + submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Cell with Subcells" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:basicCellArray]; + submenuCellReversed = [[SDLMenuCell alloc] initWithTitle:@"Cell with Subcells" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:basicCellArray.reverseObjectEnumerator.allObjects]; + submenuImageCell = [[SDLMenuCell alloc] initWithTitle:@"Cell with Image and Subcell" secondaryText:nil tertiaryText:nil icon:testArtwork2 secondaryArtwork:nil submenuLayout:SDLMenuLayoutTiles subCells:@[textOnlyCell]]; addCommandSuccessResponse = [[SDLAddCommandResponse alloc] init]; addCommandSuccessResponse.success = @YES; @@ -586,6 +597,61 @@ @interface SDLMenuCell () }); }); + context(@"rearranging cells and their subcells", ^{ + beforeEach(^{ + testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textAndImageCell, submenuCell] copyItems:YES]; + [SDLMenuReplaceUtilities updateIdsOnMenuCells:testCurrentMenu parentId:ParentIdNotFound]; + + testNewMenu = [[NSArray alloc] initWithArray:@[submenuCellReversed, textAndImageCell, textOnlyCell] copyItems:YES]; + + OCMStub([testFileManager uploadArtworks:[OCMArg any] progressHandler:([OCMArg invokeBlockWithArgs:textAndImageCell.icon.name, @1.0, [NSNull null], nil]) completionHandler:([OCMArg invokeBlockWithArgs: @[textAndImageCell.icon.name], [NSNull null], nil])]); + }); + + fit(@"should sent the correct deletions and additions", ^{ + testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; + [testOp start]; + + // Delete textOnlyCell and textAndImageCell + expect(testConnectionManager.receivedRequests).to(haveCount(2)); + expect(testConnectionManager.receivedRequests[0]).to(beAnInstanceOf(SDLDeleteCommand.class)); + expect(testConnectionManager.receivedRequests[1]).to(beAnInstanceOf(SDLDeleteCommand.class)); + + [testConnectionManager respondToRequestWithResponse:deleteCommandSuccessResponse requestNumber:0 error:nil]; + [testConnectionManager respondToRequestWithResponse:deleteSubMenuSuccessResponse requestNumber:1 error:nil]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + expect(testOp.currentMenu).to(haveCount(1)); + + // Main Menu Add Command / Add Submenu + expect(testConnectionManager.receivedRequests).to(haveCount(4)); + + [testConnectionManager respondToRequestWithResponse:addSubMenuSuccessResponse requestNumber:2 error:nil]; + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:3 error:nil]; + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + expect(testOp.currentMenu).to(haveCount(3)); + + NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]]; + NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate]; + + NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]]; + NSArray *subDeletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate]; + + NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]]; + NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate]; + + NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; + NSArray *submenu = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; + + expect(deletes).to(haveCount(1)); + expect(subDeletes).to(haveCount(1)); + expect(adds).to(haveCount(51)); + expect(submenu).to(haveCount(1)); + + expect(testOp.isFinished).to(beTrue()); + expect(resultError).to(beNil()); + expect(resultMenuCells).to(haveCount(3)); + }); + }); + context(@"removing a cell with subcells", ^{ beforeEach(^{ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell] copyItems:YES]; From 61dd2f7661b2f92932fc7c99ade542400151f847 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 23 Aug 2021 14:43:00 -0400 Subject: [PATCH 098/112] Lots of menu subcell update fixes * Move transfer cell id method to menu replace utilities * Rename `updateIdsOnMenuCells` to `addIdsToMenuCells` on SDLMenuReplaceUtilities * Update menu replace operation: * Re-add transferring Ids on keeps * Update names of delete / add menu cell methods * Add menu cell method now doesn't send submenus --- .../private/SDLMenuReplaceOperation.m | 65 +++++++++---------- .../private/SDLMenuReplaceUtilities.h | 4 +- .../private/SDLMenuReplaceUtilities.m | 20 +++++- 3 files changed, 51 insertions(+), 38 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 612c9ce41..5d561d379 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -80,7 +80,7 @@ - (void)start { [super start]; if (self.isCancelled) { return; } - [SDLMenuReplaceUtilities updateIdsOnMenuCells:self.updatedMenu parentId:ParentIdNotFound]; + [SDLMenuReplaceUtilities addIdsToMenuCells:self.updatedMenu parentId:ParentIdNotFound]; // Strip the "current menu" and the new menu of properties that are not displayed on the head unit NSArray *updatedStrippedMenu = [self.class sdl_cellsWithRemovedPropertiesFromCells:self.updatedMenu basedOnWindowCapability:self.windowCapability]; @@ -111,7 +111,7 @@ - (void)start { NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. -// [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; + [SDLMenuReplaceUtilities transferCellIDsFromCells:oldKeeps toCells:newKeeps]; // Upload the artworks, then we will start updating the main menu __weak typeof(self) weakSelf = self; @@ -157,10 +157,10 @@ - (void)sdl_uploadMenuArtworksWithCompletionHandler:(void(^)(NSError *_Nullable /// @param handler A handler called when complete - (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells cellsToAdd:(NSArray *)addCells completionHandler:(void(^)(NSError *_Nullable error))handler { __weak typeof(self) weakSelf = self; - [self sdl_sendDeleteCurrentMenu:deleteCells withCompletionHandler:^(NSError * _Nullable error) { + [self sdl_sendDeleteMenuCells:deleteCells withCompletionHandler:^(NSError * _Nullable error) { __strong typeof(weakSelf) strongSelf = weakSelf; if (strongSelf.isCancelled) { return handler(error); } - [strongSelf sdl_sendNewMenuCells:addCells oldMenu:strongSelf.currentMenu withCompletionHandler:^(NSError * _Nullable error) { + [strongSelf sdl_sendAddMenuCells:addCells withCompletionHandler:^(NSError * _Nullable error) { handler(error); }]; }]; @@ -188,11 +188,11 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells // [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; __weak typeof(self) weakself = self; - [self sdl_sendDeleteCurrentMenu:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { + [self sdl_sendDeleteMenuCells:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { if (weakself.isCancelled) { return completionHandler([NSError sdl_menuManager_replaceOperationCancelled]); } if (error != nil) { return completionHandler(error); } - [weakself sdl_sendNewMenuCells:cellsToAdd oldMenu:weakself.currentMenu[startIndex].subCells withCompletionHandler:^(NSError * _Nullable error) { + [weakself sdl_sendAddMenuCells:cellsToAdd withCompletionHandler:^(NSError * _Nullable error) { if (weakself.isCancelled) { return completionHandler([NSError sdl_menuManager_replaceOperationCancelled]); } if (error != nil) { return completionHandler(error); } @@ -215,7 +215,7 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells /// Send Delete RPCs for given menu cells /// @param deleteMenuCells The menu cells to be deleted /// @param completionHandler A handler called when the RPCs are finished with an error if any failed -- (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuCells withCompletionHandler:(void(^)(NSError *_Nullable error))completionHandler { +- (void)sdl_sendDeleteMenuCells:(nullable NSArray *)deleteMenuCells withCompletionHandler:(void(^)(NSError *_Nullable error))completionHandler { if (deleteMenuCells.count == 0) { return completionHandler(nil); } __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; @@ -241,16 +241,15 @@ - (void)sdl_sendDeleteCurrentMenu:(nullable NSArray *)deleteMenuC /// Send Add RPCs for given new menu cells compared to old menu cells /// @param newMenuCells The new menu cells we want displayed -/// @param oldMenu The old menu cells we no longer want displayed /// @param completionHandler A handler called when the RPCs are finished with an error if any failed -- (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSArray *)oldMenu withCompletionHandler:(void(^)(NSError *_Nullable error))completionHandler { +- (void)sdl_sendAddMenuCells:(NSArray *)newMenuCells withCompletionHandler:(void(^)(NSError *_Nullable error))completionHandler { if (self.updatedMenu.count == 0 || newMenuCells.count == 0) { SDLLogV(@"There are no cells to update."); return completionHandler(nil); } NSArray *mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells fileManager:self.fileManager usingIndexesFrom:self.updatedMenu windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout];; - NSArray *subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells fileManager:self.fileManager windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; +// NSArray *subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells fileManager:self.fileManager windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; __weak typeof(self) weakSelf = self; @@ -267,26 +266,29 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA if (!success) { SDLLogE(@"Failed to send main menu commands: %@", errors); return completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); + } else { + SDLLogD(@"Finished sending new cells"); + return completionHandler(nil); } - [weakSelf.connectionManager sendRequests:subMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { - if (error != nil) { - errors[request] = error; - } else { - // Find the id of the successful request and add it from the current menu list wherever it needs to be - UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; - UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; - [SDLMenuReplaceUtilities addCellWithCellId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; - } - } completionHandler:^(BOOL success) { - if (!success) { - SDLLogE(@"Failed to send sub menu commands: %@", errors); - return completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); - } - - SDLLogD(@"Finished updating menu"); - completionHandler(nil); - }]; +// [weakSelf.connectionManager sendRequests:subMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { +// if (error != nil) { +// errors[request] = error; +// } else { +// // Find the id of the successful request and add it from the current menu list wherever it needs to be +// UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; +// UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; +// [SDLMenuReplaceUtilities addCellWithCellId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; +// } +// } completionHandler:^(BOOL success) { +// if (!success) { +// SDLLogE(@"Failed to send sub menu commands: %@", errors); +// return completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); +// } +// +// SDLLogD(@"Finished updating menu"); +// completionHandler(nil); +// }]; }]; } @@ -337,13 +339,6 @@ - (void)sdl_sendNewMenuCells:(NSArray *)newMenuCells oldMenu:(NSA return [keepMenuCells copy]; } -- (void)sdl_transferCellIDsFromOldCells:(NSArray *)oldCells toKeptCells:(NSArray *)newCells { - if (oldCells.count == 0) { return; } - for (NSUInteger i = 0; i < newCells.count; i++) { - newCells[i].cellId = oldCells[i].cellId; - } -} - #pragma mark - Menu Uniqueness + (NSArray *)sdl_cellsWithRemovedPropertiesFromCells:(NSArray *)menuCells basedOnWindowCapability:(SDLWindowCapability *)windowCapability { diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index 35defac10..11896031f 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -23,7 +23,9 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Ids -+ (void)updateIdsOnMenuCells:(NSArray *)menuCells parentId:(UInt32)parentId; ++ (void)addIdsToMenuCells:(NSArray *)menuCells parentId:(UInt32)parentId; + ++ (void)transferCellIDsFromCells:(NSArray *)fromCells toCells:(NSArray *)toCells; #pragma mark - Artworks diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index 22ab00762..b71488a9a 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -53,14 +53,30 @@ + (UInt32)nextMenuId { /// Assign cell ids on an array of menu cells given a parent id (or no parent id) /// @param menuCells The array of menu cells to update /// @param parentId The parent id to assign if needed -+ (void)updateIdsOnMenuCells:(NSArray *)menuCells parentId:(UInt32)parentId { ++ (void)addIdsToMenuCells:(NSArray *)menuCells parentId:(UInt32)parentId { for (SDLMenuCell *cell in menuCells) { cell.cellId = self.class.nextMenuId; if (parentId != ParentIdNotFound) { cell.parentCellId = parentId; } if (cell.subCells.count > 0) { - [self updateIdsOnMenuCells:cell.subCells parentId:cell.cellId]; + [self addIdsToMenuCells:cell.subCells parentId:cell.cellId]; + } + } +} + ++ (void)transferCellIDsFromCells:(NSArray *)fromCells toCells:(NSArray *)toCells { + if (fromCells.count == 0 || fromCells.count != toCells.count) { return; } + for (NSUInteger i = 0; i < toCells.count; i++) { + toCells[i].cellId = fromCells[i].cellId; + } + + // Update parent ids + for (SDLMenuCell *cell in toCells) { + if (cell.subCells == nil) { continue; } + + for (SDLMenuCell *subCell in cell.subCells) { + subCell.parentCellId = cell.cellId; } } } From 1797fe4f4e05ce58fb7e56bf16fd1a61ac2d199a Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 23 Aug 2021 15:22:10 -0400 Subject: [PATCH 099/112] Menu replace operation fixes * Many method name clarification updates * Hopefully fix mis-using global updatedMenu when submenus are in view * Re-enable sending submenus for add cells that are not submenus of keeps --- .../private/SDLMenuReplaceOperation.m | 76 ++++++++++--------- .../private/SDLMenuReplaceUtilities.h | 2 +- .../private/SDLMenuReplaceUtilities.m | 2 +- 3 files changed, 41 insertions(+), 39 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 5d561d379..b295fe50f 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -125,7 +125,7 @@ - (void)start { if (strongSelf.isCancelled) { return [strongSelf finishOperation]; } if (error != nil) { return [strongSelf finishOperationWithError:error]; } - [strongSelf sdl_updateSubMenuWithOldKeptCells:oldKeeps newKeptCells:newKeeps atIndex:0 completionHandler:^(NSError * _Nullable error) { + [strongSelf sdl_updateSubMenuWithOldKeptCells:oldKeeps newKeptCells:newKeeps keptCellIndex:0 completionHandler:^(NSError * _Nullable error) { return [strongSelf finishOperationWithError:error]; }]; }]; @@ -160,7 +160,7 @@ - (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells ce [self sdl_sendDeleteMenuCells:deleteCells withCompletionHandler:^(NSError * _Nullable error) { __strong typeof(weakSelf) strongSelf = weakSelf; if (strongSelf.isCancelled) { return handler(error); } - [strongSelf sdl_sendAddMenuCells:addCells withCompletionHandler:^(NSError * _Nullable error) { + [strongSelf sdl_sendAddMenuCells:addCells withPositionsFromMenu:self.updatedMenu completionHandler:^(NSError * _Nullable error) { handler(error); }]; }]; @@ -169,17 +169,18 @@ - (void)sdl_updateMenuWithCellsToDelete:(NSArray *)deleteCells ce /// Takes the submenu cells that are old keeps and new keeps and determines which cells need to be deleted or added /// @param oldKeptCells The old kept cells /// @param newKeptCells The new kept cells -/// @param startIndex The index of the main menu to use -- (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells newKeptCells:(NSArray *)newKeptCells atIndex:(NSUInteger)startIndex completionHandler:(void(^)(NSError *_Nullable error))completionHandler { - if (oldKeptCells.count == 0 || startIndex >= oldKeptCells.count) { return completionHandler(nil); } +/// @param index The index of the main menu to use +/// @param completionHandler The handler to call when all submenu updates are complete +- (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells newKeptCells:(NSArray *)newKeptCells keptCellIndex:(NSUInteger)index completionHandler:(void(^)(NSError *_Nullable error))completionHandler { + if (oldKeptCells.count == 0 || index >= oldKeptCells.count) { return completionHandler(nil); } - if (oldKeptCells[startIndex].subCells.count > 0) { - SDLDynamicMenuUpdateRunScore *tempScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:oldKeptCells[startIndex].subCells updatedMenuCells:newKeptCells[startIndex].subCells]; + if (oldKeptCells[index].subCells.count > 0) { + SDLDynamicMenuUpdateRunScore *tempScore = [SDLDynamicMenuUpdateAlgorithm dynamicRunScoreOldMenuCells:oldKeptCells[index].subCells updatedMenuCells:newKeptCells[index].subCells]; NSArray *deleteMenuStatus = tempScore.oldStatus; NSArray *addMenuStatus = tempScore.updatedStatus; - NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; - NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; + NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:oldKeptCells[index].subCells basedOnStatusList:deleteMenuStatus]; + NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:newKeptCells[index].subCells basedOnStatusList:addMenuStatus]; // TODO: These will be necessary once we do subcells of subcells // NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; @@ -192,19 +193,19 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells if (weakself.isCancelled) { return completionHandler([NSError sdl_menuManager_replaceOperationCancelled]); } if (error != nil) { return completionHandler(error); } - [weakself sdl_sendAddMenuCells:cellsToAdd withCompletionHandler:^(NSError * _Nullable error) { + [weakself sdl_sendAddMenuCells:cellsToAdd withPositionsFromMenu:newKeptCells[index].subCells completionHandler:^(NSError * _Nullable error) { if (weakself.isCancelled) { return completionHandler([NSError sdl_menuManager_replaceOperationCancelled]); } if (error != nil) { return completionHandler(error); } // After the first set of submenu cells were added and deleted we must find the next set of subcells until we loop through all the elements - [weakself sdl_updateSubMenuWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1) completionHandler:^(NSError * _Nullable error) { + [weakself sdl_updateSubMenuWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells keptCellIndex:(index + 1) completionHandler:^(NSError * _Nullable error) { completionHandler(error); }]; }]; }]; } else { // There are no subcells, we can skip to the next index. - [self sdl_updateSubMenuWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells atIndex:(startIndex + 1) completionHandler:^(NSError * _Nullable error) { + [self sdl_updateSubMenuWithOldKeptCells:oldKeptCells newKeptCells:newKeptCells keptCellIndex:(index + 1) completionHandler:^(NSError * _Nullable error) { completionHandler(error); }]; } @@ -240,16 +241,17 @@ - (void)sdl_sendDeleteMenuCells:(nullable NSArray *)deleteMenuCel } /// Send Add RPCs for given new menu cells compared to old menu cells -/// @param newMenuCells The new menu cells we want displayed +/// @param addMenuCells The new menu cells we want displayed +/// @param fullMenu The full menu from which the addMenuCells come. This allows us to grab the positions from that menu for the new cells /// @param completionHandler A handler called when the RPCs are finished with an error if any failed -- (void)sdl_sendAddMenuCells:(NSArray *)newMenuCells withCompletionHandler:(void(^)(NSError *_Nullable error))completionHandler { - if (self.updatedMenu.count == 0 || newMenuCells.count == 0) { +- (void)sdl_sendAddMenuCells:(NSArray *)addMenuCells withPositionsFromMenu:(NSArray *)fullMenu completionHandler:(void(^)(NSError *_Nullable error))completionHandler { + if (addMenuCells.count == 0) { SDLLogV(@"There are no cells to update."); return completionHandler(nil); } - NSArray *mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:newMenuCells fileManager:self.fileManager usingIndexesFrom:self.updatedMenu windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout];; -// NSArray *subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:newMenuCells fileManager:self.fileManager windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + NSArray *mainMenuCommands = [SDLMenuReplaceUtilities mainMenuCommandsForCells:addMenuCells fileManager:self.fileManager usingPositionsFromFullMenu:fullMenu windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; + NSArray *subMenuCommands = [SDLMenuReplaceUtilities subMenuCommandsForCells:addMenuCells fileManager:self.fileManager windowCapability:self.windowCapability defaultSubmenuLayout:self.menuConfiguration.defaultSubmenuLayout]; __block NSMutableDictionary *errors = [NSMutableDictionary dictionary]; __weak typeof(self) weakSelf = self; @@ -260,35 +262,35 @@ - (void)sdl_sendAddMenuCells:(NSArray *)newMenuCells withCompleti // Find the id of the successful request and add it from the current menu list wherever it needs to be UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; - [SDLMenuReplaceUtilities addCellWithCellId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; + [SDLMenuReplaceUtilities addCellWithCellId:commandId position:position fromNewMenuList:addMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; } } completionHandler:^(BOOL success) { if (!success) { SDLLogE(@"Failed to send main menu commands: %@", errors); return completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); - } else { + } else if (subMenuCommands.count == 0) { SDLLogD(@"Finished sending new cells"); return completionHandler(nil); } -// [weakSelf.connectionManager sendRequests:subMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { -// if (error != nil) { -// errors[request] = error; -// } else { -// // Find the id of the successful request and add it from the current menu list wherever it needs to be -// UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; -// UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; -// [SDLMenuReplaceUtilities addCellWithCellId:commandId position:position fromNewMenuList:newMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; -// } -// } completionHandler:^(BOOL success) { -// if (!success) { -// SDLLogE(@"Failed to send sub menu commands: %@", errors); -// return completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); -// } -// -// SDLLogD(@"Finished updating menu"); -// completionHandler(nil); -// }]; + [weakSelf.connectionManager sendRequests:subMenuCommands progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) { + if (error != nil) { + errors[request] = error; + } else { + // Find the id of the successful request and add it from the current menu list wherever it needs to be + UInt32 commandId = [SDLMenuReplaceUtilities commandIdForRPCRequest:request]; + UInt16 position = [SDLMenuReplaceUtilities positionForRPCRequest:request]; + [SDLMenuReplaceUtilities addCellWithCellId:commandId position:position fromNewMenuList:addMenuCells toMainMenuList:weakSelf.mutableCurrentMenu]; + } + } completionHandler:^(BOOL success) { + if (!success) { + SDLLogE(@"Failed to send sub menu commands: %@", errors); + return completionHandler([NSError sdl_menuManager_failedToUpdateWithDictionary:errors]); + } + + SDLLogD(@"Finished sending new cells"); + completionHandler(nil); + }]; }]; } diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index 11896031f..ec78dcf92 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -55,7 +55,7 @@ NS_ASSUME_NONNULL_BEGIN /// @param menu The menu from which we will manage indexes /// @param windowCapability The window capability with which to check available text fields / image fields /// @param defaultSubmenuLayout The default submenu layout to use for displaying submenus -+ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingIndexesFrom:(NSArray *)menu windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; ++ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingPositionsFromFullMenu:(NSArray *)menu windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout; /// Generate SDLAddCommand and SDLAddSubMenu RPCs for the given submenu cells /// @param cells The cells to generate AddCommand / AddSubMenu RPCs for diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index b71488a9a..c2f377529 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -162,7 +162,7 @@ + (UInt16)positionForRPCRequest:(SDLRPCRequest *)request { return [mutableDeletes copy]; } -+ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingIndexesFrom:(NSArray *)menu windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { ++ (NSArray *)mainMenuCommandsForCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager usingPositionsFromFullMenu:(NSArray *)menu windowCapability:(SDLWindowCapability *)windowCapability defaultSubmenuLayout:(SDLMenuLayout)defaultSubmenuLayout { NSMutableArray *mutableCommands = [NSMutableArray array]; for (NSUInteger menuInteger = 0; menuInteger < menu.count; menuInteger++) { for (NSUInteger updateCellsIndex = 0; updateCellsIndex < cells.count; updateCellsIndex++) { From 9ff204a57dfdb9245c780e719b61e2e5f80290d7 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Mon, 23 Aug 2021 15:36:12 -0400 Subject: [PATCH 100/112] Add a log --- SmartDeviceLink/private/SDLMenuManager.m | 1 + SmartDeviceLink/private/SDLMenuReplaceOperation.m | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 8d9a6d2ed..792484a88 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -196,6 +196,7 @@ - (void)setMenuCells:(NSArray *)menuCells { SDLMenuReplaceOperation *menuReplaceOperation = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells compatibilityModeEnabled:(![self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) currentMenuUpdatedHandler:^(NSArray * _Nonnull currentMenuCells, NSError *error) { weakself.currentMenuCells = currentMenuCells; [weakself sdl_updateMenuReplaceOperationsWithNewCurrentMenu]; + SDLLogD(@"Finished updating menu"); }]; // Cancel previous replace menu operations diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index b295fe50f..e50aab451 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -437,7 +437,7 @@ - (void)finishOperationWithError:(nullable NSError *)error { } - (void)finishOperation { - SDLLogV(@"Finishing menu manager dynamic replace operation"); + SDLLogV(@"Finishing menu manager replace operation"); if (self.isCancelled) { self.internalError = [NSError sdl_menuManager_replaceOperationCancelled]; } From fd0c9c5d7031f638b9aa0c1250109218d2ada5c8 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 24 Aug 2021 10:48:24 -0400 Subject: [PATCH 101/112] Transfer menu cell handlers from new cells to old --- SmartDeviceLink/private/SDLMenuReplaceOperation.m | 14 ++++++++------ SmartDeviceLink/private/SDLMenuReplaceUtilities.h | 2 ++ SmartDeviceLink/private/SDLMenuReplaceUtilities.m | 8 ++++++++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index e50aab451..8265ae360 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -110,9 +110,12 @@ - (void)start { NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:runScore.oldStatus]; NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; - // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. + // Old kept cells ids need to be moved to the new kept cells so that submenu changes have correct parent ids [SDLMenuReplaceUtilities transferCellIDsFromCells:oldKeeps toCells:newKeeps]; + // Transfer new cells' handlers to the old cells, which are stored in the current menu + [SDLMenuReplaceUtilities transferCellHandlersFromCells:newKeeps toCells:oldKeeps]; + // Upload the artworks, then we will start updating the main menu __weak typeof(self) weakSelf = self; [self sdl_uploadMenuArtworksWithCompletionHandler:^(NSError * _Nullable error) { @@ -182,11 +185,10 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:oldKeptCells[index].subCells basedOnStatusList:deleteMenuStatus]; NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:newKeptCells[index].subCells basedOnStatusList:addMenuStatus]; - // TODO: These will be necessary once we do subcells of subcells -// NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; -// NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; -// -// [self sdl_transferCellIDsFromOldCells:oldKeeps toKeptCells:newKeeps]; + // Transfer ids from subcell keeps to old subcells, which are stored in the current menu + NSArray *oldSubcellKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; + NSArray *newSubcellKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; + [SDLMenuReplaceUtilities transferCellHandlersFromCells:newSubcellKeeps toCells:oldSubcellKeeps]; __weak typeof(self) weakself = self; [self sdl_sendDeleteMenuCells:cellsToDelete withCompletionHandler:^(NSError * _Nullable error) { diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h index ec78dcf92..4488d0d4b 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.h +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.h @@ -27,6 +27,8 @@ NS_ASSUME_NONNULL_BEGIN + (void)transferCellIDsFromCells:(NSArray *)fromCells toCells:(NSArray *)toCells; ++ (void)transferCellHandlersFromCells:(NSArray *)fromCells toCells:(NSArray *)toCells; + #pragma mark - Artworks /// Finds all artworks that need to be uploaded from the given list of menu cells diff --git a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m index c2f377529..4977a4794 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceUtilities.m +++ b/SmartDeviceLink/private/SDLMenuReplaceUtilities.m @@ -28,6 +28,7 @@ @interface SDLMenuCell () @property (assign, nonatomic) UInt32 parentCellId; @property (assign, nonatomic) UInt32 cellId; @property (copy, nonatomic, readwrite, nullable) NSArray *subCells; +@property (copy, nonatomic, readwrite, nullable) SDLMenuCellSelectionHandler handler; @end @@ -81,6 +82,13 @@ + (void)transferCellIDsFromCells:(NSArray *)fromCells toCells:(NS } } ++ (void)transferCellHandlersFromCells:(NSArray *)fromCells toCells:(NSArray *)toCells { + if (fromCells.count == 0 || fromCells.count != toCells.count) { return; } + for (NSUInteger i = 0; i < toCells.count; i++) { + toCells[i].handler = fromCells[i].handler; + } +} + #pragma mark Artworks + (NSArray *)findAllArtworksToBeUploadedFromCells:(NSArray *)cells fileManager:(SDLFileManager *)fileManager windowCapability:(SDLWindowCapability *)windowCapability { From 829513cee98ff19437074055028e9a7ca85d2024 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 24 Aug 2021 12:04:02 -0400 Subject: [PATCH 102/112] Fix build issues --- SmartDeviceLink/private/SDLMenuReplaceOperation.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index 8265ae360..bc50c8284 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -186,8 +186,8 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:newKeptCells[index].subCells basedOnStatusList:addMenuStatus]; // Transfer ids from subcell keeps to old subcells, which are stored in the current menu - NSArray *oldSubcellKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[startIndex].subCells basedOnStatusList:deleteMenuStatus]; - NSArray *newSubcellKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[startIndex].subCells basedOnStatusList:addMenuStatus]; + NSArray *oldSubcellKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[index].subCells basedOnStatusList:deleteMenuStatus]; + NSArray *newSubcellKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[index].subCells basedOnStatusList:addMenuStatus]; [SDLMenuReplaceUtilities transferCellHandlersFromCells:newSubcellKeeps toCells:oldSubcellKeeps]; __weak typeof(self) weakself = self; From 1e2a7fce31231f8945d2f36f6f70f5cbe87925b0 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 24 Aug 2021 13:22:53 -0400 Subject: [PATCH 103/112] Menu fixes * Fixed menu handlers not using current menu --- SmartDeviceLink/private/SDLMenuManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 792484a88..9bae9f7b8 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -353,7 +353,7 @@ - (BOOL)sdl_callHandlerForCells:(NSArray *)cells command:(SDLOnCo - (void)sdl_commandNotification:(SDLRPCNotificationNotification *)notification { SDLOnCommand *onCommand = (SDLOnCommand *)notification.notification; - [self sdl_callHandlerForCells:self.menuCells command:onCommand]; + [self sdl_callHandlerForCells:self.currentMenuCells command:onCommand]; } - (void)sdl_displayCapabilityDidUpdate { From 7053b9de012c8f2cf736f902d8fe2c0576e27014 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 24 Aug 2021 15:52:18 -0400 Subject: [PATCH 104/112] Update spec helper to remove commented out code --- .../DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m index acb885b67..c6069d315 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m @@ -49,31 +49,22 @@ @implementation SDLMenuReplaceUtilitiesSpecHelpers SDLArtwork *artwork4 = [[SDLArtwork alloc] initWithData:cellArtData4 name:@"Test Art 4" fileExtension:@"png" persistent:NO]; SDLMenuCell *subList1SubList1Cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 1" icon:nil secondaryArtwork:artwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; -// subList1SubList1Cell1.cellId = 1; SDLMenuCell *subList1SubList1Cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 2" icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; -// subList1SubList1Cell2.cellId = 2; NSArray *subList1SubList1 = @[subList1SubList1Cell1, subList1SubList1Cell2]; SDLMenuCell *subList1Cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork4 secondaryArtwork:nil submenuLayout:nil subCells:subList1SubList1]; -// subList1Cell1.cellId = 3; SDLMenuCell *subList1Cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork2 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; -// subList1Cell2.cellId = 4; NSArray *subList1 = @[subList1Cell1, subList1Cell2]; SDLMenuCell *subList2Cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork4 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; -// subList2Cell1.cellId = 5; SDLMenuCell *subList2Cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork2 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; -// subList2Cell2.cellId = 6; NSArray *subList2 = @[subList2Cell1, subList2Cell2]; SDLMenuCell *topListCell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil submenuLayout:nil subCells:subList1]; -// topListCell1.cellId = 7; SDLMenuCell *topListCell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; -// topListCell2.cellId = 8; SDLMenuCell *topListCell3 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:subList2]; -// topListCell3.cellId = 9; - return [@[topListCell1, topListCell2, topListCell3] mutableCopy]; + return @[topListCell1, topListCell2, topListCell3].mutableCopy; } @end From c776099c68bc2e1a7dfeda100e50f55de069d02e Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 24 Aug 2021 15:52:34 -0400 Subject: [PATCH 105/112] Add menu replace utility tests --- .../DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m | 63 ++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m index aba198781..51f6bca6a 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m @@ -48,7 +48,7 @@ @interface SDLMenuReplaceUtilities () SDLMenuReplaceUtilities.nextMenuId = 0; testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu; - [SDLMenuReplaceUtilities updateIdsOnMenuCells:testMenuCells parentId:ParentIdNotFound]; + [SDLMenuReplaceUtilities addIdsToMenuCells:testMenuCells parentId:ParentIdNotFound]; expect(testMenuCells[0].cellId).to(equal(1)); expect(testMenuCells[1].cellId).to(equal(6)); @@ -74,6 +74,55 @@ @interface SDLMenuReplaceUtilities () }); }); +describe(@"transferring cell ids", ^{ + it(@"should properly transfer ids and set parent ids", ^{ + testMenuCells = [[NSMutableArray alloc] initWithArray:SDLMenuReplaceUtilitiesSpecHelpers.deepMenu copyItems:YES]; + [SDLMenuReplaceUtilities addIdsToMenuCells:testMenuCells parentId:ParentIdNotFound]; + + NSArray *toCells = [[NSArray alloc] initWithArray:SDLMenuReplaceUtilitiesSpecHelpers.deepMenu copyItems:YES]; + [SDLMenuReplaceUtilities transferCellIDsFromCells:testMenuCells toCells:toCells]; + + // Top-level cells should have same cell ids + for (NSUInteger i = 0; i < testMenuCells.count; i++) { + expect(toCells[i].cellId).to(equal(testMenuCells[i].cellId)); + } + + // Sub-cells should _not_ have the same cell ids + for (NSUInteger i = 0; i < testMenuCells[0].subCells.count; i++) { + expect(toCells[0].subCells[i].cellId).toNot(equal(testMenuCells[0].subCells[i].cellId)); + } + + // Sub-cells should have proper parent ids + for (NSUInteger i = 0; i < testMenuCells[0].subCells.count; i++) { + expect(toCells[0].subCells[i].parentCellId).to(equal(toCells[0].cellId)); + } + }); +}); + +describe(@"transferring cell handlers", ^{ + __block BOOL cell1HandlerTriggered = NO; + __block BOOL cell2HandlerTriggered = NO; + beforeEach(^{ + cell1HandlerTriggered = NO; + cell2HandlerTriggered = NO; + }); + + it(@"should properly transfer cell handlers", ^{ + SDLMenuCell *cell1 = [[SDLMenuCell alloc] initWithTitle:@"Cell1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { + cell1HandlerTriggered = YES; + }]; + SDLMenuCell *cell2 = [[SDLMenuCell alloc] initWithTitle:@"Cell1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { + cell2HandlerTriggered = YES; + }]; + + [SDLMenuReplaceUtilities transferCellHandlersFromCells:@[cell1] toCells:@[cell2]]; + cell2.handler(SDLTriggerSourceMenu); + + expect(cell1HandlerTriggered).to(beTrue()); + expect(cell2HandlerTriggered).to(beFalse()); + }); +}); + describe(@"finding all artworks from cells", ^{ beforeEach(^{ mockFileManager = OCMClassMock([SDLFileManager class]); @@ -259,7 +308,7 @@ @interface SDLMenuReplaceUtilities () }); it(@"should generate the correct RPCs", ^{ - NSArray *requests = [SDLMenuReplaceUtilities mainMenuCommandsForCells:testMenuCells fileManager:mockFileManager usingIndexesFrom:testMenuCells windowCapability:testWindowCapability defaultSubmenuLayout:testMenuLayout]; + NSArray *requests = [SDLMenuReplaceUtilities mainMenuCommandsForCells:testMenuCells fileManager:mockFileManager usingPositionsFromFullMenu:testMenuCells windowCapability:testWindowCapability defaultSubmenuLayout:testMenuLayout]; expect(requests).to(haveCount(3)); expect(requests[0]).to(beAnInstanceOf(SDLAddCommand.class)); expect(requests[1]).to(beAnInstanceOf(SDLAddCommand.class)); @@ -273,7 +322,7 @@ @interface SDLMenuReplaceUtilities () }); it(@"should generate the correct RPCs", ^{ - NSArray *requests = [SDLMenuReplaceUtilities mainMenuCommandsForCells:testMenuCells fileManager:mockFileManager usingIndexesFrom:testMenuCells windowCapability:testWindowCapability defaultSubmenuLayout:testMenuLayout]; + NSArray *requests = [SDLMenuReplaceUtilities mainMenuCommandsForCells:testMenuCells fileManager:mockFileManager usingPositionsFromFullMenu:testMenuCells windowCapability:testWindowCapability defaultSubmenuLayout:testMenuLayout]; expect(requests).to(haveCount(3)); expect(requests[0]).to(beAnInstanceOf(SDLAddSubMenu.class)); expect(requests[1]).to(beAnInstanceOf(SDLAddCommand.class)); @@ -305,7 +354,7 @@ @interface SDLMenuReplaceUtilities () context(@"from a shallow list", ^{ beforeEach(^{ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu; - [SDLMenuReplaceUtilities updateIdsOnMenuCells:testMenuCells parentId:ParentIdNotFound]; + [SDLMenuReplaceUtilities addIdsToMenuCells:testMenuCells parentId:ParentIdNotFound]; }); context(@"when the cell is in the menu", ^{ @@ -342,7 +391,7 @@ @interface SDLMenuReplaceUtilities () context(@"from a deep list", ^{ beforeEach(^{ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu; - [SDLMenuReplaceUtilities updateIdsOnMenuCells:testMenuCells parentId:ParentIdNotFound]; + [SDLMenuReplaceUtilities addIdsToMenuCells:testMenuCells parentId:ParentIdNotFound]; }); context(@"when the cell is in the top menu", ^{ @@ -400,7 +449,7 @@ @interface SDLMenuReplaceUtilities () context(@"from a shallow list", ^{ beforeEach(^{ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu; - [SDLMenuReplaceUtilities updateIdsOnMenuCells:testMenuCells parentId:ParentIdNotFound]; + [SDLMenuReplaceUtilities addIdsToMenuCells:testMenuCells parentId:ParentIdNotFound]; SDLMenuCell *newCell = [[SDLMenuCell alloc] initWithTitle:@"New Cell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; newCell.cellId = 99; @@ -456,7 +505,7 @@ @interface SDLMenuReplaceUtilities () beforeEach(^{ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu.copy; - [SDLMenuReplaceUtilities updateIdsOnMenuCells:testMenuCells parentId:ParentIdNotFound]; + [SDLMenuReplaceUtilities addIdsToMenuCells:testMenuCells parentId:ParentIdNotFound]; newMenu = [[NSMutableArray alloc] initWithArray:testMenuCells copyItems:YES]; NSMutableArray *subMenuToUpdate = newMenu[0].subCells.mutableCopy; From f8834d18f2a461f0e1d1a3886c056c82264f4a11 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 24 Aug 2021 15:52:44 -0400 Subject: [PATCH 106/112] Fix menu manager tests --- SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index 296c4c933..c663205e2 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -157,12 +157,12 @@ @interface SDLMenuManager() }); context(@"if the new menu cells are identical to the old menu cells", ^{ - it(@"should only queue one transaction", ^{ + it(@"should queue two transactions and let the operation handle not updating", ^{ testManager.menuCells = @[textOnlyCell]; testManager.menuCells = @[textOnlyCell]; expect(testManager.menuCells).to(equal(@[textOnlyCell])); - expect(testManager.transactionQueue.operationCount).to(equal(1)); + expect(testManager.transactionQueue.operationCount).to(equal(2)); }); }); @@ -333,7 +333,7 @@ @interface SDLMenuManager() }]; cellWithHandler.cellId = 1; - testManager.menuCells = @[cellWithHandler]; + testManager.currentMenuCells = @[cellWithHandler]; }); it(@"should call the cell handler", ^{ @@ -362,7 +362,7 @@ @interface SDLMenuManager() cellWithHandler.parentCellId = 1; - testManager.menuCells = @[submenuCell]; + testManager.currentMenuCells = @[submenuCell]; }); it(@"should call the cell handler", ^{ From 5ff681136479bae3a8411cd6c38c99a7d132eeb4 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 24 Aug 2021 15:55:38 -0400 Subject: [PATCH 107/112] Fixes to menu replace operation --- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index ee00e4a7a..927b333b5 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -78,6 +78,15 @@ @interface SDLMenuCell () __block SDLMenuReplaceUtilities *mockReplaceUtilities = nil; + beforeSuite(^{ + for (int i = 0; i < 49; i++) { + NSString *cellTitle = [NSString stringWithFormat:@"Cell %@", @(i)]; + [basicCellArray addObject:[[SDLMenuCell alloc] initWithTitle:cellTitle secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[cellTitle] handler:^(SDLTriggerSource _Nonnull triggerSource) { + NSLog(@"%@ pressed", cellTitle); + }]]; + } + }); + beforeEach(^{ [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:7 minor:1 patch:0]; @@ -86,13 +95,6 @@ @interface SDLMenuCell () testArtwork3 = [[SDLArtwork alloc] initWithData:[@"Test data 3" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO]; testArtwork3.overwrite = YES; - for (int i = 0; i < 49; i++) { - NSString *cellTitle = [NSString stringWithFormat:@"Cell %@", @(i)]; - [basicCellArray addObject:[[SDLMenuCell alloc] initWithTitle:cellTitle secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[cellTitle] handler:^(SDLTriggerSource _Nonnull triggerSource) { - NSLog(@"%@ pressed", cellTitle); - }]]; - } - textOnlyCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; textOnlyCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 5" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:testArtwork secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}]; @@ -272,7 +274,7 @@ @interface SDLMenuCell () NSPredicate *submenuCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; NSArray *submenus = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:submenuCommandPredicate]; - expect(adds).to(haveCount(2)); + expect(adds).to(haveCount(49)); expect(submenus).to(haveCount(1)); [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil]; @@ -292,7 +294,7 @@ @interface SDLMenuCell () context(@"adding a text cell", ^{ beforeEach(^{ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell] copyItems:YES]; - [SDLMenuReplaceUtilities updateIdsOnMenuCells:testCurrentMenu parentId:ParentIdNotFound]; + [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound]; testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2] copyItems:YES]; }); @@ -328,7 +330,7 @@ @interface SDLMenuCell () context(@"when all cells remain the same", ^{ beforeEach(^{ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES]; - [SDLMenuReplaceUtilities updateIdsOnMenuCells:testCurrentMenu parentId:ParentIdNotFound]; + [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound]; testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES]; }); @@ -558,7 +560,7 @@ @interface SDLMenuCell () context(@"rearranging cells with subcells", ^{ beforeEach(^{ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, submenuCell, submenuImageCell] copyItems:YES]; - [SDLMenuReplaceUtilities updateIdsOnMenuCells:testCurrentMenu parentId:ParentIdNotFound]; + [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound]; testNewMenu = [[NSArray alloc] initWithArray:@[submenuCell, submenuImageCell, textOnlyCell] copyItems:YES]; @@ -600,7 +602,7 @@ @interface SDLMenuCell () context(@"rearranging cells and their subcells", ^{ beforeEach(^{ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textAndImageCell, submenuCell] copyItems:YES]; - [SDLMenuReplaceUtilities updateIdsOnMenuCells:testCurrentMenu parentId:ParentIdNotFound]; + [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound]; testNewMenu = [[NSArray alloc] initWithArray:@[submenuCellReversed, textAndImageCell, textOnlyCell] copyItems:YES]; @@ -643,7 +645,7 @@ @interface SDLMenuCell () expect(deletes).to(haveCount(1)); expect(subDeletes).to(haveCount(1)); - expect(adds).to(haveCount(51)); + expect(adds).to(haveCount(50)); expect(submenu).to(haveCount(1)); expect(testOp.isFinished).to(beTrue()); @@ -655,7 +657,7 @@ @interface SDLMenuCell () context(@"removing a cell with subcells", ^{ beforeEach(^{ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell] copyItems:YES]; - [SDLMenuReplaceUtilities updateIdsOnMenuCells:testCurrentMenu parentId:ParentIdNotFound]; + [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound]; testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textAndImageCell, submenuCell] copyItems:YES]; @@ -697,7 +699,7 @@ @interface SDLMenuCell () context(@"when cells remain the same", ^{ beforeEach(^{ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES]; - [SDLMenuReplaceUtilities updateIdsOnMenuCells:testCurrentMenu parentId:ParentIdNotFound]; + [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound]; testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES]; }); From cae62c6718a668e5836e5b5f0f18d2fa84536dcb Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 25 Aug 2021 11:43:18 -0400 Subject: [PATCH 108/112] Fix some more menu replace tests --- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 927b333b5..3b61af06a 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -79,7 +79,7 @@ @interface SDLMenuCell () __block SDLMenuReplaceUtilities *mockReplaceUtilities = nil; beforeSuite(^{ - for (int i = 0; i < 49; i++) { + for (int i = 0; i < 50; i++) { NSString *cellTitle = [NSString stringWithFormat:@"Cell %@", @(i)]; [basicCellArray addObject:[[SDLMenuCell alloc] initWithTitle:cellTitle secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[cellTitle] handler:^(SDLTriggerSource _Nonnull triggerSource) { NSLog(@"%@ pressed", cellTitle); @@ -274,7 +274,7 @@ @interface SDLMenuCell () NSPredicate *submenuCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; NSArray *submenus = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:submenuCommandPredicate]; - expect(adds).to(haveCount(49)); + expect(adds).to(haveCount(50)); expect(submenus).to(haveCount(1)); [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil]; @@ -609,14 +609,14 @@ @interface SDLMenuCell () OCMStub([testFileManager uploadArtworks:[OCMArg any] progressHandler:([OCMArg invokeBlockWithArgs:textAndImageCell.icon.name, @1.0, [NSNull null], nil]) completionHandler:([OCMArg invokeBlockWithArgs: @[textAndImageCell.icon.name], [NSNull null], nil])]); }); - fit(@"should sent the correct deletions and additions", ^{ + it(@"should sent the correct deletions and additions", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; [testOp start]; - // Delete textOnlyCell and textAndImageCell + // Delete textOnlyCell and submenuCell expect(testConnectionManager.receivedRequests).to(haveCount(2)); expect(testConnectionManager.receivedRequests[0]).to(beAnInstanceOf(SDLDeleteCommand.class)); - expect(testConnectionManager.receivedRequests[1]).to(beAnInstanceOf(SDLDeleteCommand.class)); + expect(testConnectionManager.receivedRequests[1]).to(beAnInstanceOf(SDLDeleteSubMenu.class)); [testConnectionManager respondToRequestWithResponse:deleteCommandSuccessResponse requestNumber:0 error:nil]; [testConnectionManager respondToRequestWithResponse:deleteSubMenuSuccessResponse requestNumber:1 error:nil]; @@ -643,11 +643,18 @@ @interface SDLMenuCell () NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]]; NSArray *submenu = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate]; + // Submenu add commands sent expect(deletes).to(haveCount(1)); expect(subDeletes).to(haveCount(1)); - expect(adds).to(haveCount(50)); + expect(adds).to(haveCount(51)); expect(submenu).to(haveCount(1)); + // Respond to all 50 submenu add commands + for (NSUInteger i = 0; i < 50; i++) { + [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:(i + 4) error:nil]; + } + [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES]; + expect(testOp.isFinished).to(beTrue()); expect(resultError).to(beNil()); expect(resultMenuCells).to(haveCount(3)); From 61a48afdd4c586af5fef97638d3e250402167a21 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 25 Aug 2021 12:04:38 -0400 Subject: [PATCH 109/112] Add test for transferring handlers --- .../DevAPISpecs/SDLMenuReplaceOperationSpec.m | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m index 3b61af06a..bb3c662bb 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m @@ -704,14 +704,21 @@ @interface SDLMenuCell () }); context(@"when cells remain the same", ^{ + __block BOOL secondHandlerCalled = NO; + beforeEach(^{ + secondHandlerCalled = NO; + testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES]; [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound]; + textOnlyCell.handler = ^(SDLTriggerSource triggerSource) { + secondHandlerCalled = YES; + }; testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES]; }); - it(@"should send dynamic deletes first then dynamic adds when cells stay the same", ^{ + it(@"should not send deletes or adds, but should transfer handlers", ^{ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock]; [testOp start]; @@ -727,6 +734,9 @@ @interface SDLMenuCell () expect(testOp.isFinished).to(beTrue()); expect(resultError).to(beNil()); expect(resultMenuCells).to(haveCount(3)); + + resultMenuCells[0].handler(SDLTriggerSourceMenu); + expect(secondHandlerCalled).to(beTrue()); }); }); }); From 7aacb05eb27a0624ed1506812a455be2db95bb40 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 8 Sep 2021 13:41:17 -0400 Subject: [PATCH 110/112] Update from review --- .../private/SDLMenuReplaceOperation.m | 26 ++++++------------- SmartDeviceLink/public/SDLMenuCell.m | 4 --- SmartDeviceLink/public/SDLScreenManager.m | 4 +-- 3 files changed, 10 insertions(+), 24 deletions(-) diff --git a/SmartDeviceLink/private/SDLMenuReplaceOperation.m b/SmartDeviceLink/private/SDLMenuReplaceOperation.m index bc50c8284..8f656c8a5 100644 --- a/SmartDeviceLink/private/SDLMenuReplaceOperation.m +++ b/SmartDeviceLink/private/SDLMenuReplaceOperation.m @@ -107,8 +107,8 @@ - (void)start { NSArray *cellsToDelete = [self sdl_filterDeleteMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:runScore.oldStatus]; NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares - NSArray *oldKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:self.currentMenu basedOnStatusList:runScore.oldStatus]; - NSArray *newKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; + NSArray *oldKeeps = [self sdl_filterKeepMenuItems:self.currentMenu basedOnStatusList:runScore.oldStatus]; + NSArray *newKeeps = [self sdl_filterKeepMenuItems:self.updatedMenu basedOnStatusList:runScore.updatedStatus]; // Old kept cells ids need to be moved to the new kept cells so that submenu changes have correct parent ids [SDLMenuReplaceUtilities transferCellIDsFromCells:oldKeeps toCells:newKeeps]; @@ -186,8 +186,8 @@ - (void)sdl_updateSubMenuWithOldKeptCells:(NSArray *)oldKeptCells NSArray *cellsToAdd = [self sdl_filterAddMenuItemsWithNewMenuItems:newKeptCells[index].subCells basedOnStatusList:addMenuStatus]; // Transfer ids from subcell keeps to old subcells, which are stored in the current menu - NSArray *oldSubcellKeeps = [self sdl_filterKeepMenuItemsWithOldMenuItems:oldKeptCells[index].subCells basedOnStatusList:deleteMenuStatus]; - NSArray *newSubcellKeeps = [self sdl_filterKeepMenuItemsWithNewMenuItems:newKeptCells[index].subCells basedOnStatusList:addMenuStatus]; + NSArray *oldSubcellKeeps = [self sdl_filterKeepMenuItems:oldKeptCells[index].subCells basedOnStatusList:deleteMenuStatus]; + NSArray *newSubcellKeeps = [self sdl_filterKeepMenuItems:newKeptCells[index].subCells basedOnStatusList:addMenuStatus]; [SDLMenuReplaceUtilities transferCellHandlersFromCells:newSubcellKeeps toCells:oldSubcellKeeps]; __weak typeof(self) weakself = self; @@ -322,22 +322,12 @@ - (void)sdl_sendAddMenuCells:(NSArray *)addMenuCells withPosition return [addCells copy]; } -- (NSArray *)sdl_filterKeepMenuItemsWithOldMenuItems:(NSArray *)oldMenuCells basedOnStatusList:(NSArray *)keepStatusList { +- (NSArray *)sdl_filterKeepMenuItems:(NSArray *)menuCells basedOnStatusList:(NSArray *)keepStatusList { NSMutableArray *keepMenuCells = [[NSMutableArray alloc] init]; - for (NSUInteger index = 0; index < keepStatusList.count; index++) { - if (keepStatusList[index].unsignedIntegerValue == SDLMenuCellUpdateStateKeep) { - [keepMenuCells addObject:oldMenuCells[index]]; - } - } - return [keepMenuCells copy]; -} - -- (NSArray *)sdl_filterKeepMenuItemsWithNewMenuItems:(NSArray *)newMenuCells basedOnStatusList:(NSArray *)keepStatusList { - NSMutableArray *keepMenuCells = [[NSMutableArray alloc] init]; - for (NSUInteger index = 0; index < keepStatusList.count; index++) { - if (keepStatusList[index].unsignedIntegerValue == SDLMenuCellUpdateStateKeep) { - [keepMenuCells addObject:newMenuCells[index]]; + for (NSUInteger i = 0; i < keepStatusList.count; i++) { + if (keepStatusList[i].unsignedIntegerValue == SDLMenuCellUpdateStateKeep) { + [keepMenuCells addObject:menuCells[i]]; } } return [keepMenuCells copy]; diff --git a/SmartDeviceLink/public/SDLMenuCell.m b/SmartDeviceLink/public/SDLMenuCell.m index a66812032..fb07ef49f 100644 --- a/SmartDeviceLink/public/SDLMenuCell.m +++ b/SmartDeviceLink/public/SDLMenuCell.m @@ -112,16 +112,12 @@ - (BOOL)isEqual:(id)object { } - (BOOL)isEqualToCell:(SDLMenuCell *)cell { - if (cell == nil) { return NO; } - return (self.hash == cell.hash); } #pragma mark With Unique Title - (BOOL)isEqualToCellWithUniqueTitle:(SDLMenuCell *)cell { - if (cell == nil) { return NO; } - return ([self sdl_hashWithUniqueTitle] == [cell sdl_hashWithUniqueTitle]); } diff --git a/SmartDeviceLink/public/SDLScreenManager.m b/SmartDeviceLink/public/SDLScreenManager.m index 72ea10d53..d413cdee6 100644 --- a/SmartDeviceLink/public/SDLScreenManager.m +++ b/SmartDeviceLink/public/SDLScreenManager.m @@ -332,11 +332,11 @@ - (void)dismissKeyboardWithCancelID:(NSNumber *)cancelID{ #pragma mark - Menu - (BOOL)openMenu { - return [self.menuManager openMenu:nil]; + return [self.menuManager openMenu:nil]; } - (BOOL)openSubmenu:(SDLMenuCell *)cell { - return [self.menuManager openMenu:cell]; + return [self.menuManager openMenu:cell]; } #pragma mark - Alert From e3d8ab0d27f0169941899e5951045948bb87b1d7 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 9 Sep 2021 08:37:20 -0400 Subject: [PATCH 111/112] Fix still setting menu configuration if it fails --- SmartDeviceLink/private/SDLMenuManager.m | 1 + 1 file changed, 1 insertion(+) diff --git a/SmartDeviceLink/private/SDLMenuManager.m b/SmartDeviceLink/private/SDLMenuManager.m index 9bae9f7b8..731449dd5 100644 --- a/SmartDeviceLink/private/SDLMenuManager.m +++ b/SmartDeviceLink/private/SDLMenuManager.m @@ -168,6 +168,7 @@ - (void)setMenuConfiguration:(SDLMenuConfiguration *)menuConfiguration { SDLMenuConfigurationUpdateOperation *configurationUpdateOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:self.connectionManager windowCapability:self.windowCapability newMenuConfiguration:menuConfiguration configurationUpdatedHandler:^(SDLMenuConfiguration *newMenuConfiguration, NSError *_Nullable error) { if (error != nil) { SDLLogE(@"Error updating menu configuration: %@", error); + return; } weakself.currentMenuConfiguration = newMenuConfiguration; [weakself sdl_updateMenuReplaceOperationsWithNewMenuConfiguration]; From 7ef29ccad916ad794aa7983817a4aa3a9fc59a5e Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 21 Sep 2021 14:06:50 -0400 Subject: [PATCH 112/112] Fix using wrong menu configuration pass through to menu operation --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 8 +++++++- .../xcschemes/SmartDeviceLink-Example-ObjC.xcscheme | 2 +- .../xcschemes/SmartDeviceLink-Example-Swift.xcscheme | 2 +- .../xcshareddata/xcschemes/SmartDeviceLink.xcscheme | 2 +- .../xcshareddata/xcschemes/SmartDeviceLinkSwift.xcscheme | 2 +- SmartDeviceLink/private/SDLMenuManager.m | 2 +- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index b5aa60742..8406bd793 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -7986,7 +7986,7 @@ attributes = { CLASSPREFIX = SDL; LastSwiftUpdateCheck = 0710; - LastUpgradeCheck = 1250; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = smartdevicelink; TargetAttributes = { 5D4019AE1A76EC350006B0C2 = { @@ -9480,6 +9480,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = NCVC2MHU7M; @@ -9498,6 +9499,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = NCVC2MHU7M; @@ -9523,6 +9525,7 @@ CLANG_STATIC_ANALYZER_MODE = deep; CLANG_UNDEFINED_BEHAVIOR_SANITIZER_INTEGER = YES; CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES; + CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_FLOAT_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; @@ -9579,6 +9582,7 @@ CLANG_STATIC_ANALYZER_MODE = deep; CLANG_UNDEFINED_BEHAVIOR_SANITIZER_INTEGER = YES; CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES; + CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_FLOAT_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; @@ -9696,6 +9700,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = NCVC2MHU7M; @@ -9718,6 +9723,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = NCVC2MHU7M; diff --git a/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme b/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme index ed5b74e00..52405b71f 100644 --- a/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme +++ b/SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme @@ -1,6 +1,6 @@ *)menuCells { _menuCells = [[NSArray alloc] initWithArray:menuCells copyItems:YES]; __weak typeof(self) weakself = self; - SDLMenuReplaceOperation *menuReplaceOperation = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.menuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells compatibilityModeEnabled:(![self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) currentMenuUpdatedHandler:^(NSArray * _Nonnull currentMenuCells, NSError *error) { + SDLMenuReplaceOperation *menuReplaceOperation = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:self.connectionManager fileManager:self.fileManager windowCapability:self.windowCapability menuConfiguration:self.currentMenuConfiguration currentMenu:self.currentMenuCells updatedMenu:self.menuCells compatibilityModeEnabled:(![self sdl_isDynamicMenuUpdateActive:self.dynamicMenuUpdatesMode]) currentMenuUpdatedHandler:^(NSArray * _Nonnull currentMenuCells, NSError *error) { weakself.currentMenuCells = currentMenuCells; [weakself sdl_updateMenuReplaceOperationsWithNewCurrentMenu]; SDLLogD(@"Finished updating menu");