Bläddra i källkod

WatchOS2 compiles and message based communication works

Tobias Conradi 10 år sedan
förälder
incheckning
8903090cd8

+ 3 - 0
Sources/VLCAppDelegate.h

@@ -16,6 +16,7 @@
 
 #import "VLCMenuTableViewController.h"
 #import "VLCDownloadViewController.h"
+#import "VLCWatchCommunication.h"
 
 @class VLCLibraryViewController;
 @class VLCPlayerDisplayController;
@@ -28,6 +29,8 @@ extern NSString *const VLCDropboxSessionWasAuthorized;
 
 @property (nonatomic, readonly) VLCPlayerDisplayController *playerDisplayController;
 
+@property (nonatomic, readonly) VLCWatchCommunication *watchCommunication;
+
 @property (nonatomic, strong) UIWindow *window;
 
 @property (nonatomic, readonly) BOOL passcodeValidated;

+ 4 - 76
Sources/VLCAppDelegate.m

@@ -26,7 +26,6 @@
 #import <BoxSDK/BoxSDK.h>
 #import "VLCNotificationRelay.h"
 #import "VLCPlaybackController.h"
-#import "VLCWatchMessage.h"
 #import "VLCPlaybackController+MediaLibrary.h"
 #import "VLCPlayerDisplayController.h"
 #import <MediaPlayer/MediaPlayer.h>
@@ -45,6 +44,7 @@ NSString *const VLCDropboxSessionWasAuthorized = @"VLCDropboxSessionWasAuthorize
     BOOL _passcodeValidated;
     BOOL _isRunningMigration;
     BOOL _isComingFromHandoff;
+    VLCWatchCommunication *_watchCommunication;
 }
 
 @end
@@ -168,6 +168,8 @@ NSString *const VLCDropboxSessionWasAuthorized = @"VLCDropboxSessionWasAuthorize
         setupBlock();
     }
 
+    _watchCommunication = [VLCWatchCommunication sharedInstance];
+
     VLCNotificationRelay *notificationRelay = [VLCNotificationRelay sharedRelay];
     [notificationRelay addRelayLocalName:NSManagedObjectContextDidSaveNotification toRemoteName:@"org.videolan.ios-app.dbupdate"];
     [notificationRelay addRelayLocalName:VLCPlaybackControllerPlaybackMetadataDidChange toRemoteName:kVLCDarwinNotificationNowPlayingInfoUpdate];
@@ -436,83 +438,9 @@ didFailToContinueUserActivityWithType:(NSString *)userActivityType
 handleWatchKitExtensionRequest:(NSDictionary *)userInfo
               reply:(void (^)(NSDictionary *))reply
 {
-    /* dispatch background task */
-    __block UIBackgroundTaskIdentifier taskIdentifier = [application beginBackgroundTaskWithName:nil
-                                                                               expirationHandler:^{
-                                                                                   [application endBackgroundTask:taskIdentifier];
-                                                                                   taskIdentifier = UIBackgroundTaskInvalid;
-    }];
-
-    VLCWatchMessage *message = [[VLCWatchMessage alloc] initWithDictionary:userInfo];
-    NSString *name = message.name;
-    NSDictionary *responseDict = nil;
-    if ([name isEqualToString:VLCWatchMessageNameGetNowPlayingInfo]) {
-        responseDict = [self nowPlayingResponseDict];
-    } else if ([name isEqualToString:VLCWatchMessageNamePlayPause]) {
-        [[VLCPlaybackController sharedInstance] playPause];
-        responseDict = @{@"playing": @([VLCPlaybackController sharedInstance].isPlaying)};
-    } else if ([name isEqualToString:VLCWatchMessageNameSkipForward]) {
-        [[VLCPlaybackController sharedInstance] forward];
-    } else if ([name isEqualToString:VLCWatchMessageNameSkipBackward]) {
-        [[VLCPlaybackController sharedInstance] backward];
-    } else if ([name isEqualToString:VLCWatchMessageNamePlayFile]) {
-        [self playFileFromWatch:message];
-    } else if ([name isEqualToString:VLCWatchMessageNameSetVolume]) {
-        [self setVolumeFromWatch:message];
-    } else {
-        APLog(@"Did not handle request from WatchKit Extension: %@",userInfo);
-    }
-    reply(responseDict);
-}
-
-- (void)playFileFromWatch:(VLCWatchMessage *)message
-{
-    NSManagedObject *managedObject = nil;
-    NSString *uriString = (id)message.payload;
-    if ([uriString isKindOfClass:[NSString class]]) {
-        NSURL *uriRepresentation = [NSURL URLWithString:uriString];
-        managedObject = [[MLMediaLibrary sharedMediaLibrary] objectForURIRepresentation:uriRepresentation];
-    }
-    if (managedObject == nil) {
-        APLog(@"%s file not found: %@",__PRETTY_FUNCTION__,message);
-        return;
-    }
-
-    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
-    [vpc playMediaLibraryObject:managedObject];
-}
-
-- (void)setVolumeFromWatch:(VLCWatchMessage *)message
-{
-    NSNumber *volume = (id)message.payload;
-    if ([volume isKindOfClass:[NSNumber class]]) {
-        /*
-         * Since WatchKit doesn't provide something like MPVolumeView we use deprecated API.
-         * rdar://20783803 Feature Request: WatchKit equivalent for MPVolumeView
-         */
-        [MPMusicPlayerController applicationMusicPlayer].volume = volume.floatValue;
-    }
+    [self.watchCommunication session:[WCSession defaultSession] didReceiveMessage:userInfo replyHandler:reply];
 }
 
-- (NSDictionary *)nowPlayingResponseDict {
-    NSMutableDictionary *response = [NSMutableDictionary new];
-    NSMutableDictionary *nowPlayingInfo = [[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo mutableCopy];
-    NSNumber *playbackTime = [VLCPlaybackController sharedInstance].mediaPlayer.time.numberValue;
-    if (playbackTime) {
-        nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = @(playbackTime.floatValue/1000);
-    }
-    if (nowPlayingInfo) {
-        response[@"nowPlayingInfo"] = nowPlayingInfo;
-    }
-    MLFile *currentFile = [VLCPlaybackController sharedInstance].currentlyPlayingMediaFile;
-    NSString *URIString = currentFile.objectID.URIRepresentation.absoluteString;
-    if (URIString) {
-        response[@"URIRepresentation"] = URIString;
-    }
 
-    response[@"volume"] = @([MPMusicPlayerController applicationMusicPlayer].volume);
-
-    return response;
-}
 
 @end

+ 42 - 39
Sources/VLCThumbnailsCache.m

@@ -22,10 +22,10 @@
 #import <MediaLibraryKit/MediaLibraryKit.h>
 
 @interface VLCThumbnailsCache() {
-    NSInteger _maximalCacheSize;
+    NSInteger MaxCacheSize;
     NSCache *_thumbnailCache;
     NSCache *_thumbnailCacheMetadata;
-    CGSize _imageSize;
+    NSInteger _currentDeviceIdiom;
 }
 @end
 
@@ -39,50 +39,30 @@
 {
     self = [super init];
     if (self) {
-#if TARGET_OS_WATCH
-        CGRect screenRect = WKInterfaceDevice.currentDevice.screenBounds;
-        _imageSize = CGSizeMake(screenRect.size.width * WKInterfaceDevice.currentDevice.screenScale, 120.);
-        _maximalCacheSize = MAX_CACHE_SIZE_WATCH;
-#else
-        NSInteger currentDeviceIdiom = [[UIDevice currentDevice] userInterfaceIdiom];
+// TODO: correct for watch
+#ifndef TARGET_OS_WATCH
+        _currentDeviceIdiom = [[UIDevice currentDevice] userInterfaceIdiom];
+        MaxCacheSize = 0;
 
-        switch (currentDeviceIdiom) {
+        switch (_currentDeviceIdiom) {
             case UIUserInterfaceIdiomPad:
-                _maximalCacheSize = MAX_CACHE_SIZE_IPAD;
+                MaxCacheSize = MAX_CACHE_SIZE_IPAD;
                 break;
             case UIUserInterfaceIdiomPhone:
-                _maximalCacheSize = MAX_CACHE_SIZE_IPHONE;
+                MaxCacheSize = MAX_CACHE_SIZE_IPHONE;
                 break;
 
             default:
-                _maximalCacheSize = MAX_CACHE_SIZE_WATCH;
+                MaxCacheSize = MAX_CACHE_SIZE_WATCH;
                 break;
         }
-
-        if (currentDeviceIdiom == UIUserInterfaceIdiomPad) {
-            if ([UIScreen mainScreen].scale==2.0)
-                _imageSize = CGSizeMake(682., 384.);
-            else
-                _imageSize = CGSizeMake(341., 192.);
-        } else if (currentDeviceIdiom == UIUserInterfaceIdiomPhone) {
-            if ([UIScreen mainScreen].scale==2.0)
-                _imageSize = CGSizeMake(480., 270.);
-            else
-                _imageSize = CGSizeMake(720., 405.);
-        } else {
-            if ([WKInterfaceDevice class]) {
-                if (WKInterfaceDevice.currentDevice != nil) {
-                    CGRect screenRect = WKInterfaceDevice.currentDevice.screenBounds;
-                    _imageSize = CGSizeMake(screenRect.size.width * WKInterfaceDevice.currentDevice.screenScale, 120.);
-                }
-            }
-        }
+#else
+        MaxCacheSize = MAX_CACHE_SIZE_WATCH;
 #endif
-
         _thumbnailCache = [[NSCache alloc] init];
         _thumbnailCacheMetadata = [[NSCache alloc] init];
-        [_thumbnailCache setCountLimit: _maximalCacheSize];
-        [_thumbnailCacheMetadata setCountLimit: _maximalCacheSize];
+        [_thumbnailCache setCountLimit: MaxCacheSize];
+        [_thumbnailCacheMetadata setCountLimit: MaxCacheSize];
     }
     return self;
 }
@@ -283,22 +263,45 @@
 - (UIImage *)clusterThumbFromFiles:(NSArray *)files andNumber:(NSUInteger)fileNumber blur:(BOOL)blurImage
 {
     UIImage *clusterThumb;
+    CGSize imageSize;
+    // TODO: correct for watch
+#ifndef TARGET_OS_WATCH
+    if (_currentDeviceIdiom == UIUserInterfaceIdiomPad) {
+        if ([UIScreen mainScreen].scale==2.0)
+            imageSize = CGSizeMake(682., 384.);
+        else
+            imageSize = CGSizeMake(341., 192.);
+    } else if (_currentDeviceIdiom == UIUserInterfaceIdiomPhone) {
+        if ([UIScreen mainScreen].scale==2.0)
+            imageSize = CGSizeMake(480., 270.);
+        else
+            imageSize = CGSizeMake(720., 405.);
+    } else
+#endif
+    {
+        if ([WKInterfaceDevice class]) {
+            if (WKInterfaceDevice.currentDevice != nil) {
+                CGRect screenRect = WKInterfaceDevice.currentDevice.screenBounds;
+                imageSize = CGSizeMake(screenRect.size.width * WKInterfaceDevice.currentDevice.screenScale, 120.);
+            }
+        }
+    }
 
-    UIGraphicsBeginImageContext(_imageSize);
+    UIGraphicsBeginImageContext(imageSize);
     NSUInteger iter = files.count < fileNumber ? files.count : fileNumber;
     for (NSUInteger i = 0; i < iter; i++) {
         MLFile *file =  [files objectAtIndex:i];
         clusterThumb = [self thumbnailForMediaFile:file refreshCache:NO];
         CGContextRef context = UIGraphicsGetCurrentContext();
-        CGFloat imagePartWidth = (_imageSize.width / iter);
+        CGFloat imagePartWidth = (imageSize.width / iter);
         //the rect in which the image should be drawn
-        CGRect clippingRect = CGRectMake(imagePartWidth * i, 0, imagePartWidth, _imageSize.height);
+        CGRect clippingRect = CGRectMake(imagePartWidth * i, 0, imagePartWidth, imageSize.height);
         CGContextSaveGState(context);
         CGContextClipToRect(context, clippingRect);
         //take the center of the clippingRect and calculate the offset from the original center
-        CGFloat centerOffset = (imagePartWidth * i + imagePartWidth / 2) - _imageSize.width / 2;
+        CGFloat centerOffset = (imagePartWidth * i + imagePartWidth / 2) - imageSize.width / 2;
         //shift the rect to draw the middle of the image in the clippingrect
-        CGRect drawingRect = CGRectMake(centerOffset, 0, _imageSize.width, _imageSize.height);
+        CGRect drawingRect = CGRectMake(centerOffset, 0, imageSize.width, imageSize.height);
         [clusterThumb drawInRect:drawingRect];
         //get rid of the old clippingRect
         CGContextRestoreGState(context);

+ 19 - 0
Sources/VLCWatchCommunication.h

@@ -0,0 +1,19 @@
+/*****************************************************************************
+ * VLCWatchCommunication.h
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2015 VideoLAN. All rights reserved.
+ * $Id$
+ *
+ * Author: Tobias Conradi <videolan # tobias-conradi.de>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+#import <Foundation/Foundation.h>
+#import <WatchConnectivity/WatchConnectivity.h>
+
+@interface VLCWatchCommunication : NSObject <WCSessionDelegate>
+
++ (instancetype)sharedInstance;
+
+@end

+ 129 - 0
Sources/VLCWatchCommunication.m

@@ -0,0 +1,129 @@
+/*****************************************************************************
+ * VLCWatchCommunication.m
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2015 VideoLAN. All rights reserved.
+ * $Id$
+ *
+ * Author: Tobias Conradi <videolan # tobias-conradi.de>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+
+#import "VLCWatchCommunication.h"
+#import "VLCWatchMessage.h"
+#import "VLCPlaybackController+MediaLibrary.h"
+#import <MediaPlayer/MediaPlayer.h>
+
+@implementation VLCWatchCommunication
+
+- (instancetype)init
+{
+    self = [super init];
+    if (self) {
+        if ([WCSession isSupported]) {
+            WCSession *session = [WCSession defaultSession];
+            session.delegate = self;
+            [session activateSession];
+        }
+    }
+    return self;
+}
+
+static VLCWatchCommunication *_singeltonInstance = nil;
+
++ (VLCWatchCommunication *)sharedInstance
+{
+    @synchronized(self) {
+        static dispatch_once_t pred;
+        dispatch_once(&pred, ^{
+            _singeltonInstance = [[self alloc] init];
+        });
+    }
+    return _singeltonInstance;
+}
+
+- (void)playFileFromWatch:(VLCWatchMessage *)message
+{
+    NSManagedObject *managedObject = nil;
+    NSString *uriString = (id)message.payload;
+    if ([uriString isKindOfClass:[NSString class]]) {
+        NSURL *uriRepresentation = [NSURL URLWithString:uriString];
+        managedObject = [[MLMediaLibrary sharedMediaLibrary] objectForURIRepresentation:uriRepresentation];
+    }
+    if (managedObject == nil) {
+        APLog(@"%s file not found: %@",__PRETTY_FUNCTION__,message);
+        return;
+    }
+
+    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
+    [vpc playMediaLibraryObject:managedObject];
+}
+
+- (void)session:(nonnull WCSession *)session didReceiveMessage:(nonnull NSDictionary<NSString *,id> *)userInfo replyHandler:(nonnull void (^)(NSDictionary<NSString *,id> * _Nonnull))replyHandler {
+    UIApplication *application = [UIApplication sharedApplication];
+    /* dispatch background task */
+    __block UIBackgroundTaskIdentifier taskIdentifier = [application beginBackgroundTaskWithName:nil
+                                                                               expirationHandler:^{
+                                                                                   [application endBackgroundTask:taskIdentifier];
+                                                                                   taskIdentifier = UIBackgroundTaskInvalid;
+                                                                               }];
+
+    VLCWatchMessage *message = [[VLCWatchMessage alloc] initWithDictionary:userInfo];
+    NSString *name = message.name;
+    NSDictionary *responseDict = nil;
+    if ([name isEqualToString:VLCWatchMessageNameGetNowPlayingInfo]) {
+        responseDict = [self nowPlayingResponseDict];
+    } else if ([name isEqualToString:VLCWatchMessageNamePlayPause]) {
+        [[VLCPlaybackController sharedInstance] playPause];
+        responseDict = @{@"playing": @([VLCPlaybackController sharedInstance].isPlaying)};
+    } else if ([name isEqualToString:VLCWatchMessageNameSkipForward]) {
+        [[VLCPlaybackController sharedInstance] forward];
+    } else if ([name isEqualToString:VLCWatchMessageNameSkipBackward]) {
+        [[VLCPlaybackController sharedInstance] backward];
+    } else if ([name isEqualToString:VLCWatchMessageNamePlayFile]) {
+        [self playFileFromWatch:message];
+    } else if ([name isEqualToString:VLCWatchMessageNameSetVolume]) {
+        [self setVolumeFromWatch:message];
+    } else {
+        APLog(@"Did not handle request from WatchKit Extension: %@",userInfo);
+    }
+    replyHandler(responseDict);
+}
+
+
+- (void)setVolumeFromWatch:(VLCWatchMessage *)message
+{
+    NSNumber *volume = (id)message.payload;
+    if ([volume isKindOfClass:[NSNumber class]]) {
+        /*
+         * Since WatchKit doesn't provide something like MPVolumeView we use deprecated API.
+         * rdar://20783803 Feature Request: WatchKit equivalent for MPVolumeView
+         */
+        [MPMusicPlayerController applicationMusicPlayer].volume = volume.floatValue;
+    }
+}
+
+- (NSDictionary *)nowPlayingResponseDict {
+    NSMutableDictionary *response = [NSMutableDictionary new];
+    NSMutableDictionary *nowPlayingInfo = [[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo mutableCopy];
+    NSNumber *playbackTime = [VLCPlaybackController sharedInstance].mediaPlayer.time.numberValue;
+    if (playbackTime) {
+        nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = @(playbackTime.floatValue/1000);
+    }
+    if (nowPlayingInfo) {
+        response[@"nowPlayingInfo"] = nowPlayingInfo;
+    }
+    MLFile *currentFile = [VLCPlaybackController sharedInstance].currentlyPlayingMediaFile;
+    NSString *URIString = currentFile.objectID.URIRepresentation.absoluteString;
+    if (URIString) {
+        response[@"URIRepresentation"] = URIString;
+    }
+
+    response[@"volume"] = @([MPMusicPlayerController applicationMusicPlayer].volume);
+
+    return response;
+}
+
+@end

+ 5 - 1
VLC WatchKit Native Extension/Classes/ExtensionDelegate.swift

@@ -7,11 +7,15 @@
 //
 
 import WatchKit
+import WatchConnectivity
 
-class ExtensionDelegate: NSObject, WKExtensionDelegate {
+class ExtensionDelegate: NSObject, WKExtensionDelegate, WCSessionDelegate {
 
     func applicationDidFinishLaunching() {
         // Perform any final initialization of your application.
+        WCSession.defaultSession().delegate = self;
+        WCSession.defaultSession().activateSession()
+
     }
 
     func applicationDidBecomeActive() {

+ 2 - 16
VLC WatchKit Native Extension/VLC for iOS WatchKit Extension-Prefix.pch

@@ -21,22 +21,8 @@
     #import <Foundation/Foundation.h>
 #endif
 
-#import "VLCAudio.h"
-#import "VLCLibrary.h"
-#import "VLCMedia.h"
-#import "VLCMediaDiscoverer.h"
-#import "VLCMediaList.h"
-#import "VLCMediaPlayer.h"
-#import "VLCMediaListPlayer.h"
-#import "VLCMediaThumbnailer.h"
-#import "VLCTime.h"
-#import "MLFile.h"
-#import "MLLabel.h"
-#import "MLMediaLibrary.h"
-#import "MLShow.h"
-#import "MLShowEpisode.h"
-#import "MLAlbum.h"
-#import "MLAlbumTrack.h"
+#import <MediaLibraryKit/MediaLibraryKit.h>
+
 
 #ifndef NDEBUG
 #define APLog(format, ...) NSLog(format, ## __VA_ARGS__)

+ 43 - 3
VLC for iOS.xcodeproj/project.pbxproj

@@ -215,6 +215,12 @@
 		D9C52A9E9D4D5AFA7EF1B45A /* libPods-vlc-ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAF8927B0BE9C328466C0EA7 /* libPods-vlc-ios.a */; };
 		DD1574761B67BBDB00641E8E /* VLCNotificationRelay.m in Sources */ = {isa = PBXBuildFile; fileRef = DD1574751B67BBDB00641E8E /* VLCNotificationRelay.m */; };
 		DD1A45FD1B676BAC00086F57 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DDE1BCE41B676B8800A4B9CE /* Localizable.strings */; };
+		DD2789DC1B67A44200CED769 /* MediaLibraryKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD2789DB1B67A44200CED769 /* MediaLibraryKit.framework */; };
+		DD2789DD1B67A5C400CED769 /* VLCThumbnailsCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D37849D183A98DD009EE944 /* VLCThumbnailsCache.m */; };
+		DD2789DE1B67A5CD00CED769 /* VLCWatchMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = DD3EA6301AF50CFE007FF096 /* VLCWatchMessage.m */; };
+		DD2789E21B67A7BE00CED769 /* VLCWatchCommunication.m in Sources */ = {isa = PBXBuildFile; fileRef = DD2789E11B67A7BE00CED769 /* VLCWatchCommunication.m */; };
+		DD2789E41B67A88600CED769 /* WatchConnectivity.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD2789E31B67A88600CED769 /* WatchConnectivity.framework */; };
+		DD2789E51B67A8F000CED769 /* MLMediaLibrary+playlist.m in Sources */ = {isa = PBXBuildFile; fileRef = DD7110EF1AF38B2B00854776 /* MLMediaLibrary+playlist.m */; };
 		DD3567681B6760BF00338947 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DD3567661B6760BF00338947 /* Interface.storyboard */; };
 		DD35676A1B6760BF00338947 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DD3567691B6760BF00338947 /* Assets.xcassets */; };
 		DD3567711B6760BF00338947 /* VLC WatchKit Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = DD3567701B6760BF00338947 /* VLC WatchKit Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@@ -222,7 +228,6 @@
 		DD35677E1B6760BF00338947 /* VLC WatchKit App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = DD3567641B6760BF00338947 /* VLC WatchKit App.app */; };
 		DD35678A1B67618F00338947 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD3567891B67618F00338947 /* CoreData.framework */; };
 		DD35678C1B67619700338947 /* WatchKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD35678B1B67619700338947 /* WatchKit.framework */; };
-		DD35678E1B6761A600338947 /* libMediaLibraryKit-readonly.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DD35678D1B6761A600338947 /* libMediaLibraryKit-readonly.a */; };
 		DD3567901B6761CE00338947 /* WatchConnectivity.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD35678F1B6761CD00338947 /* WatchConnectivity.framework */; };
 		DD3567B41B67674700338947 /* VLCTime.m in Sources */ = {isa = PBXBuildFile; fileRef = DD3567B31B67674700338947 /* VLCTime.m */; };
 		DD3567EF1B6768FC00338947 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3567D91B6768FC00338947 /* ExtensionDelegate.swift */; };
@@ -689,6 +694,10 @@
 		DD1574741B67BBDB00641E8E /* VLCNotificationRelay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCNotificationRelay.h; sourceTree = "<group>"; };
 		DD1574751B67BBDB00641E8E /* VLCNotificationRelay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCNotificationRelay.m; sourceTree = "<group>"; };
 		DD1A45FC1B676BAC00086F57 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
+		DD2789DB1B67A44200CED769 /* MediaLibraryKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaLibraryKit.framework; path = "ImportedSources/MediaLibraryKit/build/Debug-watchos/MediaLibraryKit.framework"; sourceTree = "<group>"; };
+		DD2789E01B67A7BE00CED769 /* VLCWatchCommunication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCWatchCommunication.h; path = Sources/VLCWatchCommunication.h; sourceTree = SOURCE_ROOT; };
+		DD2789E11B67A7BE00CED769 /* VLCWatchCommunication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCWatchCommunication.m; path = Sources/VLCWatchCommunication.m; sourceTree = SOURCE_ROOT; };
+		DD2789E31B67A88600CED769 /* WatchConnectivity.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WatchConnectivity.framework; path = System/Library/Frameworks/WatchConnectivity.framework; sourceTree = SDKROOT; };
 		DD3567641B6760BF00338947 /* VLC WatchKit App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "VLC WatchKit App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		DD3567671B6760BF00338947 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = "<group>"; };
 		DD3567691B6760BF00338947 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -698,7 +707,6 @@
 		DD35677B1B6760BF00338947 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		DD3567891B67618F00338947 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS2.0.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; };
 		DD35678B1B67619700338947 /* WatchKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WatchKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS2.0.sdk/System/Library/Frameworks/WatchKit.framework; sourceTree = DEVELOPER_DIR; };
-		DD35678D1B6761A600338947 /* libMediaLibraryKit-readonly.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libMediaLibraryKit-readonly.a"; path = "../../../Library/Developer/Xcode/DerivedData/VLC_for_iOS-asxdcuzswmictvazfgdbzoqjdhaf/Build/Products/Debug-iphoneos/libMediaLibraryKit-readonly.a"; sourceTree = "<group>"; };
 		DD35678F1B6761CD00338947 /* WatchConnectivity.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WatchConnectivity.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS2.0.sdk/System/Library/Frameworks/WatchConnectivity.framework; sourceTree = DEVELOPER_DIR; };
 		DD3567B31B67674700338947 /* VLCTime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCTime.m; path = ImportedSources/VLCKit/Sources/VLCTime.m; sourceTree = SOURCE_ROOT; };
 		DD3567B51B67675400338947 /* VLCTime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = VLCTime.h; path = ImportedSources/VLCKit/Headers/Public/VLCTime.h; sourceTree = SOURCE_ROOT; };
@@ -762,6 +770,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				DD2789E41B67A88600CED769 /* WatchConnectivity.framework in Frameworks */,
 				7D2DF7C51B677C2600FB78AB /* libMobileVLCKit.a in Frameworks */,
 				7D2DF7C31B67777D00FB78AB /* libc++.tbd in Frameworks */,
 				7D2DF7C11B67760100FB78AB /* libGTLTouchStaticLib.a in Frameworks */,
@@ -801,8 +810,8 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				DD2789DC1B67A44200CED769 /* MediaLibraryKit.framework in Frameworks */,
 				DD3567901B6761CE00338947 /* WatchConnectivity.framework in Frameworks */,
-				DD35678E1B6761A600338947 /* libMediaLibraryKit-readonly.a in Frameworks */,
 				DD35678C1B67619700338947 /* WatchKit.framework in Frameworks */,
 				DD35678A1B67618F00338947 /* CoreData.framework in Frameworks */,
 			);
@@ -1232,6 +1241,8 @@
 		7D94FCDD16DE7D1000F2623B /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				DD2789E31B67A88600CED769 /* WatchConnectivity.framework */,
+				DD2789DB1B67A44200CED769 /* MediaLibraryKit.framework */,
 				7D2DF7C41B677C2600FB78AB /* libMobileVLCKit.a */,
 				7D2DF7C21B67777D00FB78AB /* libc++.tbd */,
 				7D2DF7C01B67760100FB78AB /* libGTLTouchStaticLib.a */,
@@ -1280,6 +1291,7 @@
 				7D6B08BB174A72A900A05173 /* VLCConstants.h */,
 				7DBBF180183AB3B80009A339 /* VLCAppDelegate.h */,
 				7DBBF181183AB3B80009A339 /* VLCAppDelegate.m */,
+				DD2789DF1B67A79700CED769 /* WatchSupport */,
 				7DBB788E1B305D8300894467 /* Keychain & random singletons */,
 				A7D03A4817A4249F0022C16F /* MediaDiscovering */,
 				7D2339AB176DE70E008D223C /* Menu */,
@@ -1558,6 +1570,15 @@
 			name = "External VLC Libraries";
 			sourceTree = "<group>";
 		};
+		DD2789DF1B67A79700CED769 /* WatchSupport */ = {
+			isa = PBXGroup;
+			children = (
+				DD2789E01B67A7BE00CED769 /* VLCWatchCommunication.h */,
+				DD2789E11B67A7BE00CED769 /* VLCWatchCommunication.m */,
+			);
+			name = WatchSupport;
+			sourceTree = "<group>";
+		};
 		DD3567651B6760BF00338947 /* VLC WatchKit Native */ = {
 			isa = PBXGroup;
 			children = (
@@ -1964,6 +1985,7 @@
 				2915543E17490B9C00B86CAD /* HTTPErrorResponse.m in Sources */,
 				E0C04F951A25B4410080331A /* VLCDocumentPickerController.m in Sources */,
 				2915543F17490B9C00B86CAD /* HTTPFileResponse.m in Sources */,
+				DD2789E21B67A7BE00CED769 /* VLCWatchCommunication.m in Sources */,
 				41B93C011A53833B00102E8B /* VLCProgressView.m in Sources */,
 				417CDA231A48D1F300D9ACE7 /* VLCCloudServicesTableViewController.m in Sources */,
 				2915544017490B9C00B86CAD /* HTTPRedirectResponse.m in Sources */,
@@ -2059,11 +2081,14 @@
 				DD3567EF1B6768FC00338947 /* ExtensionDelegate.swift in Sources */,
 				DD3567F81B6768FC00338947 /* VLCWatchTableController.m in Sources */,
 				DD3567F61B6768FC00338947 /* VLCPlaylistInterfaceController.m in Sources */,
+				DD2789E51B67A8F000CED769 /* MLMediaLibrary+playlist.m in Sources */,
 				DD3567F31B6768FC00338947 /* VLCDetailInterfaceController.m in Sources */,
+				DD2789DE1B67A5CD00CED769 /* VLCWatchMessage.m in Sources */,
 				DD3567F71B6768FC00338947 /* VLCRowController.m in Sources */,
 				DD3567F21B6768FC00338947 /* VLCBaseInterfaceController.m in Sources */,
 				DD3567B41B67674700338947 /* VLCTime.m in Sources */,
 				DD3567F41B6768FC00338947 /* VLCNotificationRelay.m in Sources */,
+				DD2789DD1B67A5C400CED769 /* VLCThumbnailsCache.m in Sources */,
 				DD3567F51B6768FC00338947 /* VLCNowPlayingInterfaceController.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -2524,6 +2549,10 @@
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/ImportedSources/MediaLibraryKit/build/Debug-watchos",
+				);
 				GCC_NO_COMMON_BLOCKS = YES;
 				GCC_OPTIMIZATION_LEVEL = 0;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -2538,6 +2567,7 @@
 					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
 					"$(SRCROOT)/ImportedSources/VLCKit/Headers/Public",
 					"$(SRCROOT)/ImportedSources/MediaLibraryKit/Headers/Public",
+					"$(SRCROOT)/Sources",
 				);
 				INFOPLIST_FILE = "$(SRCROOT)/VLC WatchKit Native Extension/Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
@@ -2567,6 +2597,10 @@
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				ENABLE_NS_ASSERTIONS = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/ImportedSources/MediaLibraryKit/build/Debug-watchos",
+				);
 				GCC_NO_COMMON_BLOCKS = YES;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
 				GCC_PREFIX_HEADER = "VLC WatchKit Native Extension/VLC for iOS WatchKit Extension-Prefix.pch";
@@ -2580,6 +2614,7 @@
 					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
 					"$(SRCROOT)/ImportedSources/VLCKit/Headers/Public",
 					"$(SRCROOT)/ImportedSources/MediaLibraryKit/Headers/Public",
+					"$(SRCROOT)/Sources",
 				);
 				INFOPLIST_FILE = "$(SRCROOT)/VLC WatchKit Native Extension/Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
@@ -2608,6 +2643,10 @@
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				ENABLE_NS_ASSERTIONS = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/ImportedSources/MediaLibraryKit/build/Debug-watchos",
+				);
 				GCC_NO_COMMON_BLOCKS = YES;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
 				GCC_PREFIX_HEADER = "VLC WatchKit Native Extension/VLC for iOS WatchKit Extension-Prefix.pch";
@@ -2621,6 +2660,7 @@
 					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
 					"$(SRCROOT)/ImportedSources/VLCKit/Headers/Public",
 					"$(SRCROOT)/ImportedSources/MediaLibraryKit/Headers/Public",
+					"$(SRCROOT)/Sources",
 				);
 				INFOPLIST_FILE = "$(SRCROOT)/VLC WatchKit Native Extension/Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";