summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Fischer <joeljfischer@gmail.com>2018-04-10 10:46:31 -0400
committerJoel Fischer <joeljfischer@gmail.com>2018-04-10 10:46:31 -0400
commit2288f47c73c1e652e08409f04753eccdab644681 (patch)
treea4bff580ada8d84d2ffccdf07b3359bc465d197e
parent1a33c27a06a140feb6ecd094d4625a0288022183 (diff)
downloadsdl_ios-2288f47c73c1e652e08409f04753eccdab644681.tar.gz
Starting work on menu manager
-rw-r--r--SmartDeviceLink-iOS.xcodeproj/project.pbxproj40
-rw-r--r--SmartDeviceLink/SDLMenuCell.h32
-rw-r--r--SmartDeviceLink/SDLMenuCell.m39
-rw-r--r--SmartDeviceLink/SDLMenuManager.h26
-rw-r--r--SmartDeviceLink/SDLMenuManager.m146
-rw-r--r--SmartDeviceLink/SDLScreenManager.h5
-rw-r--r--SmartDeviceLink/SDLVoiceCommand.h24
-rw-r--r--SmartDeviceLink/SDLVoiceCommand.m27
8 files changed, 339 insertions, 0 deletions
diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
index bff33322b..655b61606 100644
--- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
+++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
@@ -348,6 +348,12 @@
5D293AFE1FE078A9000CBD7E /* SDLCarWindowViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D293AFC1FE078A9000CBD7E /* SDLCarWindowViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
5D293AFF1FE078A9000CBD7E /* SDLCarWindowViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D293AFD1FE078A9000CBD7E /* SDLCarWindowViewController.m */; };
5D2F58081D0717D5001085CE /* SDLManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D2F58071D0717D5001085CE /* SDLManagerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5D339CEA207C066E000CC364 /* SDLMenuCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D339CE8207C066E000CC364 /* SDLMenuCell.h */; };
+ 5D339CEB207C066E000CC364 /* SDLMenuCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D339CE9207C066E000CC364 /* SDLMenuCell.m */; };
+ 5D339CEF207C08BA000CC364 /* SDLVoiceCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D339CED207C08BA000CC364 /* SDLVoiceCommand.h */; };
+ 5D339CF0207C08BA000CC364 /* SDLVoiceCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D339CEE207C08BA000CC364 /* SDLVoiceCommand.m */; };
+ 5D339CF3207C0ACE000CC364 /* SDLMenuManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D339CF1207C0ACE000CC364 /* SDLMenuManager.h */; };
+ 5D339CF4207C0ACE000CC364 /* SDLMenuManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D339CF2207C0ACE000CC364 /* SDLMenuManager.m */; };
5D3E48751D6F3B330000BFEF /* SDLAsynchronousOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D3E48731D6F3B330000BFEF /* SDLAsynchronousOperation.h */; };
5D3E48761D6F3B330000BFEF /* SDLAsynchronousOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D3E48741D6F3B330000BFEF /* SDLAsynchronousOperation.m */; };
5D3E487B1D6F888E0000BFEF /* SDLRPCResponseNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D3E48791D6F888E0000BFEF /* SDLRPCResponseNotification.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -1588,6 +1594,12 @@
5D293AFC1FE078A9000CBD7E /* SDLCarWindowViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLCarWindowViewController.h; sourceTree = "<group>"; };
5D293AFD1FE078A9000CBD7E /* SDLCarWindowViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLCarWindowViewController.m; sourceTree = "<group>"; };
5D2F58071D0717D5001085CE /* SDLManagerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLManagerDelegate.h; sourceTree = "<group>"; };
+ 5D339CE8207C066E000CC364 /* SDLMenuCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLMenuCell.h; sourceTree = "<group>"; };
+ 5D339CE9207C066E000CC364 /* SDLMenuCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLMenuCell.m; sourceTree = "<group>"; };
+ 5D339CED207C08BA000CC364 /* SDLVoiceCommand.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLVoiceCommand.h; sourceTree = "<group>"; };
+ 5D339CEE207C08BA000CC364 /* SDLVoiceCommand.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLVoiceCommand.m; sourceTree = "<group>"; };
+ 5D339CF1207C0ACE000CC364 /* SDLMenuManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLMenuManager.h; sourceTree = "<group>"; };
+ 5D339CF2207C0ACE000CC364 /* SDLMenuManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLMenuManager.m; sourceTree = "<group>"; };
5D3E48731D6F3B330000BFEF /* SDLAsynchronousOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLAsynchronousOperation.h; sourceTree = "<group>"; };
5D3E48741D6F3B330000BFEF /* SDLAsynchronousOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLAsynchronousOperation.m; sourceTree = "<group>"; };
5D3E48791D6F888E0000BFEF /* SDLRPCResponseNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLRPCResponseNotification.h; sourceTree = "<group>"; };
@@ -2917,6 +2929,7 @@
5D0A736F203F0C450001595D /* Screen */ = {
isa = PBXGroup;
children = (
+ 5D339CE5207C0651000CC364 /* Menu */,
5D0A737F203F23D10001595D /* Soft Button */,
5D0A737D203F23B30001595D /* Text and Graphic */,
5DAD5F7D204DEDEB0025624C /* SDLScreenManager.h */,
@@ -3029,6 +3042,27 @@
path = Utilities;
sourceTree = "<group>";
};
+ 5D339CE5207C0651000CC364 /* Menu */ = {
+ isa = PBXGroup;
+ children = (
+ 5D339CEC207C08AB000CC364 /* Cells */,
+ 5D339CF1207C0ACE000CC364 /* SDLMenuManager.h */,
+ 5D339CF2207C0ACE000CC364 /* SDLMenuManager.m */,
+ );
+ name = Menu;
+ sourceTree = "<group>";
+ };
+ 5D339CEC207C08AB000CC364 /* Cells */ = {
+ isa = PBXGroup;
+ children = (
+ 5D339CE8207C066E000CC364 /* SDLMenuCell.h */,
+ 5D339CE9207C066E000CC364 /* SDLMenuCell.m */,
+ 5D339CED207C08BA000CC364 /* SDLVoiceCommand.h */,
+ 5D339CEE207C08BA000CC364 /* SDLVoiceCommand.m */,
+ );
+ name = Cells;
+ sourceTree = "<group>";
+ };
5D3E48771D6F3DA40000BFEF /* Superclass Operation */ = {
isa = PBXGroup;
children = (
@@ -4899,6 +4933,7 @@
5D61FD091A84238C00846EE7 /* SDLOnDriverDistraction.h in Headers */,
E9C32B9E1AB20C5900F283AF /* EAAccessoryManager+SDLProtocols.h in Headers */,
5DAD5F7F204DEDEB0025624C /* SDLScreenManager.h in Headers */,
+ 5D339CEF207C08BA000CC364 /* SDLVoiceCommand.h in Headers */,
5D61FC4B1A84238C00846EE7 /* SDLBeltStatus.h in Headers */,
DAC5726B1D10D5FC0004288B /* dispatch_timer.h in Headers */,
DA9F7E991DCC052C00ACAE48 /* SDLLocationCoordinate.h in Headers */,
@@ -5197,6 +5232,7 @@
97E26DEC1E807AD70074A3C7 /* SDLMutableDataQueue.h in Headers */,
5D61FDF71A84238C00846EE7 /* SDLV1ProtocolMessage.h in Headers */,
5D61FDFB1A84238C00846EE7 /* SDLV2ProtocolMessage.h in Headers */,
+ 5D339CEA207C066E000CC364 /* SDLMenuCell.h in Headers */,
5DBF06311E64A9C600A5CF03 /* SDLLogModel.h in Headers */,
5D7F87EB1CE3C1A1002DD7C4 /* SDLDeleteFileOperation.h in Headers */,
5D61FCFC1A84238C00846EE7 /* SDLNames.h in Headers */,
@@ -5210,6 +5246,7 @@
5D00AC6B1F141339004000D9 /* SDLSystemCapability.h in Headers */,
5DCD7AE01FCCA8D200A0FC7F /* SDLCarWindow.h in Headers */,
5D61FD6F1A84238C00846EE7 /* SDLRPCPayload.h in Headers */,
+ 5D339CF3207C0ACE000CC364 /* SDLMenuManager.h in Headers */,
5D61FCF01A84238C00846EE7 /* SDLLockScreenStatusManager.h in Headers */,
5D61FD311A84238C00846EE7 /* SDLPolicyDataParser.h in Headers */,
);
@@ -5675,6 +5712,7 @@
5D61FDC41A84238C00846EE7 /* SDLTBTState.m in Sources */,
5D61FDA61A84238C00846EE7 /* SDLSpeak.m in Sources */,
5D4631111F2135850092EFDC /* SDLControlFramePayloadConstants.m in Sources */,
+ 5D339CF0207C08BA000CC364 /* SDLVoiceCommand.m in Sources */,
DA9F7E881DCC049900ACAE48 /* SDLSubscribeWayPoints.m in Sources */,
5D61FDDE1A84238C00846EE7 /* SDLTTSChunk.m in Sources */,
5D61FD9E1A84238C00846EE7 /* SDLSliderResponse.m in Sources */,
@@ -5691,6 +5729,7 @@
5D61FC9F1A84238C00846EE7 /* SDLEncodedSyncPData.m in Sources */,
5D61FE061A84238C00846EE7 /* SDLVehicleDataResultCode.m in Sources */,
5D61FCA41A84238C00846EE7 /* SDLEndAudioPassThru.m in Sources */,
+ 5D339CEB207C066E000CC364 /* SDLMenuCell.m in Sources */,
5D8B17541AC9E11B006A6E1C /* SDLDialNumberResponse.m in Sources */,
DA6223BE1E7B088200878689 /* CVPixelBufferRef+SDLUtil.m in Sources */,
1FF7DABC1F75B2BF00B46C30 /* SDLFocusableItemLocator.m in Sources */,
@@ -5783,6 +5822,7 @@
5DCD7AF71FCCA8E400A0FC7F /* SDLScreenshotViewController.m in Sources */,
5D61FD081A84238C00846EE7 /* SDLOnCommand.m in Sources */,
5D53C46E1B7A99B9003526EA /* SDLStreamingMediaManager.m in Sources */,
+ 5D339CF4207C0ACE000CC364 /* SDLMenuManager.m in Sources */,
5D61FD6A1A84238C00846EE7 /* SDLRPCMessage.m in Sources */,
1E5AD0391F1F4E390029B8AF /* SDLClimateControlCapabilities.m in Sources */,
5D61FDA21A84238C00846EE7 /* SDLSoftButtonCapabilities.m in Sources */,
diff --git a/SmartDeviceLink/SDLMenuCell.h b/SmartDeviceLink/SDLMenuCell.h
new file mode 100644
index 000000000..c0fe34cec
--- /dev/null
+++ b/SmartDeviceLink/SDLMenuCell.h
@@ -0,0 +1,32 @@
+//
+// SDLMenuCell.h
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 4/9/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class SDLArtwork;
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef void(^SDLMenuCellSelectionHandler)(void);
+
+@interface SDLMenuCell : NSObject
+
+@property (copy, nonatomic, readonly) NSString *title;
+@property (strong, nonatomic, readonly, nullable) SDLArtwork *icon;
+@property (copy, nonatomic, readonly, nullable) NSArray<NSString *> *voiceCommands;
+@property (copy, nonatomic, readonly, nullable) SDLMenuCellSelectionHandler handler;
+
+// Note that if this is non-nil, the icon and handler will be ignored.
+@property (copy, nonatomic, readonly, nullable) NSArray<SDLMenuCell *> *subCells;
+
+- (instancetype)initWithTitle:(NSString *)title icon:(nullable SDLArtwork *)icon voiceCommands:(nullable NSArray<NSString *> *)voiceCommands handler:(SDLMenuCellSelectionHandler)handler;
+- (instancetype)initWithTitle:(NSString *)title voiceCommands:(nullable NSArray<NSString *> *)voiceCommands subCells:(NSArray<SDLMenuCell *> *)subCells;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLMenuCell.m b/SmartDeviceLink/SDLMenuCell.m
new file mode 100644
index 000000000..4b25ddd57
--- /dev/null
+++ b/SmartDeviceLink/SDLMenuCell.m
@@ -0,0 +1,39 @@
+//
+// SDLMenuCell.m
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 4/9/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import "SDLMenuCell.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@implementation SDLMenuCell
+
+- (instancetype)initWithTitle:(NSString *)title icon:(nullable SDLArtwork *)icon voiceCommands:(nullable NSArray<NSString *> *)voiceCommands handler:(SDLMenuCellSelectionHandler)handler {
+ self = [super init];
+ if (!self) { return nil; }
+
+ _title = title;
+ _icon = icon;
+ _voiceCommands = voiceCommands;
+ _handler = handler;
+
+ return self;
+}
+
+- (instancetype)initWithTitle:(NSString *)title voiceCommands:(nullable NSArray<NSString *> *)voiceCommands subCells:(NSArray<SDLMenuCell *> *)subCells {
+ self = [super init];
+ if (!self) { return nil; }
+
+ _voiceCommands = voiceCommands;
+ _subCells = subCells;
+
+ return self;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLMenuManager.h b/SmartDeviceLink/SDLMenuManager.h
new file mode 100644
index 000000000..1163c0b44
--- /dev/null
+++ b/SmartDeviceLink/SDLMenuManager.h
@@ -0,0 +1,26 @@
+//
+// SDLMenuManager.h
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 4/9/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class SDLMenuCell;
+@class SDLVoiceCommand;
+
+/**
+ The handler run when the update has completed
+
+ @param error An error if the update failed and an error occurred
+ */
+typedef void(^SDLMenuUpdateCompletionHandler)(NSError *__nullable error);
+
+@interface SDLMenuManager : NSObject
+
+@property (copy, nonatomic) NSArray<SDLMenuCell *> *menuCells;
+@property (copy, nonatomic) NSArray<SDLVoiceCommand *> *voiceCommands;
+
+@end
diff --git a/SmartDeviceLink/SDLMenuManager.m b/SmartDeviceLink/SDLMenuManager.m
new file mode 100644
index 000000000..6814fbc10
--- /dev/null
+++ b/SmartDeviceLink/SDLMenuManager.m
@@ -0,0 +1,146 @@
+//
+// SDLMenuManager.m
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 4/9/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import "SDLMenuManager.h"
+
+#import "SDLAddCommand.h"
+#import "SDLAddSubMenu.h"
+#import "SDLArtwork.h"
+#import "SDLDeleteCommand.h"
+#import "SDLImage.h"
+#import "SDLMenuCell.h"
+#import "SDLMenuParams.h"
+#import "SDLOnCommand.h"
+#import "SDLRegisterAppInterfaceResponse.h"
+#import "SDLRPCNotificationNotification.h"
+#import "SDLRPCResponseNotification.h"
+#import "SDLSetDisplayLayoutResponse.h"
+#import "SDLVoiceCommand.h"
+
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SDLMenuManager()
+
+@property (strong, nonatomic, nullable) SDLDisplayCapabilities *displayCapabilities;
+
+// The top level of AddCommands & AddSubMenus
+@property (copy, nonatomic) NSArray<SDLRPCRequest *> *cellCommands;
+@property (copy, nonatomic) NSArray<SDLAddCommand *> *voiceCommandCommands;
+
+@end
+
+@implementation SDLMenuManager
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) { return nil; }
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_registerResponse:) name:SDLDidReceiveRegisterAppInterfaceResponse object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_displayLayoutResponse:) name:SDLDidReceiveSetDisplayLayoutResponse object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_commandNotification:) name:SDLDidReceiveCommandNotification object:nil];
+
+ return self;
+}
+
+- (void)sdl_updateWithCompletionHandler:(SDLMenuUpdateCompletionHandler)completionHandler {
+ // Upload images that need it
+
+ // Delete old commands, keep voice commands
+
+ // Add new commands once images are uploaded
+}
+
+#pragma mark - Setters
+
+- (void)setMenuCells:(NSArray<SDLMenuCell *> *)menuCells {
+ _menuCells = menuCells;
+}
+
+- (void)setVoiceCommands:(NSArray<SDLVoiceCommand *> *)voiceCommands {
+ _voiceCommands = voiceCommands;
+}
+
+#pragma mark - Helpers
+
+- (NSArray<SDLDeleteCommand *> *)deleteCommandsForCurrentCells {
+ NSMutableArray<SDLDeleteCommand *> *mutableDeletes = [NSMutableArray array];
+ for (SDLRPCRequest *request in self.voiceCommandCommands) {
+ if ([request isKindOfClass:[SDLAddCommand class]]) {
+ SDLAddCommand *command = (SDLAddCommand *)request;
+ SDLDeleteCommand *delete = [[SDLDeleteCommand alloc] initWithId:command.cmdID.unsignedIntValue];
+ [mutableDeletes addObject:delete];
+ } else if ([request isKindOfClass:[SDLAddSubMenu class]]) {
+ SDLAddSubMenu *subMenu = (SDLAddSubMenu *)request;
+ SDLDeleteCommand *delete = [[SDLDeleteCommand alloc] initWithId:subMenu.menuID.unsignedIntValue];
+ [mutableDeletes addObject:delete];
+ } else {
+ NSAssert(NO, @"Menu manager only handles AddSubMenu and AddCommand, not %@", [request class]);
+ }
+ }
+
+ return [mutableDeletes copy];
+}
+
+- (NSArray<SDLDeleteCommand *> *)deleteCommandsForCurrentVoiceCommands {
+ NSMutableArray<SDLDeleteCommand *> *mutableDeletes = [NSMutableArray array];
+ for (SDLAddCommand *command in self.voiceCommandCommands) {
+ SDLDeleteCommand *delete = [[SDLDeleteCommand alloc] initWithId:command.cmdID.unsignedIntValue];
+ [mutableDeletes addObject:delete];
+ }
+
+ return [mutableDeletes copy];
+}
+
+- (SDLAddCommand *)sdl_commandForMenuCell:(SDLMenuCell *)cell withParentCellId:(nullable NSNumber<SDLInt> *)parentId {
+ SDLAddCommand *command = [[SDLAddCommand alloc] init];
+
+ SDLMenuParams *params = [[SDLMenuParams alloc] init];
+ params.menuName = cell.title;
+ params.parentID = parentId;
+
+ command.menuParams = params;
+ command.vrCommands = cell.voiceCommands;
+ command.cmdIcon = cell.icon ? [[SDLImage alloc] initWithName:cell.icon.name] : nil;
+
+ return command;
+}
+
+- (SDLAddSubMenu *)sdl_subMenuCommandForMenuCell:(SDLMenuCell *)cell {
+ return [[SDLAddSubMenu alloc] initWithId:0 menuName:cell.title];
+}
+
+- (SDLAddCommand *)sdl_commandForVoiceCommand:(SDLVoiceCommand *)voiceCommand {
+ SDLAddCommand *command = [[SDLAddCommand alloc] init];
+ command.vrCommands = voiceCommand.voiceCommands;
+
+ return command;
+}
+
+#pragma mark - Observers
+
+- (void)sdl_commandNotification:(SDLRPCNotificationNotification *)notification {
+ SDLOnCommand *onCommand = (SDLOnCommand *)notification.notification;
+}
+
+- (void)sdl_registerResponse:(SDLRPCResponseNotification *)notification {
+ SDLRegisterAppInterfaceResponse *response = (SDLRegisterAppInterfaceResponse *)notification.response;
+ self.displayCapabilities = response.displayCapabilities;
+}
+
+- (void)sdl_displayLayoutResponse:(SDLRPCResponseNotification *)notification {
+ SDLSetDisplayLayoutResponse *response = (SDLSetDisplayLayoutResponse *)notification.response;
+ self.displayCapabilities = response.displayCapabilities;
+
+ // Auto-send an updated show
+ [self sdl_updateWithCompletionHandler:nil];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLScreenManager.h b/SmartDeviceLink/SDLScreenManager.h
index 716a0eae9..70d57ccf4 100644
--- a/SmartDeviceLink/SDLScreenManager.h
+++ b/SmartDeviceLink/SDLScreenManager.h
@@ -13,6 +13,8 @@
@class SDLArtwork;
@class SDLFileManager;
+@class SDLMenuCell;
+@class SDLVoiceCommand;
@class SDLSoftButtonObject;
@protocol SDLConnectionManagerType;
@@ -44,6 +46,9 @@ typedef void(^SDLScreenManagerUpdateCompletionHandler)(NSError *__nullable error
@property (copy, nonatomic) NSArray<SDLSoftButtonObject *> *softButtonObjects;
+@property (copy, nonatomic) NSArray<SDLMenuCell *> *menu;
+@property (copy, nonatomic) NSArray<SDLVoiceCommand *> *voiceCommands;
+
- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager fileManager:(SDLFileManager *)fileManager;
/**
diff --git a/SmartDeviceLink/SDLVoiceCommand.h b/SmartDeviceLink/SDLVoiceCommand.h
new file mode 100644
index 000000000..8f4442b4e
--- /dev/null
+++ b/SmartDeviceLink/SDLVoiceCommand.h
@@ -0,0 +1,24 @@
+//
+// SDLVoiceCommand.h
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 4/9/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef void(^SDLVoiceCommandSelectionHandler)(void);
+
+@interface SDLVoiceCommand : NSObject
+
+@property (copy, nonatomic, readonly) NSArray<NSString *> *voiceCommands;
+@property (copy, nonatomic, readonly, nullable) SDLVoiceCommandSelectionHandler handler;
+
+- (instancetype)initWithVoiceCommands:(NSArray<NSString *> *)voiceCommands handler:(SDLVoiceCommandSelectionHandler)handler;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLVoiceCommand.m b/SmartDeviceLink/SDLVoiceCommand.m
new file mode 100644
index 000000000..faa2007e1
--- /dev/null
+++ b/SmartDeviceLink/SDLVoiceCommand.m
@@ -0,0 +1,27 @@
+//
+// SDLVoiceCommand.m
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 4/9/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import "SDLVoiceCommand.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@implementation SDLVoiceCommand
+
+- (instancetype)initWithVoiceCommands:(NSArray<NSString *> *)voiceCommands handler:(SDLVoiceCommandSelectionHandler)handler {
+ self = [super init];
+ if (!self) { return nil; }
+
+ _voiceCommands = voiceCommands;
+ _handler = handler;
+
+ return self;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END