Skip to content

Commit

Permalink
Merge pull request #1901 from smartdevicelink/feature/issue-1898-menu…
Browse files Browse the repository at this point in the history
…-manager-refactor

Menu Manager Refactor and Updating Implementation
  • Loading branch information
joeljfischer authored Sep 24, 2021
2 parents 4c1fe3f + 6010452 commit 095d0a2
Show file tree
Hide file tree
Showing 44 changed files with 3,577 additions and 1,511 deletions.
2 changes: 1 addition & 1 deletion Cartfile.private
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
github "Quick/Quick" ~> 3.0
github "Quick/Nimble" ~> 9.0.0
github "Quick/Nimble" ~> 9.0
github "erikdoe/ocmock" ~> 3.7
162 changes: 129 additions & 33 deletions SmartDeviceLink-iOS.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1250"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1250"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1250"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1250"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
19 changes: 18 additions & 1 deletion SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,24 @@

@class SDLDynamicMenuUpdateRunScore;
@class SDLMenuCell;
@class SDLWindowCapability;

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, SDLMenuCellUpdateState) {
/// Marks the cell to be deleted
SDLMenuCellUpdateStateDelete = 0,

/// Marks the cell to be added
SDLMenuCellUpdateStateAdd,

/// Marks the cell to be kept
SDLMenuCellUpdateStateKeep
};

@interface SDLDynamicMenuUpdateAlgorithm : NSObject

/**
Expand All @@ -21,7 +36,9 @@ NS_ASSUME_NONNULL_BEGIN
@param oldMenuCells The old menu array
@param updatedMenuCells The new menu array
*/
+ (nullable SDLDynamicMenuUpdateRunScore *)compareOldMenuCells:(NSArray<SDLMenuCell *> *)oldMenuCells updatedMenuCells:(NSArray<SDLMenuCell *> *)updatedMenuCells;
+ (SDLDynamicMenuUpdateRunScore *)dynamicRunScoreOldMenuCells:(NSArray<SDLMenuCell *> *)oldMenuCells updatedMenuCells:(NSArray<SDLMenuCell *> *)updatedMenuCells;

+ (SDLDynamicMenuUpdateRunScore *)compatibilityRunScoreWithOldMenuCells:(NSArray<SDLMenuCell *> *)oldMenuCells updatedMenuCells:(NSArray<SDLMenuCell *> *)updatedMenuCells;

@end

Expand Down
55 changes: 32 additions & 23 deletions SmartDeviceLink/private/SDLDynamicMenuUpdateAlgorithm.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,50 @@
#import "SDLDynamicMenuUpdateRunScore.h"
#import "SDLMenuCell.h"
#import "SDLLogMacros.h"
#import "SDLMenuManagerConstants.h"
#import "SDLWindowCapability.h"

NS_ASSUME_NONNULL_BEGIN

@implementation SDLDynamicMenuUpdateAlgorithm

#pragma mark - Update Menu Cells
+ (nullable SDLDynamicMenuUpdateRunScore *)compareOldMenuCells:(NSArray<SDLMenuCell *> *)oldMenuCells updatedMenuCells:(NSArray<SDLMenuCell *> *)updatedMenuCells{
#pragma mark Compatibility Menu Run Score

+ (SDLDynamicMenuUpdateRunScore *)compatibilityRunScoreWithOldMenuCells:(NSArray<SDLMenuCell *> *)oldMenuCells updatedMenuCells:(NSArray<SDLMenuCell *> *)updatedMenuCells {
return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[self sdl_buildAllDeleteStatusesForMenu:oldMenuCells] updatedStatus:[self sdl_buildAllAddStatusesForMenu:updatedMenuCells] score:updatedMenuCells.count];
}

#pragma mark - Dynamic Menu Run Score

+ (SDLDynamicMenuUpdateRunScore *)dynamicRunScoreOldMenuCells:(NSArray<SDLMenuCell *> *)oldMenuCells updatedMenuCells:(NSArray<SDLMenuCell *> *)updatedMenuCells {
if (oldMenuCells.count > 0 && updatedMenuCells.count == 0) {
return [[SDLDynamicMenuUpdateRunScore alloc] initWithOldStatus:[SDLDynamicMenuUpdateAlgorithm sdl_buildAllDeleteStatusesforMenu:oldMenuCells] updatedStatus:@[] score: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<SDLMenuCell *> *)oldMenuCells updatedMenuCells:(NSArray<SDLMenuCell *> *)updatedMenuCells {
SDLDynamicMenuUpdateRunScore *bestScore = nil;
+ (SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)startRun oldMenuCells:(NSArray<SDLMenuCell *> *)oldMenuCells updatedMenuCells:(NSArray<SDLMenuCell *> *)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
NSMutableArray<NSNumber *> *oldMenuStatus = [SDLDynamicMenuUpdateAlgorithm sdl_buildAllDeleteStatusesforMenu:oldMenuCells];
NSMutableArray<NSNumber *> *oldMenuStatus = [SDLDynamicMenuUpdateAlgorithm sdl_buildAllDeleteStatusesForMenu:oldMenuCells];
NSMutableArray<NSNumber *> *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]]) {
oldMenuStatus[oldCellIndex] = @(MenuCellStateKeep);
newMenuStatus[newCellIndex] = @(MenuCellStateKeep);
if ([oldMenuCells[oldCellIndex] isEqualToCellWithUniqueTitle:updatedMenuCells[newCellIndex]]) {
oldMenuStatus[oldCellIndex] = @(SDLMenuCellUpdateStateKeep);
newMenuStatus[newCellIndex] = @(SDLMenuCellUpdateStateKeep);
startIndex = newCellIndex + 1;
break;
}
Expand All @@ -54,18 +64,17 @@ + (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++;
}
}

// 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) {
// 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];
}
}
Expand All @@ -78,12 +87,12 @@ + (nullable SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)sta
@param oldMenu The old menu array
*/
+ (NSMutableArray<NSNumber *> *)sdl_buildAllDeleteStatusesforMenu:(NSArray<SDLMenuCell *> *)oldMenu {
NSMutableArray<NSNumber *> *oldMenuStatus = [[NSMutableArray alloc] init];
+ (NSMutableArray<NSNumber *> *)sdl_buildAllDeleteStatusesForMenu:(NSArray<SDLMenuCell *> *)oldMenu {
NSMutableArray<NSNumber *> *oldMenuStatus = [[NSMutableArray alloc] initWithCapacity:oldMenu.count];
for (NSUInteger index = 0; index < oldMenu.count; index++) {
[oldMenuStatus addObject:@(MenuCellStateDelete)];
[oldMenuStatus addObject:@(SDLMenuCellUpdateStateDelete)];
}
return [oldMenuStatus mutableCopy];
return oldMenuStatus;
}

/**
Expand All @@ -92,11 +101,11 @@ + (nullable SDLDynamicMenuUpdateRunScore *)sdl_startCompareAtRun:(NSUInteger)sta
@param newMenu The new menu array
*/
+ (NSMutableArray<NSNumber *> *)sdl_buildAllAddStatusesForMenu:(NSArray<SDLMenuCell *> *)newMenu {
NSMutableArray<NSNumber *> *newMenuStatus = [[NSMutableArray alloc] init];
NSMutableArray<NSNumber *> *newMenuStatus = [[NSMutableArray alloc] initWithCapacity:newMenu.count];
for (NSUInteger index = 0; index < newMenu.count; index++) {
[newMenuStatus addObject:@(MenuCellStateAdd)];
[newMenuStatus addObject:@(SDLMenuCellUpdateStateAdd)];
}
return [newMenuStatus mutableCopy];
return newMenuStatus;
}

@end
Expand Down
7 changes: 5 additions & 2 deletions SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<NSNumber *> *oldStatus;

/**
Will contain all the Adds and Keeps
Will contain all the Adds and Keeps. Contains SDLMenuState.
*/
@property (copy, nonatomic, readonly) NSArray<NSNumber *> *updatedStatus;

Expand All @@ -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<NSNumber *> *)oldStatus updatedStatus:(NSArray<NSNumber *> *)updatedStatus score:(NSUInteger)score;

@end
Expand Down
25 changes: 25 additions & 0 deletions SmartDeviceLink/private/SDLDynamicMenuUpdateRunScore.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#import "SDLDynamicMenuUpdateRunScore.h"

#import "SDLDynamicMenuUpdateAlgorithm.h"

NS_ASSUME_NONNULL_BEGIN

@implementation SDLDynamicMenuUpdateRunScore
Expand All @@ -23,6 +25,29 @@ - (instancetype)initWithOldStatus:(NSArray<NSNumber *> *)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<NSString *> *)sdl_stringArrayForCellUpdateStatuses:(NSArray<NSNumber *> *)statuses {
NSMutableArray<NSString *> *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];
}

- (BOOL)isEmpty {
return (self.oldStatus.count == 0 && self.updatedStatus.count == 0 && self.score == 0);
}

@end

NS_ASSUME_NONNULL_END
9 changes: 9 additions & 0 deletions SmartDeviceLink/private/SDLError.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
#import "SDLErrorConstants.h"
#import "SDLResult.h"

@class SDLMenuCell;
@class SDLMenuConfiguration;


NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -56,7 +59,13 @@ NS_ASSUME_NONNULL_BEGIN

#pragma mark Menu Manager

+ (NSError *)sdl_menuManager_configurationOperationLayoutsNotSupported;
+ (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
Expand Down
49 changes: 49 additions & 0 deletions SmartDeviceLink/private/SDLError.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#import "SDLError.h"

#import "SDLChoiceSetManager.h"
#import "SDLMenuConfiguration.h"

NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -270,6 +271,54 @@ + (NSError *)sdl_subscribeButtonManager_notSubscribed {

#pragma mark Menu Manager

+ (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,
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: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."
}];
}

+ (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_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];
}
Expand Down
3 changes: 1 addition & 2 deletions SmartDeviceLink/private/SDLLifecycleManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -681,8 +681,7 @@ - (void)sendRequest:(SDLRPCRequest *)request withResponseHandler:(nullable SDLRe

- (void)sendRequests:(NSArray<SDLRPCRequest *> *)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];
Expand Down
2 changes: 1 addition & 1 deletion SmartDeviceLink/private/SDLLogFileModuleMap.m
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
26 changes: 26 additions & 0 deletions SmartDeviceLink/private/SDLMenuConfigurationUpdateOperation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// 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 "SDLMenuReplaceUtilities.h"
#import "SDLWindowCapability.h"

NS_ASSUME_NONNULL_BEGIN

typedef void(^SDLMenuConfigurationUpdatedBlock)(SDLMenuConfiguration *_Nullable newMenuConfiguration, NSError *_Nullable error);

@interface SDLMenuConfigurationUpdateOperation : SDLAsynchronousOperation

- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager windowCapability:(SDLWindowCapability *)windowCapability newMenuConfiguration:(SDLMenuConfiguration *)newConfiguration configurationUpdatedHandler:(SDLMenuConfigurationUpdatedBlock)configurationUpdatedBlock;

@end

NS_ASSUME_NONNULL_END
Loading

0 comments on commit 095d0a2

Please sign in to comment.