VLCPlayerDisplayController.m 12.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*****************************************************************************
 * VLCPlayerDisplayController.m
 * VLC for iOS
 *****************************************************************************
 * Copyright (c) 2015 VideoLAN. All rights reserved.
 * $Id$
 *
 * Authors: Tobias Conradi <videolan # tobias-conradi.de>
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/

#import "VLCPlayerDisplayController.h"
#import "VLCPlaybackController.h"
15
#import "VLCMiniPlaybackView.h"
16
#import "VLCPlaybackNavigationController.h"
17
#import "VLCPlaybackController+MediaLibrary.h"
18
#import "VLC-Swift.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
19
#if TARGET_OS_IOS
20
#import "VLC-Swift.h"
21
#import "VLCMovieViewController.h"
22 23
#else
#import "VLCFullscreenMovieTVViewController.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
24
#endif
25 26 27

static NSString *const VLCPlayerDisplayControllerDisplayModeKey = @"VLCPlayerDisplayControllerDisplayMode";

28
@interface VLCUntouchableView: UIView
29 30
@end

31
@implementation VLCUntouchableView
32

33
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
34
{
35 36 37
    UIView *result = [super hitTest:point withEvent:event];
    return result == self ? nil : result;
}
38

39
@end
40

41 42 43 44
@interface VLCPlayerDisplayController () <VLCMovieViewControllerDelegate>
@property (nonatomic, strong) UIViewController<VLCPlaybackControllerDelegate> *movieViewController;
@property (nonatomic, strong) UIView<VLCPlaybackControllerDelegate, VLCMiniPlaybackViewInterface> *miniPlaybackView;
@property (nonatomic, strong) NSLayoutConstraint *bottomConstraint;
Soomin Lee's avatar
Soomin Lee committed
45
@property (nonatomic, strong) VLCService *services;
46
@end
47

48
@implementation VLCPlayerDisplayController
49

Soomin Lee's avatar
Soomin Lee committed
50
- (instancetype)initWithServices:(id)services
51
{
Soomin Lee's avatar
Soomin Lee committed
52
    self = [super initWithNibName:nil bundle:nil];
53
    if (self) {
Soomin Lee's avatar
Soomin Lee committed
54 55 56 57
        NSAssert([services isKindOfClass:[VLCService class]], @"VLCPlayerDisplayController: Injected services class issue");

        _services = services;

58 59 60 61
        NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
        [notificationCenter addObserver:self selector:@selector(playbackDidStart:) name:VLCPlaybackControllerPlaybackDidStart object:nil];
        [notificationCenter addObserver:self selector:@selector(playbackDidFail:) name:VLCPlaybackControllerPlaybackDidFail object:nil];
        [notificationCenter addObserver:self selector:@selector(playbackDidStop:) name:VLCPlaybackControllerPlaybackDidStop object:nil];
62
        [[NSUserDefaults standardUserDefaults] registerDefaults:@{VLCPlayerDisplayControllerDisplayModeKey : @(VLCPlayerDisplayControllerDisplayModeFullscreen)}];
63 64 65 66 67 68
    }
    return self;
}

- (void)viewDidLoad
{
69
    self.view = [[VLCUntouchableView alloc] initWithFrame:self.view.frame];
70 71
}

72 73 74 75 76 77 78 79
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        self.view.frame = CGRectMake(0, 0, size.width, size.height);
    } completion:nil];
}

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
#pragma mark - properties

- (VLCPlayerDisplayControllerDisplayMode)displayMode
{
    return [[NSUserDefaults standardUserDefaults] integerForKey:VLCPlayerDisplayControllerDisplayModeKey];
}

- (void)setDisplayMode:(VLCPlayerDisplayControllerDisplayMode)displayMode
{
    [[NSUserDefaults standardUserDefaults] setInteger:displayMode forKey:VLCPlayerDisplayControllerDisplayModeKey];
}

- (VLCPlaybackController *)playbackController {
    if (_playbackController == nil) {
        _playbackController = [VLCPlaybackController sharedInstance];
    }
    return _playbackController;
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
99
- (UIViewController<VLCPlaybackControllerDelegate> *)movieViewController
100 101
{
    if (!_movieViewController) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
102
#if TARGET_OS_IOS
Soomin Lee's avatar
Soomin Lee committed
103
        _movieViewController = [[VLCMovieViewController alloc] initWithServices:_services];
104
        ((VLCMovieViewController *)_movieViewController).delegate = self;
105 106
#else
        _movieViewController = [[VLCFullscreenMovieTVViewController alloc] initWithNibName:nil bundle:nil];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
107
#endif
108
        self.playbackController.delegate = _movieViewController;
109 110 111 112 113 114 115 116
    }
    return _movieViewController;
}

#pragma mark - Notification Handling

- (void)playbackDidStart:(NSNotification *)notification
{
117 118 119
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    BOOL enforceFullscreen = [[defaults objectForKey:kVLCSettingVideoFullscreenPlayback] boolValue];

120
    if (self.playbackController.fullscreenSessionRequested && enforceFullscreen) {
121 122 123 124
        [self showFullscreenPlayback];
        return;
    }

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
    switch (self.displayMode) {
        case VLCPlayerDisplayControllerDisplayModeFullscreen:
            [self _presentFullscreenPlaybackViewIfNeeded];
            break;
        case VLCPlayerDisplayControllerDisplayModeMiniplayer:
            [self _showHideMiniPlaybackView];
            break;
        default:
            break;
    }
}

- (void)playbackDidStop:(NSNotification *)notification
{
    [self dismissPlaybackView];
}

- (void)playbackDidFail:(NSNotification *)notification
{
    [self showPlaybackError];
}

#pragma mark - API

- (void)showFullscreenPlayback
{
    self.displayMode = VLCPlayerDisplayControllerDisplayModeFullscreen;
    [self _presentFullscreenPlaybackViewIfNeeded];
}

- (void)closeFullscreenPlayback
{
157
    [self.movieViewController dismissViewControllerAnimated:[self shouldAnimate] completion:nil];
158 159 160 161 162
    self.displayMode = VLCPlayerDisplayControllerDisplayModeMiniplayer;
    [self _showHideMiniPlaybackView];
}

#pragma mark - presentation handling
Felix Paul Kühne's avatar
Felix Paul Kühne committed
163

164 165 166 167 168
- (BOOL)shouldAnimate
{
    return [[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground;
}

169 170 171 172 173 174 175 176 177 178 179 180 181
- (void)pushPlaybackView
{
    switch (self.displayMode) {
        case VLCPlayerDisplayControllerDisplayModeFullscreen:
            [self _presentFullscreenPlaybackViewIfNeeded];
            break;
        case VLCPlayerDisplayControllerDisplayModeMiniplayer:
            [self _showHideMiniPlaybackView];
        default:
            break;
    }
}

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
- (void)dismissPlaybackView
{
    switch (self.displayMode) {
        case VLCPlayerDisplayControllerDisplayModeFullscreen:
            [self _closeFullscreenPlayback];
            break;
        case VLCPlayerDisplayControllerDisplayModeMiniplayer:
            [self _showHideMiniPlaybackView];
        default:
            break;
    }
}

- (void)showPlaybackError
{
    NSString *failedString = NSLocalizedString(@"PLAYBACK_FAILED", nil);
198
#if TARGET_OS_IOS
199 200
    switch (self.displayMode) {
        case VLCPlayerDisplayControllerDisplayModeFullscreen:
201 202
            if ([self.movieViewController respondsToSelector:@selector(showStatusMessage:)]) {
                [self.movieViewController showStatusMessage:failedString];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
203
            }
204 205 206
            break;
        case VLCPlayerDisplayControllerDisplayModeMiniplayer:
        default:
207 208
            [VLCAlertViewController alertViewManagerWithTitle:failedString
                                                 errorMessage:nil
209
                                               viewController:self];
210 211
            break;
    }
212 213 214 215 216 217 218 219 220 221 222 223
#else
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:failedString
                                                                   message:@""
                                                            preferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"BUTTON_OK", nil)
                                                            style:UIAlertActionStyleDefault
                                                          handler:^(UIAlertAction * action) {}];

    [alert addAction:defaultAction];
    [self presentViewController:alert animated:YES completion:nil];
#endif
224 225 226
}

#pragma mark - fullscreen player
Felix Paul Kühne's avatar
Felix Paul Kühne committed
227

228 229
- (void)_presentFullscreenPlaybackViewIfNeeded
{
230 231 232 233 234
    dispatch_async(dispatch_get_main_queue(), ^{
        if (!self.movieViewController.presentingViewController) {
            [self _presentMovieViewControllerAnimated:[self shouldAnimate]];
        }
    });
235 236 237 238
}

- (void)_closeFullscreenPlayback
{
239 240 241 242 243
    dispatch_async(dispatch_get_main_queue(), ^{
        BOOL animated = [self shouldAnimate];
        [self.movieViewController dismissViewControllerAnimated:animated completion:nil];
        [self _showHideMiniPlaybackView];
    });
244 245 246 247
}

- (void)_presentMovieViewControllerAnimated:(BOOL)animated
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
248
    UIViewController<VLCPlaybackControllerDelegate> *movieViewController = self.movieViewController;
249 250
    UINavigationController *navCon = [[VLCPlaybackNavigationController alloc] initWithRootViewController:movieViewController];
    [movieViewController prepareForMediaPlayback:self.playbackController];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
251

252 253 254 255 256 257 258 259
    UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    [window.rootViewController presentViewController:navCon animated:animated completion:nil];
}

#pragma mark - miniplayer

- (void)_showHideMiniPlaybackView
{
260 261 262
#if TARGET_OS_TV
    return;
#else
263 264 265 266 267
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(_showHideMiniPlaybackView) withObject:nil waitUntilDone:NO];
        return;
    }

268
    VLCPlaybackController *playbackController = [VLCPlaybackController sharedInstance];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
269
    UIView<VLCPlaybackControllerDelegate, VLCMiniPlaybackViewInterface> *miniPlaybackView = self.miniPlaybackView;
270
    const NSTimeInterval animationDuration = 0.25;
271
    const BOOL activePlaybackSession = playbackController.isPlaying || playbackController.willPlay;
272 273 274 275 276 277 278 279 280 281 282 283 284
    const BOOL miniPlayerVisible = miniPlaybackView.visible;

    BOOL needsShow = activePlaybackSession && !miniPlayerVisible;
    BOOL needsHide = !activePlaybackSession && miniPlayerVisible;

    if (self.editing) {
        needsHide = YES;
        needsShow = NO;
    }

    void (^completionBlock)(BOOL) = nil;
    if (needsShow) {
        if (!miniPlaybackView) {
285 286 287
            self.miniPlaybackView = miniPlaybackView = [[VLCMiniPlaybackView alloc] initWithFrame:CGRectZero];
            miniPlaybackView.translatesAutoresizingMaskIntoConstraints = NO;
            miniPlaybackView.userInteractionEnabled = YES;
288
            [self.view addSubview:miniPlaybackView];
289 290 291 292 293 294 295 296
            _bottomConstraint = [miniPlaybackView.topAnchor constraintEqualToAnchor:self.view.bottomAnchor];
            [NSLayoutConstraint activateConstraints:
             @[_bottomConstraint,
               [miniPlaybackView.heightAnchor constraintEqualToConstant:60.0],
               [miniPlaybackView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
               [miniPlaybackView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor],
               ]];
            [self.view layoutIfNeeded];
297 298 299 300 301
        }
        miniPlaybackView.visible = YES;
    } else if (needsHide) {
        miniPlaybackView.visible = NO;
        completionBlock = ^(BOOL finished) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
302
            UIView<VLCPlaybackControllerDelegate, VLCMiniPlaybackViewInterface> *miniPlaybackView = self.miniPlaybackView;
303 304
            if (miniPlaybackView.visible == NO) {
                [miniPlaybackView removeFromSuperview];
305
                self.miniPlaybackView = nil;
306 307 308 309 310 311 312 313
            }
        };
    }
    //when switching between tableview and collectionview all subviews are removed, make sure to readd it when this happens
    if (!miniPlaybackView.superview && miniPlayerVisible) {
        [self.view addSubview:miniPlaybackView];
    }
    // either way update view
Felix Paul Kühne's avatar
Felix Paul Kühne committed
314
    [miniPlaybackView prepareForMediaPlayback:playbackController];
315 316 317 318 319 320

    if (needsShow || needsHide) {
        [UIView animateWithDuration:animationDuration
                              delay:animationDuration
                            options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction
                         animations:^{
321 322 323 324 325 326 327
                             self.bottomConstraint.active = NO;
                             if (needsShow) {
                                 self.bottomConstraint = [miniPlaybackView.bottomAnchor constraintEqualToAnchor:self.realBottomAnchor];
                             } else {
                                 self.bottomConstraint = [miniPlaybackView.topAnchor constraintEqualToAnchor:self.bottomLayoutGuide.bottomAnchor];
                             }
                             self.bottomConstraint.active = YES;
328
                             [self.view layoutIfNeeded];
329 330 331
                         }
                         completion:completionBlock];
    }
332
#endif
333 334
}

335 336 337 338 339 340 341 342 343 344 345
#pragma mark - MovieViewControllerDelegate

- (void)movieViewControllerDidSelectMinimize:(VLCMovieViewController *)movieViewController
{
    [self closeFullscreenPlayback];
}

- (BOOL)movieViewControllerShouldBeDisplayed:(VLCMovieViewController *)movieViewController
{
    return self.displayMode == VLCPlayerDisplayControllerDisplayModeFullscreen;
}
346
@end