瀏覽代碼

first step of formalizing of Watch -> App communication

Tobias Conradi 10 年之前
父節點
當前提交
6c10703b4d

+ 31 - 0
SharedSources/VLCWatchMessage.h

@@ -0,0 +1,31 @@
+//
+//  VLCWatchMessage.h
+//  VLC for iOS
+//
+//  Created by Tobias Conradi on 02.05.15.
+//  Copyright (c) 2015 VideoLAN. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+extern NSString *const VLCWatchMessageNameGetNowPlayingInfo;
+extern NSString *const VLCWatchMessageNamePlayPause;
+extern NSString *const VLCWatchMessageNameSkipForward;
+extern NSString *const VLCWatchMessageNameSkipBackward;
+extern NSString *const VLCWatchMessageNamePlayFile;
+extern NSString *const VLCWatchMessageNameSetVolume;
+
+
+@interface VLCWatchMessage : NSObject
+@property (nonatomic, readonly) NSString *name;
+@property (nonatomic, readonly) id<NSObject,NSCoding> payload;
+
+@property (nonatomic, readonly) NSDictionary *dictionaryRepresentation;
+
+- (instancetype)initWithName:(NSString *)name payload:(id<NSObject,NSCoding>)payload;
+- (instancetype)initWithDictionary:(NSDictionary *)dictionary;
+
++ (NSDictionary *)messageDictionaryForName:(NSString *)name payload:(id<NSObject,NSCoding>)payload;
++ (NSDictionary *)messageDictionaryForName:(NSString *)name;
+
+@end

+ 90 - 0
SharedSources/VLCWatchMessage.m

@@ -0,0 +1,90 @@
+//
+//  VLCWatchMessage.m
+//  VLC for iOS
+//
+//  Created by Tobias Conradi on 02.05.15.
+//  Copyright (c) 2015 VideoLAN. All rights reserved.
+//
+
+#import "VLCWatchMessage.h"
+
+NSString *const VLCWatchMessageNameGetNowPlayingInfo = @"getNowPlayingInfo";
+NSString *const VLCWatchMessageNamePlayPause = @"playpause";
+NSString *const VLCWatchMessageNameSkipForward = @"skipForward";
+NSString *const VLCWatchMessageNameSkipBackward = @"skipBackward";
+NSString *const VLCWatchMessageNamePlayFile = @"playFile";
+NSString *const VLCWatchMessageNameSetVolume = @"setVolume";
+
+static NSString *const VLCWatchMessageNameKey = @"name";
+static NSString *const VLCWatchMessagePayloadKey = @"payload";
+
+@implementation VLCWatchMessage
+@synthesize dictionaryRepresentation = _dictionaryRepresentation;
+
+- (instancetype)initWithName:(NSString *)name payload:(id<NSObject, NSCoding>)payload
+{
+    self = [super init];
+    if (self) {
+        _name = [name copy];
+        _payload = payload;
+    }
+    return self;
+}
+
+- (instancetype)initWithDictionary:(NSDictionary *)dictionary
+{
+    NSString *name = dictionary[VLCWatchMessageNameKey];
+    id<NSObject> payloadObject = dictionary[VLCWatchMessagePayloadKey];
+    id payload = [self payloadFromPayloadObject:payloadObject];
+    return [self initWithName:name payload:payload];
+}
+
+- (NSDictionary *)dictionaryRepresentation
+{
+    if (!_dictionaryRepresentation) {
+        _dictionaryRepresentation = [self.class messageDictionaryForName:self.name payload:self.payload];
+    }
+    return _dictionaryRepresentation;
+}
+
+- (id)payloadFromPayloadObject:(id<NSObject>)payloadObject {
+    id payload;
+    if ([payloadObject isKindOfClass:[NSData class]]) {
+        @try {
+            payload = [NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)payloadObject];
+        }
+        @catch (NSException *exception) {
+            NSLog(@"%s Failed to decode payload with exception: %@",__PRETTY_FUNCTION__,exception);
+        }
+    } else {
+        payload = payloadObject;
+    }
+    return payload;
+}
+
++ (NSDictionary *)messageDictionaryForName:(NSString *)name payload:(id<NSObject,NSCoding>)payload
+{
+    id payloadObject;
+    BOOL noArchiving = [payload isKindOfClass:[NSNumber class]] || [payload isKindOfClass:[NSString class]];
+    if (noArchiving) {
+        payloadObject = payload;
+    } else if (payload != nil) {
+        payloadObject = [NSKeyedArchiver archivedDataWithRootObject:payload];
+    }
+    // we use nil termination so when payloadData is nil payload is not set
+    return [NSDictionary dictionaryWithObjectsAndKeys:
+            name,VLCWatchMessageNameKey,
+            payloadObject, VLCWatchMessagePayloadKey,
+            nil];
+}
++ (NSDictionary *)messageDictionaryForName:(NSString *)name
+{
+    return [self messageDictionaryForName:name payload:nil];
+}
+
+- (NSString *)debugDescription
+{
+    return [NSString stringWithFormat:@"<%@: %p name=%@, payload=%@>",NSStringFromClass(self.class), self, _name, _payload];
+}
+
+@end

+ 17 - 14
Sources/VLCAppDelegate.m

@@ -32,6 +32,7 @@
 #import "VLCNotificationRelay.h"
 #import "VLCPlaybackController.h"
 #import "VLCNavigationController.h"
+#import "VLCWatchMessage.h"
 
 #define HAVE_FABRIC 0
 
@@ -662,45 +663,47 @@
                                                                                    taskIdentifier = UIBackgroundTaskInvalid;
     }];
 
+    VLCWatchMessage *message = [[VLCWatchMessage alloc] initWithDictionary:userInfo];
+    NSString *name = message.name;
     NSDictionary *responseDict = nil;
-    if ([userInfo[@"name"] isEqualToString:@"getNowPlayingInfo"]) {
+    if ([name isEqualToString:VLCWatchMessageNameGetNowPlayingInfo]) {
         responseDict = [self nowPlayingResponseDict];
-    } else if ([userInfo[@"name"] isEqualToString:@"playpause"]) {
+    } else if ([name isEqualToString:VLCWatchMessageNamePlayPause]) {
         [[VLCPlaybackController sharedInstance] playPause];
         responseDict = @{@"playing": @([VLCPlaybackController sharedInstance].isPlaying)};
-    } else if ([userInfo[@"name"] isEqualToString:@"skipForward"]) {
+    } else if ([name isEqualToString:VLCWatchMessageNameSkipForward]) {
         [[VLCPlaybackController sharedInstance] forward];
-    } else if ([userInfo[@"name"] isEqualToString:@"skipBackward"]) {
+    } else if ([name isEqualToString:VLCWatchMessageNameSkipBackward]) {
         [[VLCPlaybackController sharedInstance] backward];
-    } else if ([userInfo[@"name"] isEqualToString:@"playFile"]) {
-        [self playFileFromWatch:userInfo[@"userInfo"]];
-    } else if ([userInfo[@"name"] isEqualToString:@"setVolume"]) {
-        [self setVolumeFromWatch:userInfo[@"userInfo"]];
+    } else if ([name isEqualToString:VLCWatchMessageNamePlayFile]) {
+        [self playFileFromWatch:message];
+    } else if ([name isEqualToString:VLCWatchMessageNameSetVolume]) {
+        [self setVolumeFromWatch:message];
     } else {
         NSLog(@"Did not handle request from WatchKit Extension: %@",userInfo);
     }
     reply(responseDict);
 }
 
-- (void)playFileFromWatch:(NSDictionary *)userInfo
+- (void)playFileFromWatch:(VLCWatchMessage *)message
 {
     NSManagedObject *managedObject = nil;
-    NSString *uriString = userInfo[@"URIRepresentation"];
-    if (uriString) {
+    NSString *uriString = (id)message.payload;
+    if ([uriString isKindOfClass:[NSString class]]) {
         NSURL *uriRepresentation = [NSURL URLWithString:uriString];
         managedObject = [[MLMediaLibrary sharedMediaLibrary] objectForURIRepresentation:uriRepresentation];
     }
     if (managedObject == nil) {
-        NSLog(@"%s file not found: %@",__PRETTY_FUNCTION__,userInfo);
+        NSLog(@"%s file not found: %@",__PRETTY_FUNCTION__,message);
         return;
     }
 
     [self openMediaFromManagedObject:managedObject];
 }
 
-- (void)setVolumeFromWatch:(NSDictionary *)userInfo
+- (void)setVolumeFromWatch:(VLCWatchMessage *)message
 {
-    NSNumber *volume = userInfo[@"volume"];
+    NSNumber *volume = (id)message.payload;
     if ([volume isKindOfClass:[NSNumber class]]) {
         /*
          * Since WatchKit doen't provide something like MPVolumeView we use deprecated API.

+ 6 - 5
VLC for iOS WatchKit Extension/VLCDetailInterfaceController.m

@@ -15,6 +15,7 @@
 #import <MobileVLCKit/MobileVLCKit.h>
 #import "VLCThumbnailsCache.h"
 #import "WKInterfaceObject+VLCProgress.h"
+#import "VLCWatchMessage.h"
 
 @interface VLCDetailInterfaceController ()
 @property (nonatomic, weak) NSManagedObject *managedObject;
@@ -86,12 +87,12 @@
 }
 
 - (IBAction)playNow {
-    NSDictionary *dict = @{@"name":@"playFile",
-                           @"userInfo":@{
-                                   @"URIRepresentation": self.managedObject.objectID.URIRepresentation.absoluteString,
-                                   }
-                           };
+
+    id payload = self.managedObject.objectID.URIRepresentation.absoluteString;
+    NSDictionary *dict = [VLCWatchMessage messageDictionaryForName:@"playFile"
+                                                           payload:payload];
     [self updateUserActivity:@"org.videolan.vlc-ios.playing" userInfo:@{@"playingmedia":self.managedObject.objectID.URIRepresentation} webpageURL:nil];
+
     [WKInterfaceController openParentApplication:dict reply:^(NSDictionary *replyInfo, NSError *error) {
         [self showNowPlaying:nil];
     }];

+ 12 - 9
VLC for iOS WatchKit Extension/VLCNowPlayingInterfaceController.m

@@ -17,6 +17,7 @@
 #import "VLCNotificationRelay.h"
 #import "VLCThumbnailsCache.h"
 #import "WKInterfaceObject+VLCProgress.h"
+#import "VLCWatchMessage.h"
 
 @interface VLCNowPlayingInterfaceController ()
 {
@@ -71,7 +72,7 @@
 }
 
 - (void)requestNowPlayingInfo {
-    [WKInterfaceController openParentApplication:@{@"name": @"getNowPlayingInfo"} reply:^(NSDictionary *replyInfo, NSError *error) {
+    [WKInterfaceController openParentApplication:[VLCWatchMessage messageDictionaryForName:VLCWatchMessageNameGetNowPlayingInfo] reply:^(NSDictionary *replyInfo, NSError *error) {
         MLFile *file = nil;
         NSString *uriString = replyInfo[@"URIRepresentation"];
         if (uriString) {
@@ -123,7 +124,8 @@
 }
 
 - (IBAction)playPausePressed {
-    [WKInterfaceController openParentApplication:@{@"name": @"playpause"} reply:^(NSDictionary *replyInfo, NSError *error) {
+    NSDictionary *dict = [VLCWatchMessage messageDictionaryForName:VLCWatchMessageNamePlayPause];
+    [WKInterfaceController openParentApplication:dict reply:^(NSDictionary *replyInfo, NSError *error) {
         NSNumber *playing = replyInfo[@"playing"];
         if ([playing isKindOfClass:[NSNumber class]]) {
             self.playing = playing.boolValue;
@@ -136,14 +138,18 @@
 }
 
 - (IBAction)skipForward {
-    [WKInterfaceController openParentApplication:@{@"name": @"skipForward"} reply:^(NSDictionary *replyInfo, NSError *error) {
+    NSDictionary *dict = [VLCWatchMessage messageDictionaryForName:VLCWatchMessageNameSkipForward];
+
+    [WKInterfaceController openParentApplication:dict reply:^(NSDictionary *replyInfo, NSError *error) {
         if (error)
             NSLog(@"skipForward failed with reply %@ error: %@",replyInfo,error);
     }];
 }
 
 - (IBAction)skipBackward {
-    [WKInterfaceController openParentApplication:@{@"name": @"skipBackward"} reply:^(NSDictionary *replyInfo, NSError *error) {
+    NSDictionary *dict = [VLCWatchMessage messageDictionaryForName:VLCWatchMessageNameSkipBackward];
+
+    [WKInterfaceController openParentApplication:dict reply:^(NSDictionary *replyInfo, NSError *error) {
         if (error)
             NSLog(@"skipBackward failed with reply %@ error: %@",replyInfo,error);
     }];
@@ -151,11 +157,8 @@
 
 - (IBAction)volumeSliderChanged:(float)value {
     _volume = value;
-    NSDictionary *dict = @{@"name" : @"setVolume",
-                           @"userInfo" : @{
-                                   @"volume" : @(value)
-                                   },
-                           };
+    NSDictionary *dict = [VLCWatchMessage messageDictionaryForName:VLCWatchMessageNameSetVolume
+                                                           payload:@(value)];
     [WKInterfaceController openParentApplication:dict reply:^(NSDictionary *replyInfo, NSError *error) {
         if (error)
             NSLog(@"setVolume failed with reply %@ error: %@",replyInfo,error);

+ 8 - 0
VLC for iOS.xcodeproj/project.pbxproj

@@ -552,6 +552,8 @@
 		DD02C30B1ACAF0370026EFEE /* libstdc++.6.0.9.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DD02C30A1ACAF0370026EFEE /* libstdc++.6.0.9.dylib */; };
 		DD02C30E1ACAF4A50026EFEE /* VLCRowController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD02C30D1ACAF4A50026EFEE /* VLCRowController.m */; };
 		DD1542121ACFF76400AFD4EC /* VLCWatchTableController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD1542111ACFF76400AFD4EC /* VLCWatchTableController.m */; };
+		DD3EA6311AF50CFE007FF096 /* VLCWatchMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = DD3EA6301AF50CFE007FF096 /* VLCWatchMessage.m */; };
+		DD3EA6321AF50D01007FF096 /* VLCWatchMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = DD3EA6301AF50CFE007FF096 /* VLCWatchMessage.m */; };
 		DD6FA7B01ACD641C006DEB2E /* VLCNowPlayingInterfaceController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD6FA7AF1ACD641C006DEB2E /* VLCNowPlayingInterfaceController.m */; };
 		DD7110F01AF38B2B00854776 /* MLMediaLibrary+playlist.m in Sources */ = {isa = PBXBuildFile; fileRef = DD7110EF1AF38B2B00854776 /* MLMediaLibrary+playlist.m */; };
 		DD7110F11AF38B2B00854776 /* MLMediaLibrary+playlist.m in Sources */ = {isa = PBXBuildFile; fileRef = DD7110EF1AF38B2B00854776 /* MLMediaLibrary+playlist.m */; };
@@ -1621,6 +1623,8 @@
 		DD02C30D1ACAF4A50026EFEE /* VLCRowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCRowController.m; sourceTree = "<group>"; };
 		DD1542101ACFF76400AFD4EC /* VLCWatchTableController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCWatchTableController.h; sourceTree = "<group>"; };
 		DD1542111ACFF76400AFD4EC /* VLCWatchTableController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCWatchTableController.m; sourceTree = "<group>"; };
+		DD3EA62F1AF50CFE007FF096 /* VLCWatchMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCWatchMessage.h; sourceTree = "<group>"; };
+		DD3EA6301AF50CFE007FF096 /* VLCWatchMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCWatchMessage.m; sourceTree = "<group>"; };
 		DD6FA7AE1ACD641C006DEB2E /* VLCNowPlayingInterfaceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCNowPlayingInterfaceController.h; sourceTree = "<group>"; };
 		DD6FA7AF1ACD641C006DEB2E /* VLCNowPlayingInterfaceController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCNowPlayingInterfaceController.m; sourceTree = "<group>"; };
 		DD7110EE1AF38B2B00854776 /* MLMediaLibrary+playlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MLMediaLibrary+playlist.h"; sourceTree = "<group>"; };
@@ -3025,6 +3029,8 @@
 			children = (
 				DD7110EE1AF38B2B00854776 /* MLMediaLibrary+playlist.h */,
 				DD7110EF1AF38B2B00854776 /* MLMediaLibrary+playlist.m */,
+				DD3EA62F1AF50CFE007FF096 /* VLCWatchMessage.h */,
+				DD3EA6301AF50CFE007FF096 /* VLCWatchMessage.m */,
 			);
 			path = SharedSources;
 			sourceTree = "<group>";
@@ -3650,6 +3656,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				DD3EA6321AF50D01007FF096 /* VLCWatchMessage.m in Sources */,
 				DD6FA7B01ACD641C006DEB2E /* VLCNowPlayingInterfaceController.m in Sources */,
 				7DE715ED1AD2DAE50075E716 /* VLCThumbnailsCache.m in Sources */,
 				7D298F451AD58A0700A0BF68 /* UIImage+Blur.m in Sources */,
@@ -3740,6 +3747,7 @@
 				7D50903218F41C7900180139 /* VLCAlertView.m in Sources */,
 				7DBBF182183AB3B80009A339 /* VLCAppDelegate.m in Sources */,
 				7D3784C0183A9938009EE944 /* VLCLinearProgressIndicator.m in Sources */,
+				DD3EA6311AF50CFE007FF096 /* VLCWatchMessage.m in Sources */,
 				41CD695D1A29D72600E60BCE /* VLCBoxTableViewController.m in Sources */,
 				7D3784C1183A9938009EE944 /* VLCMenuButton.m in Sources */,
 				41CD695C1A29D72600E60BCE /* VLCBoxController.m in Sources */,