VLCWatchCommunication.m 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /*****************************************************************************
  2. * VLCWatchCommunication.m
  3. * VLC for iOS
  4. *****************************************************************************
  5. * Copyright (c) 2015 VideoLAN. All rights reserved.
  6. * $Id$
  7. *
  8. * Author: Tobias Conradi <videolan # tobias-conradi.de>
  9. *
  10. * Refer to the COPYING file of the official project for license.
  11. *****************************************************************************/
  12. #import "VLCWatchCommunication.h"
  13. #import "VLCWatchMessage.h"
  14. #import "VLCPlaybackController+MediaLibrary.h"
  15. #import <MediaPlayer/MediaPlayer.h>
  16. @implementation VLCWatchCommunication
  17. + (BOOL)isSupported {
  18. return [WCSession class] != nil && [WCSession isSupported];
  19. }
  20. - (instancetype)init
  21. {
  22. self = [super init];
  23. if (self) {
  24. if ([WCSession isSupported]) {
  25. WCSession *session = [WCSession defaultSession];
  26. session.delegate = self;
  27. [session activateSession];
  28. }
  29. }
  30. return self;
  31. }
  32. - (void)dealloc {
  33. [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:nil];
  34. }
  35. static VLCWatchCommunication *_singeltonInstance = nil;
  36. + (VLCWatchCommunication *)sharedInstance
  37. {
  38. @synchronized(self) {
  39. static dispatch_once_t pred;
  40. dispatch_once(&pred, ^{
  41. _singeltonInstance = [[self alloc] init];
  42. });
  43. }
  44. return _singeltonInstance;
  45. }
  46. - (void)playFileFromWatch:(VLCWatchMessage *)message
  47. {
  48. NSManagedObject *managedObject = nil;
  49. NSString *uriString = (id)message.payload;
  50. if ([uriString isKindOfClass:[NSString class]]) {
  51. NSURL *uriRepresentation = [NSURL URLWithString:uriString];
  52. managedObject = [[MLMediaLibrary sharedMediaLibrary] objectForURIRepresentation:uriRepresentation];
  53. }
  54. if (managedObject == nil) {
  55. APLog(@"%s file not found: %@",__PRETTY_FUNCTION__,message);
  56. return;
  57. }
  58. VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  59. [vpc playMediaLibraryObject:managedObject];
  60. }
  61. - (NSDictionary *)handleMessage:(nonnull VLCWatchMessage *)message {
  62. UIApplication *application = [UIApplication sharedApplication];
  63. /* dispatch background task */
  64. __block UIBackgroundTaskIdentifier taskIdentifier = [application beginBackgroundTaskWithName:nil
  65. expirationHandler:^{
  66. [application endBackgroundTask:taskIdentifier];
  67. taskIdentifier = UIBackgroundTaskInvalid;
  68. }];
  69. NSString *name = message.name;
  70. NSDictionary *responseDict = @{};
  71. if ([name isEqualToString:VLCWatchMessageNameGetNowPlayingInfo]) {
  72. responseDict = [self nowPlayingResponseDict];
  73. } else if ([name isEqualToString:VLCWatchMessageNamePlayPause]) {
  74. [[VLCPlaybackController sharedInstance] playPause];
  75. responseDict = @{@"playing": @([VLCPlaybackController sharedInstance].isPlaying)};
  76. } else if ([name isEqualToString:VLCWatchMessageNameSkipForward]) {
  77. [[VLCPlaybackController sharedInstance] forward];
  78. } else if ([name isEqualToString:VLCWatchMessageNameSkipBackward]) {
  79. [[VLCPlaybackController sharedInstance] backward];
  80. } else if ([name isEqualToString:VLCWatchMessageNamePlayFile]) {
  81. [self playFileFromWatch:message];
  82. } else if ([name isEqualToString:VLCWatchMessageNameSetVolume]) {
  83. [self setVolumeFromWatch:message];
  84. } else {
  85. APLog(@"Did not handle request from WatchKit Extension: %@",message);
  86. }
  87. return responseDict;
  88. }
  89. - (void)session:(nonnull WCSession *)session didReceiveMessage:(nonnull NSDictionary<NSString *,id> *)userInfo replyHandler:(nonnull void (^)(NSDictionary<NSString *,id> * _Nonnull))replyHandler {
  90. VLCWatchMessage *message = [[VLCWatchMessage alloc] initWithDictionary:userInfo];
  91. NSDictionary *responseDict = [self handleMessage:message];
  92. replyHandler(responseDict);
  93. }
  94. - (void)session:(nonnull WCSession *)session didReceiveMessage:(nonnull NSDictionary<NSString *,id> *)messageDict {
  95. VLCWatchMessage *message = [[VLCWatchMessage alloc] initWithDictionary:messageDict];
  96. [self handleMessage:message];
  97. }
  98. - (void)setVolumeFromWatch:(VLCWatchMessage *)message
  99. {
  100. NSNumber *volume = (id)message.payload;
  101. if ([volume isKindOfClass:[NSNumber class]]) {
  102. /*
  103. * Since WatchKit doesn't provide something like MPVolumeView we use deprecated API.
  104. * rdar://20783803 Feature Request: WatchKit equivalent for MPVolumeView
  105. */
  106. [MPMusicPlayerController applicationMusicPlayer].volume = volume.floatValue;
  107. }
  108. }
  109. - (NSDictionary *)nowPlayingResponseDict {
  110. NSMutableDictionary *response = [NSMutableDictionary new];
  111. NSMutableDictionary *nowPlayingInfo = [[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo mutableCopy];
  112. NSNumber *playbackTime = [VLCPlaybackController sharedInstance].mediaPlayer.time.numberValue;
  113. if (playbackTime) {
  114. nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = @(playbackTime.floatValue/1000);
  115. }
  116. if (nowPlayingInfo) {
  117. response[@"nowPlayingInfo"] = nowPlayingInfo;
  118. }
  119. MLFile *currentFile = [VLCPlaybackController sharedInstance].currentlyPlayingMediaFile;
  120. NSString *URIString = currentFile.objectID.URIRepresentation.absoluteString;
  121. if (URIString) {
  122. response[@"URIRepresentation"] = URIString;
  123. }
  124. response[@"volume"] = @([MPMusicPlayerController applicationMusicPlayer].volume);
  125. return response;
  126. }
  127. #pragma mark - Notifications
  128. - (void)startRelayingNotificationName:(nullable NSString *)name object:(nullable id)object {
  129. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(relayNotification:) name:name object:object];
  130. }
  131. - (void)stopRelayingNotificationName:(nullable NSString *)name object:(nullable id)object {
  132. [[NSNotificationCenter defaultCenter] removeObserver:self name:name object:object];
  133. }
  134. - (void)relayNotification:(NSNotification *)notification {
  135. NSMutableDictionary *payload = [NSMutableDictionary dictionary];
  136. payload[@"name"] = notification.name;
  137. if (notification.userInfo) {
  138. payload[@"userInfo"] = notification.userInfo;
  139. }
  140. NSDictionary *dict = [VLCWatchMessage messageDictionaryForName:VLCWatchMessageNameNotification
  141. payload:payload];
  142. if ([WCSession isSupported] && [[WCSession defaultSession] isReachable]) {
  143. [[WCSession defaultSession] sendMessage:dict replyHandler:nil errorHandler:nil];
  144. }
  145. }
  146. @end