VLCAppDelegate.m 21.8 KB
Newer Older
1
2
3
4
/*****************************************************************************
 * VLCAppDelegate.m
 * VLC for iOS
 *****************************************************************************
Felix Paul Kühne's avatar
Felix Paul Kühne committed
5
 * Copyright (c) 2013-2015 VideoLAN. All rights reserved.
6
7
8
9
10
11
12
 * $Id$
 *
 * Authors: Felix Paul Kühne <fkuehne # videolan.org>
 *          Gleb Pinigin <gpinigin # gmail.com>
 *          Jean-Romain Prévost <jr # 3on.fr>
 *          Luis Fernandes <zipleen # gmail.com>
 *          Carola Nitz <nitz.carola # googlemail.com>
13
 *          Tamas Timar <ttimar.vlc # gmail.com>
Tobias's avatar
Tobias committed
14
 *          Tobias Conradi <videolan # tobias-conradi.de>
15
16
17
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/
Felix Paul Kühne's avatar
Felix Paul Kühne committed
18
19

#import "VLCAppDelegate.h"
Gleb Pinigin's avatar
Gleb Pinigin committed
20
#import "VLCMediaFileDiscoverer.h"
21
#import "NSString+SupportedMedia.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
22
#import "UIDevice+VLC.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
23
#import "VLCPlaylistViewController.h"
24
#import "VLCHTTPUploaderController.h"
Carola's avatar
Carola committed
25
#import "VLCMigrationViewController.h"
Carola's avatar
Carola committed
26
#import <BoxSDK/BoxSDK.h>
27
#import "VLCNotificationRelay.h"
28
#import "VLCPlaybackController.h"
29
#import "VLCWatchMessage.h"
30
#import "VLCPlaybackController+MediaLibrary.h"
31
#import "VLCPlayerDisplayController.h"
Tobias's avatar
Tobias committed
32
#import <MediaPlayer/MediaPlayer.h>
33
#import <DropboxSDK/DropboxSDK.h>
Felix Paul Kühne's avatar
Felix Paul Kühne committed
34
#import <HockeySDK/HockeySDK.h>
35
#import "VLCSidebarController.h"
36
#import "VLCKeychainCoordinator.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
37
#import "VLCActivityManager.h"
38
39

NSString *const VLCDropboxSessionWasAuthorized = @"VLCDropboxSessionWasAuthorized";
40

Felix Paul Kühne's avatar
Felix Paul Kühne committed
41
42
#define BETA_DISTRIBUTION 1

43
44
@interface VLCAppDelegate () <VLCMediaFileDiscovererDelegate>
{
45
    BOOL _passcodeValidated;
Carola's avatar
Carola committed
46
    BOOL _isRunningMigration;
Carola's avatar
Carola committed
47
    BOOL _isComingFromHandoff;
Gleb Pinigin's avatar
Gleb Pinigin committed
48
}
49
50

@end
Felix Paul Kühne's avatar
Felix Paul Kühne committed
51
52
53

@implementation VLCAppDelegate

54
55
56
57
+ (void)initialize
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

58
59
60
61
62
    NSNumber *skipLoopFilterDefaultValue;
    int deviceSpeedCategory = [[UIDevice currentDevice] speedCategory];
    if (deviceSpeedCategory < 3)
        skipLoopFilterDefaultValue = kVLCSettingSkipLoopFilterNonKey;
    else
63
        skipLoopFilterDefaultValue = kVLCSettingSkipLoopFilterNonRef;
64

Felix Paul Kühne's avatar
Felix Paul Kühne committed
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
    NSDictionary *appDefaults = @{kVLCSettingPasscodeKey : @"",
                                  kVLCSettingContinueAudioInBackgroundKey : @(YES),
                                  kVLCSettingStretchAudio : @(NO),
                                  kVLCSettingTextEncoding : kVLCSettingTextEncodingDefaultValue,
                                  kVLCSettingSkipLoopFilter : skipLoopFilterDefaultValue,
                                  kVLCSettingSubtitlesFont : kVLCSettingSubtitlesFontDefaultValue,
                                  kVLCSettingSubtitlesFontColor : kVLCSettingSubtitlesFontColorDefaultValue,
                                  kVLCSettingSubtitlesFontSize : kVLCSettingSubtitlesFontSizeDefaultValue,
                                  kVLCSettingSubtitlesBoldFont: kVLCSettingSubtitlesBoldFontDefaultValue,
                                  kVLCSettingDeinterlace : kVLCSettingDeinterlaceDefaultValue,
                                  kVLCSettingNetworkCaching : kVLCSettingNetworkCachingDefaultValue,
                                  kVLCSettingPlaybackGestures : [NSNumber numberWithBool:YES],
                                  kVLCSettingFTPTextEncoding : kVLCSettingFTPTextEncodingDefaultValue,
                                  kVLCSettingWiFiSharingIPv6 : kVLCSettingWiFiSharingIPv6DefaultValue,
                                  kVLCSettingEqualizerProfile : kVLCSettingEqualizerProfileDefaultValue,
                                  kVLCSettingPlaybackForwardSkipLength : kVLCSettingPlaybackForwardSkipLengthDefaultValue,
                                  kVLCSettingPlaybackBackwardSkipLength : kVLCSettingPlaybackBackwardSkipLengthDefaultValue,
                                  kVLCSettingOpenAppForPlayback : kVLCSettingOpenAppForPlaybackDefaultValue};
83
84
85
    [defaults registerDefaults:appDefaults];
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
86
87
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
88
89
90
91
92
93
94
95
96
97
98
99
    BITHockeyManager *hockeyManager = [BITHockeyManager sharedHockeyManager];

    if (BETA_DISTRIBUTION) {
        APLog(@"Using HockeySDK beta key");
        [hockeyManager configureWithIdentifier:@"0114ca8e265244ce588d2ebd035c3577"];
    } else
        [hockeyManager configureWithIdentifier:@"c95f4227dff96c61f8b3a46a25edc584"];

    // Configure the SDK in here only!
    [hockeyManager startManager];
    [hockeyManager.authenticator authenticateInstallation];

100
101
102
103
104
105
    /* listen to validation notification */
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(passcodeWasValidated:)
                                                 name:VLCPasscodeValidated
                                               object:nil];

Felix Paul Kühne's avatar
Felix Paul Kühne committed
106
107
108
109
110
111
112
113
    // Change the keyboard for UISearchBar
    [[UITextField appearance] setKeyboardAppearance:UIKeyboardAppearanceDark];
    // For the cursor
    [[UITextField appearance] setTintColor:[UIColor VLCOrangeTintColor]];
    // Don't override the 'Cancel' button color in the search bar with the previous UITextField call. Use the default blue color
    [[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor colorWithRed:0.0 green:122.0/255.0 blue:1.0 alpha:1.0]} forState:UIControlStateNormal];
    // For the edit selection indicators
    [[UITableView appearance] setTintColor:[UIColor VLCOrangeTintColor]];
114

115
116
    [[UISwitch appearance] setOnTintColor:[UIColor VLCOrangeTintColor]];

Felix Paul Kühne's avatar
Felix Paul Kühne committed
117
118
    // Init the HTTP Server and clean its cache
    [[VLCHTTPUploaderController sharedInstance] cleanCache];
119

Felix Paul Kühne's avatar
Felix Paul Kühne committed
120
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
Carola's avatar
Carola committed
121
    // enable crash preventer
122
123
    void (^setupBlock)() = ^{
        _playlistViewController = [[VLCPlaylistViewController alloc] init];
124
125
126
        VLCSidebarController *sidebarVC = [VLCSidebarController sharedInstance];
        VLCNavigationController *navCon = [[VLCNavigationController alloc] initWithRootViewController:_playlistViewController];
        sidebarVC.contentViewController = navCon;
Carola's avatar
Carola committed
127

128
        _playerDisplayController = [[VLCPlayerDisplayController alloc] init];
129
        _playerDisplayController.childViewController = sidebarVC.fullViewController;
130
131

        self.window.rootViewController = _playerDisplayController;
Carola's avatar
Carola committed
132
133
        [self.window makeKeyAndVisible];

134
135
136
137
        [self validatePasscode];

        [[MLMediaLibrary sharedMediaLibrary] applicationWillStart];

Carola's avatar
Carola committed
138
139
        VLCMediaFileDiscoverer *discoverer = [VLCMediaFileDiscoverer sharedInstance];
        [discoverer addObserver:self];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
140
        [discoverer startDiscovering];
Carola's avatar
Carola committed
141
142
143
    };

    NSError *error = nil;
144
145

    if ([[MLMediaLibrary sharedMediaLibrary] libraryMigrationNeeded]){
Carola's avatar
Carola committed
146
147
148
149
        _isRunningMigration = YES;

        VLCMigrationViewController *migrationController = [[VLCMigrationViewController alloc] initWithNibName:@"VLCMigrationViewController" bundle:nil];
        migrationController.completionHandler = ^{
150

Carola's avatar
Carola committed
151
152
153
154
            //migrate
            setupBlock();
            _isRunningMigration = NO;
            [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
155
            [[VLCMediaFileDiscoverer sharedInstance] updateMediaList];
Carola's avatar
Carola committed
156
        };
Felix Paul Kühne's avatar
Felix Paul Kühne committed
157

Carola's avatar
Carola committed
158
159
        self.window.rootViewController = migrationController;
        [self.window makeKeyAndVisible];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
160

Carola's avatar
Carola committed
161
162
    } else {
        if (error != nil) {
163
            APLog(@"removed persistentStore since it was corrupt");
Carola's avatar
Carola committed
164
165
166
167
168
            NSURL *storeURL = ((MLMediaLibrary *)[MLMediaLibrary sharedMediaLibrary]).persistentStoreURL;
            [[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
        }
        setupBlock();
    }
169

Felix Paul Kühne's avatar
Felix Paul Kühne committed
170
171
172
    VLCNotificationRelay *notificationRelay = [VLCNotificationRelay sharedRelay];
    [notificationRelay addRelayLocalName:NSManagedObjectContextDidSaveNotification toRemoteName:@"org.videolan.ios-app.dbupdate"];
    [notificationRelay addRelayLocalName:VLCPlaybackControllerPlaybackMetadataDidChange toRemoteName:kVLCDarwinNotificationNowPlayingInfoUpdate];
173

Carola's avatar
Carola committed
174
175
    return YES;
}
176

177
178
179
180
181
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Carola's avatar
Carola committed
182
183
#pragma mark - Handoff

184
185
- (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType
{
186
187
188
    if ([userActivityType isEqualToString:@"org.videolan.vlc-ios.librarymode"] ||
        [userActivityType isEqualToString:@"org.videolan.vlc-ios.playing"] ||
        [userActivityType isEqualToString:@"org.videolan.vlc-ios.libraryselection"])
Carola's avatar
Carola committed
189
        return YES;
190

Carola's avatar
Carola committed
191
192
193
    return NO;
}

194
195
196
197
198
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
 restorationHandler:(void (^)(NSArray *))restorationHandler
{
    NSString *userActivityType = userActivity.activityType;
Carola's avatar
Carola committed
199

200
201
    if([userActivityType isEqualToString:@"org.videolan.vlc-ios.librarymode"] ||
       [userActivityType isEqualToString:@"org.videolan.vlc-ios.libraryselection"]) {
Carola's avatar
Carola committed
202
        NSDictionary *dict = userActivity.userInfo;
203
204
205
        VLCLibraryMode libraryMode = (VLCLibraryMode)[(NSNumber *)dict[@"state"] integerValue];

        if (libraryMode <= VLCLibraryModeAllSeries) {
206
207
            [[VLCSidebarController sharedInstance] selectRowAtIndexPath:[NSIndexPath indexPathForRow:libraryMode inSection:0]
                                                         scrollPosition:UITableViewScrollPositionTop];
208
209
            [self.playlistViewController setLibraryMode:(VLCLibraryMode)libraryMode];
        }
210

Carola's avatar
Carola committed
211
212
        [self.playlistViewController restoreUserActivityState:userActivity];
        _isComingFromHandoff = YES;
Carola's avatar
Carola committed
213
214
215
216
217
        return YES;
    }
    return NO;
}

218
219
220
- (void)application:(UIApplication *)application
didFailToContinueUserActivityWithType:(NSString *)userActivityType
              error:(NSError *)error
Carola's avatar
Carola committed
221
222
223
224
225
226
{
    if (error.code != NSUserCancelledError){
        //TODO: present alert
    }
}

227
228
229
230
- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation
231
{
232
    if ([[DBSession sharedSession] handleOpenURL:url]) {
233
        [[NSNotificationCenter defaultCenter] postNotificationName:VLCDropboxSessionWasAuthorized object:nil];
234
235
        return YES;
    }
236

237
238
    if (_playlistViewController && url != nil) {
        APLog(@"%@ requested %@ to be opened", sourceApplication, url);
239

240
        if (url.isFileURL) {
241
242
243
244
245
246
            NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString *directoryPath = searchPaths[0];
            NSURL *destinationURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@", directoryPath, url.lastPathComponent]];
            NSError *theError;
            [[NSFileManager defaultManager] moveItemAtURL:url toURL:destinationURL error:&theError];
            if (theError.code != noErr)
247
                APLog(@"saving the file failed (%li): %@", (long)theError.code, theError.localizedDescription);
248

Felix Paul Kühne's avatar
Felix Paul Kühne committed
249
            [[VLCMediaFileDiscoverer sharedInstance] updateMediaList];
250
251
252
253
254
        } else if ([url.scheme isEqualToString:@"vlc-x-callback"] || [url.host isEqualToString:@"x-callback-url"]) {
            // URL confirmes to the x-callback-url specification
            // vlc-x-callback://x-callback-url/action?param=value&x-success=callback
            APLog(@"x-callback-url with host '%@' path '%@' parameters '%@'", url.host, url.path, url.query);
            NSString *action = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
255
256
257
258
            NSURL *movieURL;
            NSURL *successCallback;
            NSURL *errorCallback;
            NSString *fileName;
259
260
261
262
263
            for (NSString *entry in [url.query componentsSeparatedByString:@"&"]) {
                NSArray *keyvalue = [entry componentsSeparatedByString:@"="];
                if (keyvalue.count < 2) continue;
                NSString *key = keyvalue[0];
                NSString *value = [keyvalue[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
264
265

                if ([key isEqualToString:@"url"])
266
                    movieURL = [NSURL URLWithString:value];
267
                else if ([key isEqualToString:@"filename"])
268
                    fileName = value;
269
                else if ([key isEqualToString:@"x-success"])
270
                    successCallback = [NSURL URLWithString:value];
271
272
                else if ([key isEqualToString:@"x-error"])
                    errorCallback = [NSURL URLWithString:value];
273
274
            }
            if ([action isEqualToString:@"stream"] && movieURL) {
275
276
                VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
                [vpc playURL:movieURL successCallback:successCallback errorCallback:errorCallback];
277
            }
278
279
280
            else if ([action isEqualToString:@"download"] && movieURL) {
                [self downloadMovieFromURL:movieURL fileNameOfMedia:fileName];
            }
Luis's avatar
Luis committed
281
        } else {
282
283
284
285
286
287
288
289
290
291
            NSString *receivedUrl = [url absoluteString];
            if ([receivedUrl length] > 6) {
                NSString *verifyVlcUrl = [receivedUrl substringToIndex:6];
                if ([verifyVlcUrl isEqualToString:@"vlc://"]) {
                    NSString *parsedString = [receivedUrl substringFromIndex:6];
                    NSUInteger location = [parsedString rangeOfString:@"//"].location;

                    /* Safari & al mangle vlc://http:// so fix this */
                    if (location != NSNotFound && [parsedString characterAtIndex:location - 1] != 0x3a) { // :
                            parsedString = [NSString stringWithFormat:@"%@://%@", [parsedString substringToIndex:location], [parsedString substringFromIndex:location+2]];
292
293
294
295
296
297
                    } else {
                        parsedString = [receivedUrl substringFromIndex:6];
                        if (![parsedString hasPrefix:@"http://"] && ![parsedString hasPrefix:@"https://"] && ![parsedString hasPrefix:@"ftp://"]) {
                            parsedString = [@"http://" stringByAppendingString:[receivedUrl substringFromIndex:6]];
                        }
                    }
298
299
300
                    url = [NSURL URLWithString:parsedString];
                }
            }
301
302
            [[VLCSidebarController sharedInstance] selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
                                                         scrollPosition:UITableViewScrollPositionNone];
303
304
305

            NSString *scheme = url.scheme;
            if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"] || [scheme isEqualToString:@"ftp"]) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
306
                VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"OPEN_STREAM_OR_DOWNLOAD", nil) message:url.absoluteString cancelButtonTitle:NSLocalizedString(@"BUTTON_DOWNLOAD", nil) otherButtonTitles:@[NSLocalizedString(@"PLAY_BUTTON", nil)]];
307
308
                alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
                    if (cancelled)
309
                        [self downloadMovieFromURL:url fileNameOfMedia:nil];
310
311
312
313
                    else {
                        VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
                        [vpc playURL:url successCallback:nil errorCallback:nil];
                    }
314
315
                };
                [alert show];
316
317
318
319
            } else {
                VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
                [vpc playURL:url successCallback:nil errorCallback:nil];
            }
Luis's avatar
Luis committed
320
        }
321
322
323
324
325
        return YES;
    }
    return NO;
}

326
327
328
329
330
- (void)applicationWillEnterForeground:(UIApplication *)application
{
    [[MLMediaLibrary sharedMediaLibrary] applicationWillStart];
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
331
332
- (void)applicationWillResignActive:(UIApplication *)application
{
333
    _passcodeValidated = NO;
334
    [self.playlistViewController setEditing:NO animated:NO];
335
    [self validatePasscode];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
336
337
338
339
340
    [[MLMediaLibrary sharedMediaLibrary] applicationWillExit];
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
Carola's avatar
Carola committed
341
    if (!_isRunningMigration && !_isComingFromHandoff) {
Carola's avatar
Carola committed
342
        [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
343
        [[VLCMediaFileDiscoverer sharedInstance] updateMediaList];
Carola's avatar
Carola committed
344
345
    } else if(_isComingFromHandoff) {
        _isComingFromHandoff = NO;
Carola's avatar
Carola committed
346
    }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
347
348
}

349
350
- (void)applicationWillTerminate:(UIApplication *)application
{
351
    _passcodeValidated = NO;
352
353
354
    [[NSUserDefaults standardUserDefaults] synchronize];
}

Gleb Pinigin's avatar
Gleb Pinigin committed
355
#pragma mark - media discovering
356

357
358
- (void)mediaFileAdded:(NSString *)fileName loading:(BOOL)isLoading
{
Gleb Pinigin's avatar
Gleb Pinigin committed
359
360
361
    if (!isLoading) {
        MLMediaLibrary *sharedLibrary = [MLMediaLibrary sharedMediaLibrary];
        [sharedLibrary addFilePaths:@[fileName]];
362

Gleb Pinigin's avatar
Gleb Pinigin committed
363
364
365
        /* exclude media files from backup (QA1719) */
        NSURL *excludeURL = [NSURL fileURLWithPath:fileName];
        [excludeURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
366

Gleb Pinigin's avatar
Gleb Pinigin committed
367
368
        // TODO Should we update media db after adding new files?
        [sharedLibrary updateMediaDatabase];
369
        [_playlistViewController updateViewContents];
370
371
372
    }
}

373
374
- (void)mediaFileDeleted:(NSString *)name
{
Gleb Pinigin's avatar
Gleb Pinigin committed
375
376
377
378
    [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
    [_playlistViewController updateViewContents];
}

379
380
#pragma mark - pass code validation

381
382
383
384
- (void)passcodeWasValidated:(NSNotification *)aNotifcation
{
    _passcodeValidated = YES;
    [self.playlistViewController updateViewContents];
385
386
    if ([VLCPlaybackController sharedInstance].isPlaying)
        [_playerDisplayController pushPlaybackView];
387
388
}

389
390
391
392
393
- (BOOL)passcodeValidated
{
    return _passcodeValidated;
}

394
395
- (void)validatePasscode
{
396
    VLCKeychainCoordinator *keychainCoordinator = [VLCKeychainCoordinator defaultCoordinator];
397

398
    if (!_passcodeValidated && [keychainCoordinator passcodeLockEnabled]) {
399
400
        [_playerDisplayController dismissPlaybackView];

401
        [keychainCoordinator validatePasscode];
402
403
    } else
        _passcodeValidated = YES;
404
405
}

406
407
408
409
410
#pragma mark - download handling

- (void)downloadMovieFromURL:(NSURL *)url
             fileNameOfMedia:(NSString *)fileName
{
411
412
413
    [[VLCDownloadViewController sharedInstance] addURLToDownloadList:url fileNameOfMedia:fileName];
    [[VLCSidebarController sharedInstance] selectRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:1]
                                                 scrollPosition:UITableViewScrollPositionNone];
414
415
}

416
#pragma mark - watch struff
417
418
419
- (void)application:(UIApplication *)application
handleWatchKitExtensionRequest:(NSDictionary *)userInfo
              reply:(void (^)(NSDictionary *))reply
420
421
422
423
424
425
426
427
{
    /* dispatch background task */
    __block UIBackgroundTaskIdentifier taskIdentifier = [application beginBackgroundTaskWithName:nil
                                                                               expirationHandler:^{
                                                                                   [application endBackgroundTask:taskIdentifier];
                                                                                   taskIdentifier = UIBackgroundTaskInvalid;
    }];

428
429
    VLCWatchMessage *message = [[VLCWatchMessage alloc] initWithDictionary:userInfo];
    NSString *name = message.name;
430
    NSDictionary *responseDict = nil;
431
    if ([name isEqualToString:VLCWatchMessageNameGetNowPlayingInfo]) {
432
        responseDict = [self nowPlayingResponseDict];
433
    } else if ([name isEqualToString:VLCWatchMessageNamePlayPause]) {
434
435
        [[VLCPlaybackController sharedInstance] playPause];
        responseDict = @{@"playing": @([VLCPlaybackController sharedInstance].isPlaying)};
436
    } else if ([name isEqualToString:VLCWatchMessageNameSkipForward]) {
437
        [[VLCPlaybackController sharedInstance] forward];
438
    } else if ([name isEqualToString:VLCWatchMessageNameSkipBackward]) {
439
        [[VLCPlaybackController sharedInstance] backward];
440
441
442
443
    } else if ([name isEqualToString:VLCWatchMessageNamePlayFile]) {
        [self playFileFromWatch:message];
    } else if ([name isEqualToString:VLCWatchMessageNameSetVolume]) {
        [self setVolumeFromWatch:message];
444
    } else {
445
        APLog(@"Did not handle request from WatchKit Extension: %@",userInfo);
446
    }
447
    reply(responseDict);
448
}
449

450
- (void)playFileFromWatch:(VLCWatchMessage *)message
451
{
452
    NSManagedObject *managedObject = nil;
453
454
    NSString *uriString = (id)message.payload;
    if ([uriString isKindOfClass:[NSString class]]) {
455
456
457
        NSURL *uriRepresentation = [NSURL URLWithString:uriString];
        managedObject = [[MLMediaLibrary sharedMediaLibrary] objectForURIRepresentation:uriRepresentation];
    }
458
    if (managedObject == nil) {
459
        APLog(@"%s file not found: %@",__PRETTY_FUNCTION__,message);
460
461
462
        return;
    }

463
464
    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
    [vpc playMediaLibraryObject:managedObject];
465
466
}

467
- (void)setVolumeFromWatch:(VLCWatchMessage *)message
468
{
469
    NSNumber *volume = (id)message.payload;
470
471
472
473
474
475
476
477
478
    if ([volume isKindOfClass:[NSNumber class]]) {
        /*
         * Since WatchKit doen't provide something like MPVolumeView we use deprecated API.
         * rdar://20783803 Feature Request: WatchKit equivalent for MPVolumeView
         */
        [MPMusicPlayerController applicationMusicPlayer].volume = volume.floatValue;
    }
}

479
480
- (NSDictionary *)nowPlayingResponseDict {
    NSMutableDictionary *response = [NSMutableDictionary new];
481
    NSMutableDictionary *nowPlayingInfo = [[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo mutableCopy];
482
    NSNumber *playbackTime = [VLCPlaybackController sharedInstance].mediaPlayer.time.numberValue;
483
484
485
    if (playbackTime) {
        nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = @(playbackTime.floatValue/1000);
    }
486
487
488
    if (nowPlayingInfo) {
        response[@"nowPlayingInfo"] = nowPlayingInfo;
    }
489
    MLFile *currentFile = [VLCPlaybackController sharedInstance].currentlyPlayingMediaFile;
490
491
492
    NSString *URIString = currentFile.objectID.URIRepresentation.absoluteString;
    if (URIString) {
        response[@"URIRepresentation"] = URIString;
493
    }
494
495
496

    response[@"volume"] = @([MPMusicPlayerController applicationMusicPlayer].volume);

497
    return response;
498
499
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
500
@end