VLCMovieViewController.m 58.5 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * VLCMovieViewController.m
 * VLC for iOS
 *****************************************************************************
5
 * Copyright (c) 2013-2015 VideoLAN. All rights reserved.
6 7 8 9
 * $Id$
 *
 * Authors: Felix Paul Kühne <fkuehne # videolan.org>
 *          Gleb Pinigin <gpinigin # gmail.com>
10 11
 *          Carola Nitz <caro # videolan.org>
 *          Tobias Conradi <videolan # tobias-conradi.de>
12 13 14
 *          Ahmad Harb <harb.dev.leb # gmail.com>
 *          Fabio Ritrovato <sephiroth87 # videolan.org>
 *          Pierre SAGASPE <pierre.sagaspe # me.com>
15 16 17 18
 *          Filipe Cabecinhas <vlc # filcab dot net>
 *          Marc Etcheverry <marc # taplightsoftware dot com>
 *          Christopher Loessl <cloessl # x-berg dot de>
 *          Sylver Bruneau <sylver.bruneau # gmail dot com>
19 20 21
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/
Felix Paul Kühne's avatar
Felix Paul Kühne committed
22

23
#import "VLCMovieViewController.h"
24
#import "VLCExternalDisplayController.h"
25 26
#import "VLCTrackSelectorTableViewCell.h"
#import "VLCTrackSelectorHeaderView.h"
27
#import "VLCEqualizerView.h"
28
#import "VLCMultiSelectionMenuView.h"
29 30
#import "VLCPlaybackController.h"
#import "UIDevice+VLC.h"
31
#import "VLCTimeNavigationTitleView.h"
32 33
#import "VLCPlayerDisplayController.h"
#import "VLCAppDelegate.h"
34
#import "VLCStatusLabel.h"
35
#import "VLCMovieViewControlPanelViewController.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
36

37 38
#define FORWARD_SWIPE_DURATION 30
#define BACKWARD_SWIPE_DURATION 10
39

40 41 42
#define TRACK_SELECTOR_TABLEVIEW_CELL @"track selector table view cell"
#define TRACK_SELECTOR_TABLEVIEW_SECTIONHEADER @"track selector table view section header"

43 44 45 46
#define LOCKCHECK \
if (_interfaceIsLocked) \
return

47 48 49 50 51 52 53
typedef NS_ENUM(NSInteger, VLCPanType) {
  VLCPanTypeNone,
  VLCPanTypeBrightness,
  VLCPanTypeSeek,
  VLCPanTypeVolume,
};

54
@interface VLCMovieViewController () <UIGestureRecognizerDelegate, UITableViewDataSource, UITableViewDelegate, VLCMultiSelectionViewDelegate, VLCEqualizerViewUIDelegate>
55
{
56 57 58 59 60 61 62 63 64
    BOOL _controlsHidden;
    BOOL _videoFiltersHidden;
    BOOL _playbackSpeedViewHidden;

    UIActionSheet *_subtitleActionSheet;
    UIActionSheet *_audiotrackActionSheet;

    NSTimer *_idleTimer;

65
    BOOL _viewAppeared;
66
    BOOL _displayRemainingTime;
67
    BOOL _positionSet;
68
    BOOL _playerIsSetup;
69
    BOOL _isScrubbing;
70
    BOOL _interfaceIsLocked;
71
    BOOL _switchingTracksNotChapters;
72
    BOOL _audioOnly;
Ahmad Harb's avatar
Ahmad Harb committed
73 74

    BOOL _swipeGesturesEnabled;
75
    BOOL _variableJumpDurationEnabled;
76
    UIPinchGestureRecognizer *_pinchRecognizer;
77
    VLCPanType _currentPanType;
Ahmad Harb's avatar
Ahmad Harb committed
78 79 80
    UIPanGestureRecognizer *_panRecognizer;
    UISwipeGestureRecognizer *_swipeRecognizerLeft;
    UISwipeGestureRecognizer *_swipeRecognizerRight;
81 82
    UISwipeGestureRecognizer *_swipeRecognizerUp;
    UISwipeGestureRecognizer *_swipeRecognizerDown;
83
    UITapGestureRecognizer *_tapRecognizer;
84
    UITapGestureRecognizer *_tapOnVideoRecognizer;
85 86 87

    UIView *_trackSelectorContainer;
    UITableView *_trackSelectorTableView;
88 89

    VLCEqualizerView *_equalizerView;
90
    VLCMultiSelectionMenuView *_multiSelectionView;
91 92 93

    UIView *_sleepTimerContainer;
    UIDatePicker *_sleepTimeDatePicker;
94
    NSTimer *_sleepCountDownTimer;
95

96
    NSInteger _mediaDuration;
97 98
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
99 100
@property (nonatomic, strong) UIPopoverController *masterPopoverController;
@property (nonatomic, strong) UIWindow *externalWindow;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
101 102
@end

103
@implementation VLCMovieViewController
Felix Paul Kühne's avatar
Felix Paul Kühne committed
104

105 106 107 108 109 110 111
+ (void)initialize
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *appDefaults = @{kVLCShowRemainingTime : @(YES)};
    [defaults registerDefaults:appDefaults];
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
112 113
- (void)dealloc
{
114 115
    [[NSNotificationCenter defaultCenter] removeObserver:self];

116
    if (_tapRecognizer)
117
        [self.view removeGestureRecognizer:_tapRecognizer];
Ahmad Harb's avatar
Ahmad Harb committed
118
    if (_swipeRecognizerLeft)
119
        [self.view removeGestureRecognizer:_swipeRecognizerLeft];
Ahmad Harb's avatar
Ahmad Harb committed
120
    if (_swipeRecognizerRight)
121
        [self.view removeGestureRecognizer:_swipeRecognizerRight];
122 123 124 125
    if (_swipeRecognizerUp)
        [self.view removeGestureRecognizer:_swipeRecognizerUp];
    if (_swipeRecognizerDown)
        [self.view removeGestureRecognizer:_swipeRecognizerDown];
Ahmad Harb's avatar
Ahmad Harb committed
126
    if (_panRecognizer)
127
        [self.view removeGestureRecognizer:_panRecognizer];
128 129 130 131
    if (_pinchRecognizer)
        [self.view removeGestureRecognizer:_pinchRecognizer];
    [self.view removeGestureRecognizer:_tapOnVideoRecognizer];

132 133 134
    _tapRecognizer = nil;
    _swipeRecognizerLeft = nil;
    _swipeRecognizerRight = nil;
135 136
    _swipeRecognizerUp = nil;
    _swipeRecognizerDown = nil;
137
    _panRecognizer = nil;
138 139
    _pinchRecognizer = nil;
    _tapOnVideoRecognizer = nil;
140 141
}

142
- (void)viewDidLoad
Felix Paul Kühne's avatar
Felix Paul Kühne committed
143
{
144
    [super viewDidLoad];
145 146
    CGRect rect;

147 148
    self.extendedLayoutIncludesOpaqueBars = YES;
    self.edgesForExtendedLayout = UIRectEdgeAll;
149

150 151
    self.videoFilterView.hidden = YES;
    _videoFiltersHidden = YES;
152
    _hueLabel.text = NSLocalizedString(@"VFILTER_HUE", nil);
153 154
    _hueSlider.accessibilityLabel = _hueLabel.text;
    _hueSlider.isAccessibilityElement = YES;
155
    _contrastLabel.text = NSLocalizedString(@"VFILTER_CONTRAST", nil);
156 157
    _contrastSlider.accessibilityLabel = _contrastLabel.text;
    _contrastSlider.isAccessibilityElement = YES;
158
    _brightnessLabel.text = NSLocalizedString(@"VFILTER_BRIGHTNESS", nil);
159 160
    _brightnessSlider.accessibilityLabel = _brightnessLabel.text;
    _brightnessSlider.isAccessibilityElement = YES;
161
    _saturationLabel.text = NSLocalizedString(@"VFILTER_SATURATION", nil);
162 163
    _saturationSlider.accessibilityLabel = _saturationLabel.text;
    _saturationSlider.isAccessibilityElement = YES;
164
    _gammaLabel.text = NSLocalizedString(@"VFILTER_GAMMA", nil);
165 166
    _gammaSlider.accessibilityLabel = _gammaLabel.text;
    _gammaSlider.isAccessibilityElement = YES;
167
    _playbackSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SPEED", nil);
168 169
    _playbackSpeedSlider.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedSlider.isAccessibilityElement = YES;
170 171 172 173 174 175
    _audioDelayLabel.text = NSLocalizedString(@"AUDIO_DELAY", nil);
    _audioDelaySlider.accessibilityLabel = _audioDelayLabel.text;
    _audioDelaySlider.isAccessibilityElement = YES;
    _spuDelayLabel.text = NSLocalizedString(@"SPU_DELAY", nil);
    _spuDelaySlider.accessibilityLabel = _spuDelayLabel.text;
    _spuDelaySlider.isAccessibilityElement = YES;
176

177
    _resetVideoFilterButton.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER_RESET_BUTTON", nil);
178
    _resetVideoFilterButton.isAccessibilityElement = YES;
179 180 181
    _sleepTimerButton.accessibilityLabel = NSLocalizedString(@"BUTTON_SLEEP_TIMER", nil);
    _sleepTimerButton.isAccessibilityElement = YES;
    [_sleepTimerButton setTitle:NSLocalizedString(@"BUTTON_SLEEP_TIMER", nil) forState:UIControlStateNormal];
182

183 184 185 186 187 188
    _multiSelectionView = [[VLCMultiSelectionMenuView alloc] init];
    _multiSelectionView.delegate = self;
    _multiSelectionView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin;
    _multiSelectionView.hidden = YES;
    [self.view addSubview:_multiSelectionView];

189
    _scrubHelpLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HELP", nil);
190

191 192
    self.playbackSpeedView.hidden = YES;
    _playbackSpeedViewHidden = YES;
193

194 195 196 197 198
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(handleExternalScreenDidConnect:)
                   name:UIScreenDidConnectNotification object:nil];
    [center addObserver:self selector:@selector(handleExternalScreenDidDisconnect:)
                   name:UIScreenDidDisconnectNotification object:nil];
199 200
    [center addObserver:self selector:@selector(screenBrightnessChanged:)
                   name:UIScreenBrightnessDidChangeNotification object:nil];
201 202 203 204
    [center addObserver:self
               selector:@selector(appBecameActive:)
                   name:UIApplicationDidBecomeActiveNotification
                 object:nil];
205

206 207
    _playingExternallyTitle.text = NSLocalizedString(@"PLAYING_EXTERNALLY_TITLE", nil);
    _playingExternallyDescription.text = NSLocalizedString(@"PLAYING_EXTERNALLY_DESC", nil);
208
    if ([[UIDevice currentDevice] hasExternalDisplay])
209
        [self showOnExternalDisplay];
210

211 212
    self.trackNameLabel.text = self.artistNameLabel.text = self.albumNameLabel.text = @"";

213
    _movieView.userInteractionEnabled = NO;
214 215 216
    _tapOnVideoRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControlsVisible)];
    _tapOnVideoRecognizer.delegate = self;
    [self.view addGestureRecognizer:_tapOnVideoRecognizer];
217

218 219 220
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    _displayRemainingTime = [[defaults objectForKey:kVLCShowRemainingTime] boolValue];
    _swipeGesturesEnabled = [[defaults objectForKey:kVLCSettingPlaybackGestures] boolValue];
221
    _variableJumpDurationEnabled = [[defaults objectForKey:kVLCSettingVariableJumpDuration] boolValue];
222

223 224 225 226 227 228
    _pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)];
    _pinchRecognizer.delegate = self;
    [self.view addGestureRecognizer:_pinchRecognizer];

    _tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapRecognized)];
    [_tapRecognizer setNumberOfTouchesRequired:2];
229 230

    _currentPanType = VLCPanTypeNone;
231 232 233 234 235 236 237 238
    _panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panRecognized:)];
    [_panRecognizer setMinimumNumberOfTouches:1];
    [_panRecognizer setMaximumNumberOfTouches:1];

    _swipeRecognizerLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRecognized:)];
    _swipeRecognizerLeft.direction = UISwipeGestureRecognizerDirectionLeft;
    _swipeRecognizerRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRecognized:)];
    _swipeRecognizerRight.direction = UISwipeGestureRecognizerDirectionRight;
239 240 241 242 243 244
    _swipeRecognizerUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRecognized:)];
    _swipeRecognizerUp.direction = UISwipeGestureRecognizerDirectionUp;
    _swipeRecognizerUp.numberOfTouchesRequired = 2;
    _swipeRecognizerDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRecognized:)];
    _swipeRecognizerDown.direction = UISwipeGestureRecognizerDirectionDown;
    _swipeRecognizerDown.numberOfTouchesRequired = 2;
245 246 247

    [self.view addGestureRecognizer:_swipeRecognizerLeft];
    [self.view addGestureRecognizer:_swipeRecognizerRight];
248 249
    [self.view addGestureRecognizer:_swipeRecognizerUp];
    [self.view addGestureRecognizer:_swipeRecognizerDown];
250 251 252 253
    [self.view addGestureRecognizer:_panRecognizer];
    [self.view addGestureRecognizer:_tapRecognizer];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerLeft];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerRight];
254 255
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerUp];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerDown];
256 257 258 259

    _panRecognizer.delegate = self;
    _swipeRecognizerRight.delegate = self;
    _swipeRecognizerLeft.delegate = self;
260 261
    _swipeRecognizerUp.delegate = self;
    _swipeRecognizerDown.delegate = self;
262
    _tapRecognizer.delegate = self;
263

Felix Paul Kühne's avatar
Felix Paul Kühne committed
264 265 266 267 268 269 270 271 272 273 274
    self.backButton.tintColor = [UIColor colorWithRed:(190.0f/255.0f) green:(190.0f/255.0f) blue:(190.0f/255.0f) alpha:1.];
    self.toolbar.tintColor = [UIColor whiteColor];
    self.toolbar.barStyle = UIBarStyleBlack;

    rect = self.resetVideoFilterButton.frame;
    rect.origin.y = rect.origin.y + 5.;
    self.resetVideoFilterButton.frame = rect;
    rect = self.toolbar.frame;
    rect.size.height = rect.size.height + rect.origin.y;
    rect.origin.y = 0;
    self.toolbar.frame = rect;
275

276
    _playerIsSetup = NO;
277

278 279
    [self.movieView setAccessibilityLabel:NSLocalizedString(@"VO_VIDEOPLAYER_TITLE", nil)];
    [self.movieView setAccessibilityHint:NSLocalizedString(@"VO_VIDEOPLAYER_DOUBLETAP", nil)];
280 281

    rect = self.view.frame;
282 283 284 285 286 287
    CGFloat width;
    CGFloat height;
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        width = 300.;
        height = 320.;
    } else {
288
        width = 420.;
289 290
        height = 470.;
    }
291

292
    _trackSelectorTableView = [[UITableView alloc] initWithFrame:CGRectMake(0., 0., width, height) style:UITableViewStylePlain];
293 294
    _trackSelectorTableView.delegate = self;
    _trackSelectorTableView.dataSource = self;
295
    _trackSelectorTableView.separatorColor = [UIColor clearColor];
296
    _trackSelectorTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
297 298 299
    _trackSelectorTableView.rowHeight = 44.;
    _trackSelectorTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    _trackSelectorTableView.sectionHeaderHeight = 28.;
300 301
    [_trackSelectorTableView registerClass:[VLCTrackSelectorTableViewCell class] forCellReuseIdentifier:TRACK_SELECTOR_TABLEVIEW_CELL];
    [_trackSelectorTableView registerClass:[VLCTrackSelectorHeaderView class] forHeaderFooterViewReuseIdentifier:TRACK_SELECTOR_TABLEVIEW_SECTIONHEADER];
302
    _trackSelectorTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
303

304
    _trackSelectorContainer = [[VLCFrostedGlasView alloc] initWithFrame:CGRectMake((rect.size.width - width) / 2., (rect.size.height - height) / 2., width, height)];
305
    [_trackSelectorContainer addSubview:_trackSelectorTableView];
306
    _trackSelectorContainer.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleHeight;
307 308 309 310 311 312
    _trackSelectorContainer.hidden = YES;

    if ([[UIDevice currentDevice] speedCategory] >= 3) {
        _trackSelectorTableView.opaque = NO;
        _trackSelectorTableView.backgroundColor = [UIColor clearColor];
    } else
313
        _trackSelectorTableView.backgroundColor = [UIColor blackColor];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
314
    _trackSelectorTableView.allowsMultipleSelection = YES;
315 316

    [self.view addSubview:_trackSelectorContainer];
317

318
    _equalizerView = [[VLCEqualizerView alloc] initWithFrame:CGRectMake(0, 0, 450., 240.)];
319
    _equalizerView.delegate = self.playbackController;
320
    _equalizerView.UIdelegate = self;
321 322 323
    _equalizerView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;
    _equalizerView.hidden = YES;
    [self.view addSubview:_equalizerView];
324

Felix Paul Kühne's avatar
Felix Paul Kühne committed
325 326 327 328
    /* add sleep timer UI */
    _sleepTimerContainer = [[VLCFrostedGlasView alloc] initWithFrame:CGRectMake(0., 0., 300., 162.)];
    _sleepTimerContainer.center = self.view.center;
    _sleepTimerContainer.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;
329

Felix Paul Kühne's avatar
Felix Paul Kühne committed
330 331 332 333 334 335 336
    _sleepTimeDatePicker = [[UIDatePicker alloc] init];
    if ([[UIDevice currentDevice] speedCategory] >= 3) {
        _sleepTimeDatePicker.opaque = NO;
        _sleepTimeDatePicker.backgroundColor = [UIColor clearColor];
    } else
        _sleepTimeDatePicker.backgroundColor = [UIColor blackColor];
    _sleepTimeDatePicker.tintColor = [UIColor VLCLightTextColor];
337
    _sleepTimeDatePicker.frame = CGRectMake(0., 0., 300., 162.);
Felix Paul Kühne's avatar
Felix Paul Kühne committed
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
    _sleepTimeDatePicker.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;
    [_sleepTimerContainer addSubview:_sleepTimeDatePicker];

    /* adapt the date picker style to suit our needs */
    [_sleepTimeDatePicker setValue:[UIColor whiteColor] forKeyPath:@"textColor"];
    SEL selector = NSSelectorFromString(@"setHighlightsToday:");
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDatePicker instanceMethodSignatureForSelector:selector]];
    BOOL no = NO;
    [invocation setSelector:selector];
    [invocation setArgument:&no atIndex:2];
    [invocation invokeWithTarget:_sleepTimeDatePicker];

    if (_sleepTimerContainer.subviews.count > 0) {
        NSArray *subviewsOfSubview = [_sleepTimeDatePicker.subviews[0] subviews];
        NSUInteger subviewCount = subviewsOfSubview.count;
        for (NSUInteger x = 0; x < subviewCount; x++) {
            if ([subviewsOfSubview[x] isKindOfClass:[UILabel class]])
                [subviewsOfSubview[x] setTextColor:[UIColor VLCLightTextColor]];
        }
357
    }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
358
    _sleepTimeDatePicker.datePickerMode = UIDatePickerModeCountDownTimer;
359
    _sleepTimeDatePicker.minuteInterval = 1;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
360 361 362 363 364
    _sleepTimeDatePicker.minimumDate = [NSDate date];
    _sleepTimeDatePicker.countDownDuration = 1200.;
    [_sleepTimeDatePicker addTarget:self action:@selector(sleepTimerAction:) forControlEvents:UIControlEventValueChanged];

    [self.view addSubview:_sleepTimerContainer];
365 366

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackDidStop:) name:VLCPlaybackControllerPlaybackDidStop object:nil];
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389


    VLCMovieViewControlPanelViewController *panelVC = [[VLCMovieViewControlPanelViewController alloc] initWithNibName:@"VLCMovieViewControlPanel"
                                                                                                               bundle:nil];
    [self addChildViewController:panelVC];
    [self.view addSubview:panelVC.view];
    panelVC.view.translatesAutoresizingMaskIntoConstraints = NO;
    NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[panel]|"
                                                                    options:0
                                                                    metrics:nil
                                                                      views:@{@"panel":panelVC.view}];

    NSArray *vConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[panel]|"
                                                                    options:0
                                                                    metrics:nil
                                                                      views:@{@"panel":panelVC.view}];
    [self.view addConstraints:hConstraints];
    [self.view addConstraints:vConstraints];


    [panelVC didMoveToParentViewController:self];
    self.controlPanelController = panelVC;
    self.controllerPanel = panelVC.view;
390
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
391

392 393
- (void)viewWillAppear:(BOOL)animated
{
394
    [super viewWillAppear:animated];
395

396 397 398 399 400 401
    /* reset audio meta data views */
    self.artworkImageView.image = nil;
    self.trackNameLabel.text = nil;
    self.artistNameLabel.text = nil;
    self.albumNameLabel.text = nil;

402
    _swipeGesturesEnabled = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingPlaybackGestures] boolValue];
403
    _variableJumpDurationEnabled = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingVariableJumpDuration] boolValue];
404

405
    [self.navigationController setNavigationBarHidden:YES animated:YES];
406

407
    VLCPlaybackController *vpc = self.playbackController;
408
    vpc.delegate = self;
409
    [vpc recoverPlaybackState];
410

411
    [self screenBrightnessChanged:nil];
412
    [self setControlsHidden:NO animated:YES];
413 414 415 416 417
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
418
    _viewAppeared = YES;
419

420
    VLCPlaybackController *vpc = self.playbackController;
421
    [vpc recoverDisplayedMetadata];
422 423
    vpc.videoOutputView = nil;
    vpc.videoOutputView = self.movieView;
424 425
}

426
- (void)viewDidLayoutSubviews
427
{
428 429 430 431 432 433

    CGRect equalizerRect = _equalizerView.frame;
    equalizerRect.origin.x = CGRectGetMidX(self.view.bounds) - CGRectGetWidth(equalizerRect)/2.0;
    equalizerRect.origin.y = CGRectGetMidY(self.view.bounds) - CGRectGetHeight(equalizerRect)/2.0;
    _equalizerView.frame = equalizerRect;

434
    CGRect multiSelectionFrame;
435
    CGRect controllerPanelFrame = _controllerPanel.frame;;
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455

    if (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPhone) {
        multiSelectionFrame = (CGRect){CGPointMake(0., 0.), [_multiSelectionView proposedDisplaySize]};
        multiSelectionFrame.origin.x = controllerPanelFrame.size.width - multiSelectionFrame.size.width;
        multiSelectionFrame.origin.y = controllerPanelFrame.origin.y - multiSelectionFrame.size.height;
        _multiSelectionView.frame = multiSelectionFrame;
        _multiSelectionView.showsEqualizer = YES;
        return;
    }

    if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
        _multiSelectionView.showsEqualizer = YES;
        multiSelectionFrame = (CGRect){CGPointMake(0., 0.), [_multiSelectionView proposedDisplaySize]};
        multiSelectionFrame.origin.x = controllerPanelFrame.size.width - multiSelectionFrame.size.width;
        multiSelectionFrame.origin.y = controllerPanelFrame.origin.y - multiSelectionFrame.size.height;
    } else {
        _multiSelectionView.showsEqualizer = NO;
        multiSelectionFrame = (CGRect){CGPointMake(0., 0.), [_multiSelectionView proposedDisplaySize]};
        multiSelectionFrame.origin.x = controllerPanelFrame.size.width - multiSelectionFrame.size.width;
        multiSelectionFrame.origin.y = controllerPanelFrame.origin.y - multiSelectionFrame.size.height;
456
    }
457
    _multiSelectionView.frame = multiSelectionFrame;
458 459
}

460
- (void)viewWillDisappear:(BOOL)animated
Felix Paul Kühne's avatar
Felix Paul Kühne committed
461
{
462
    VLCPlaybackController *vpc = self.playbackController;
463 464 465
    if (vpc.videoOutputView == self.movieView) {
        vpc.videoOutputView = nil;
    }
466

467
    _viewAppeared = NO;
468
    if (_idleTimer) {
469
        [_idleTimer invalidate];
470 471
        _idleTimer = nil;
    }
472
    [self.navigationController setNavigationBarHidden:NO animated:YES];
473
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
474
    [super viewWillDisappear:animated];
475

Felix Paul Kühne's avatar
Felix Paul Kühne committed
476 477 478 479
    // hide filter UI for next run
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

480 481 482
    if (_equalizerView.hidden == NO)
        _equalizerView.hidden = YES;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
483 484
    if (!_playbackSpeedViewHidden)
        _playbackSpeedViewHidden = YES;
485 486

    [[NSUserDefaults standardUserDefaults] setBool:_displayRemainingTime forKey:kVLCShowRemainingTime];
487 488 489 490 491 492 493
}

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

494 495
#pragma mark - controls visibility

496 497
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer
{
498 499
    LOCKCHECK;

500 501 502
    if (!_swipeGesturesEnabled)
        return;

503
    if (recognizer.velocity < 0.)
504
        [self minimizePlayback:nil];
505 506
}

507 508
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
509
    if (touch.view != self.view)
510 511 512 513 514
        return NO;

    return YES;
}

Ahmad Harb's avatar
Ahmad Harb committed
515 516 517 518 519
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

520
- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated
521
{
522
    _controlsHidden = hidden;
523
    CGFloat alpha = _controlsHidden? 0.0f: 1.0f;
524

525 526
    if (!_controlsHidden) {
        _controllerPanel.alpha = 0.0f;
527
        _controllerPanel.hidden = !_videoFiltersHidden;
528 529
        _toolbar.alpha = 0.0f;
        _toolbar.hidden = NO;
530 531
        _videoFilterView.alpha = 0.0f;
        _videoFilterView.hidden = _videoFiltersHidden;
532 533
        _playbackSpeedView.alpha = 0.0f;
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
534 535
        _trackSelectorContainer.alpha = 0.0f;
        _trackSelectorContainer.hidden = YES;
536 537
        _equalizerView.alpha = 0.0f;
        _equalizerView.hidden = YES;
538 539 540 541
        if (_sleepTimerContainer) {
            _sleepTimerContainer.alpha = 0.0f;
            _sleepTimerContainer.hidden = YES;
        }
542 543
        _multiSelectionView.alpha = 0.0f;
        _multiSelectionView.hidden = YES;
544 545 546 547

        _artistNameLabel.hidden = NO;
        _albumNameLabel.hidden = NO;
        _trackNameLabel.hidden = NO;
548
    }
549

550 551
    void (^animationBlock)() = ^() {
        _controllerPanel.alpha = alpha;
552
        _toolbar.alpha = alpha;
553
        _videoFilterView.alpha = alpha;
554
        _playbackSpeedView.alpha = alpha;
555
        _trackSelectorContainer.alpha = alpha;
556
        _equalizerView.alpha = alpha;
557
        _multiSelectionView.alpha = alpha;
558 559
        if (_sleepTimerContainer)
            _sleepTimerContainer.alpha = alpha;
560 561 562 563 564

        CGFloat metaInfoAlpha = _audioOnly ? 1.0f : alpha;
        _artistNameLabel.alpha = metaInfoAlpha;
        _albumNameLabel.alpha = metaInfoAlpha;
        _trackNameLabel.alpha = metaInfoAlpha;
565
    };
566

567
    void (^completionBlock)(BOOL finished) = ^(BOOL finished) {
568
        _controllerPanel.hidden = _videoFiltersHidden ? _controlsHidden : NO;
569
        _toolbar.hidden = _controlsHidden;
570
        _videoFilterView.hidden = _videoFiltersHidden;
571
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
572
        _trackSelectorContainer.hidden = YES;
573
        _equalizerView.hidden = YES;
574 575
        if (_sleepTimerContainer)
            _sleepTimerContainer.hidden = YES;
576
        _multiSelectionView.hidden = YES;
577 578 579 580

        _artistNameLabel.hidden = _audioOnly ? NO : _controlsHidden;
        _albumNameLabel.hidden =  _audioOnly ? NO : _controlsHidden;
        _trackNameLabel.hidden =  _audioOnly ? NO : _controlsHidden;
581
    };
582

583 584
    UIStatusBarAnimation animationType = animated? UIStatusBarAnimationFade: UIStatusBarAnimationNone;
    NSTimeInterval animationDuration = animated? 0.3: 0.0;
585 586

    [[UIApplication sharedApplication] setStatusBarHidden:_viewAppeared ? _controlsHidden : NO withAnimation:animationType];
587 588
    [UIView animateWithDuration:animationDuration animations:animationBlock completion:completionBlock];
}
589

590
- (void)toggleControlsVisible
591
{
592 593 594 595 596
    if (!_trackSelectorContainer.hidden) {
        for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers)
            [recognizer setEnabled:YES];
    }

597 598 599
    if (_controlsHidden && !_videoFiltersHidden)
        _videoFiltersHidden = YES;

600
    [self setControlsHidden:!_controlsHidden animated:YES];
601 602
}

603
- (void)_resetIdleTimer
604 605
{
    if (!_idleTimer)
606
        _idleTimer = [NSTimer scheduledTimerWithTimeInterval:4.
607 608 609 610 611
                                                      target:self
                                                    selector:@selector(idleTimerExceeded)
                                                    userInfo:nil
                                                     repeats:NO];
    else {
612 613
        if (fabs([_idleTimer.fireDate timeIntervalSinceNow]) < 4.)
            [_idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:4.]];
614 615 616 617 618 619 620
    }
}

- (void)idleTimerExceeded
{
    _idleTimer = nil;
    if (!_controlsHidden)
621
        [self toggleControlsVisible];
622

623 624 625
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

626 627 628
    if (_equalizerView.hidden == NO)
        _equalizerView.hidden = YES;

629 630 631
    if (!_playbackSpeedViewHidden)
        _playbackSpeedViewHidden = YES;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
632 633
    if (self.scrubIndicatorView.hidden == NO)
        self.scrubIndicatorView.hidden = YES;
634 635
}

636 637
- (UIResponder *)nextResponder
{
638
    [self _resetIdleTimer];
639 640 641
    return [super nextResponder];
}

642 643 644 645
#pragma mark - controls

- (IBAction)closePlayback:(id)sender
{
646
    LOCKCHECK;
647 648
    [[VLCPlaybackController sharedInstance] stopPlayback];
}
649

650 651 652 653
- (IBAction)minimizePlayback:(id)sender
{
    LOCKCHECK;
    [[UIApplication sharedApplication] sendAction:@selector(closeFullscreenPlayback) to:nil from:self forEvent:nil];
654
}
655

656 657
- (IBAction)positionSliderAction:(UISlider *)sender
{
658 659
    LOCKCHECK;

660 661 662
    /* we need to limit the number of events sent by the slider, since otherwise, the user
     * wouldn't see the I-frames when seeking on current mobile devices. This isn't a problem
     * within the Simulator, but especially on older ARMv7 devices, it's clearly noticeable. */
663
    [self performSelector:@selector(_setPositionForReal) withObject:nil afterDelay:0.3];
664
    if (_mediaDuration > 0) {
665 666
        VLCTime *newPosition = [VLCTime timeWithInt:(int)(sender.value * _mediaDuration)];
        [self.timeNavigationTitleView.timeDisplayButton setTitle:newPosition.stringValue forState:UIControlStateNormal];
667
        [self.timeNavigationTitleView setNeedsLayout];
668
        self.timeNavigationTitleView.timeDisplayButton.accessibilityLabel = [NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"PLAYBACK_POSITION", nil), newPosition.stringValue];
669 670
        _positionSet = NO;
    }
671
    [self _resetIdleTimer];
672 673
}

674 675 676
- (void)_setPositionForReal
{
    if (!_positionSet) {
677 678
        self.playbackController.mediaPlayer.position = self.timeNavigationTitleView.positionSlider.value;
        [self.playbackController setNeedsMetadataUpdate];
679 680 681 682
        _positionSet = YES;
    }
}

683 684
- (IBAction)positionSliderTouchDown:(id)sender
{
685 686
    LOCKCHECK;

687 688
    [self _updateScrubLabel];
    self.scrubIndicatorView.hidden = NO;
689
    _isScrubbing = YES;
690 691 692 693
}

- (IBAction)positionSliderTouchUp:(id)sender
{
694 695
    LOCKCHECK;

696
    self.scrubIndicatorView.hidden = YES;
697
    _isScrubbing = NO;
698 699 700 701
}

- (void)_updateScrubLabel
{
702
    float speed = self.timeNavigationTitleView.positionSlider.scrubbingSpeed;
703
    if (speed == 1.)
704
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HIGH", nil);
705
    else if (speed == .5)
706
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HALF", nil);
707
    else if (speed == .25)
708
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_QUARTER", nil);
709
    else
710
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_FINE", nil);
711 712

    [self _resetIdleTimer];
713 714 715 716
}

- (IBAction)positionSliderDrag:(id)sender
{
717 718
    LOCKCHECK;

719 720 721
    [self _updateScrubLabel];
}

722 723
- (IBAction)volumeSliderAction:(id)sender
{
724 725
    LOCKCHECK;

726 727 728
    [self _resetIdleTimer];
}

729 730


731 732 733 734 735 736 737 738 739 740 741
- (void)updateTimeDisplayButton
{
    VLCMediaPlayer *mediaPlayer = self.playbackController.mediaPlayer;
    UIButton *timeDisplayButton = self.timeNavigationTitleView.timeDisplayButton;
    if (_displayRemainingTime)
        [timeDisplayButton setTitle:[[mediaPlayer remainingTime] stringValue] forState:UIControlStateNormal];
    else
        [timeDisplayButton setTitle:[[mediaPlayer time] stringValue] forState:UIControlStateNormal];
    [self.timeNavigationTitleView setNeedsLayout];
}

742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
- (void)updateSleepTimerButton
{
    NSMutableString *title = [NSMutableString stringWithString:NSLocalizedString(@"BUTTON_SLEEP_TIMER", nil)];
    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
    if (vpc.sleepTimer != nil && vpc.sleepTimer.valid) {
        int remainSeconds = (int)[vpc.sleepTimer.fireDate timeIntervalSinceNow];
        int hour = remainSeconds / 3600;
        int minute = (remainSeconds - hour * 3600) / 60;
        int second = remainSeconds % 60;
        [title appendFormat:@"  %02d:%02d:%02d", hour, minute, second];
    } else {
        [_sleepCountDownTimer invalidate];
    }

    [_sleepTimerButton setTitle:title forState:UIControlStateNormal];
}

759 760
#pragma mark - playback controller delegation

761 762 763 764 765 766 767 768
- (VLCPlaybackController *)playbackController
{
    if (!_playbackController) {
        _playbackController = [VLCPlaybackController sharedInstance];
    }
    return _playbackController;
}

769
- (void)playbackPositionUpdated:(VLCPlaybackController *)controller
770
{
771
    VLCMediaPlayer *mediaPlayer = controller.mediaPlayer;
772
    if (!_isScrubbing) {
773
        self.timeNavigationTitleView.positionSlider.value = [mediaPlayer position];
774 775
    }

776
    [self updateTimeDisplayButton];
777 778
}

779
- (void)prepareForMediaPlayback:(VLCPlaybackController *)controller
780
{
781
    self.trackNameLabel.text = self.artistNameLabel.text = self.albumNameLabel.text = @"";
782 783 784
    self.timeNavigationTitleView.positionSlider.value = 0.;
    [self.timeNavigationTitleView.timeDisplayButton setTitle:@"" forState:UIControlStateNormal];
    self.timeNavigationTitleView.timeDisplayButton.accessibilityLabel = @"";
785 786 787 788
    if (![[UIDevice currentDevice] hasExternalDisplay])
        self.brightnessSlider.value = [UIScreen mainScreen].brightness * 2.;
    [_equalizerView reloadData];

789
    double playbackRate = controller.playbackRate;
790 791
    self.playbackSpeedSlider.value = log2(playbackRate);
    self.playbackSpeedIndicator.text = [NSString stringWithFormat:@"%.2fx", playbackRate];
792

793 794
    float audioDelay = controller.audioDelay;
    self.audioDelaySlider.value = audioDelay;
795
    self.audioDelayIndicator.text = [NSString stringWithFormat:@"%1.2f s", audioDelay];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
796

797 798 799
    float subtitleDelay = controller.subtitleDelay;
    self.spuDelaySlider.value = subtitleDelay;
    self.spuDelayIndicator.text = [NSString stringWithFormat:@"%1.00f s", subtitleDelay];
800 801 802 803

    [self _resetIdleTimer];
}

804
- (void)playbackDidStop:(NSNotification *)notification
805
{
806
    [self minimizePlayback:nil];
807 808 809 810 811 812 813 814 815 816 817 818
}

- (void)mediaPlayerStateChanged:(VLCMediaPlayerState)currentState
                      isPlaying:(BOOL)isPlaying
currentMediaHasTrackToChooseFrom:(BOOL)currentMediaHasTrackToChooseFrom
        currentMediaHasChapters:(BOOL)currentMediaHasChapters
          forPlaybackController:(VLCPlaybackController *)controller
{
    if (currentState == VLCMediaPlayerStateBuffering)
        _mediaDuration = controller.mediaDuration;

    if (currentState == VLCMediaPlayerStateError)
819
        [self.statusLabel showStatusMessage:NSLocalizedString(@"PLAYBACK_FAILED", nil)];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
820

821
    [self.controlPanelController updateButtons];
822

823
    _multiSelectionView.mediaHasChapters = currentMediaHasChapters;
824 825 826 827
}

- (void)showStatusMessage:(NSString *)statusMessage forPlaybackController:(VLCPlaybackController *)controller
{
828
    [self showStatusMessage:statusMessage];
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847
}

- (void)displayMetadataForPlaybackController:(VLCPlaybackController *)controller
                                       title:(NSString *)title
                                     artwork:(UIImage *)artwork
                                      artist:(NSString *)artist
                                       album:(NSString *)album
                                   audioOnly:(BOOL)audioOnly
{
    if (!_viewAppeared)
        return;

    self.trackNameLabel.text = title;
    self.artworkImageView.image = artwork;
    if (!artwork) {
        self.artistNameLabel.text = artist;
        self.albumNameLabel.text = album;
    } else
        self.artistNameLabel.text = self.albumNameLabel.text = nil;
848

849 850 851
    self.timeNavigationTitleView.hideAspectRatio = audioOnly;
    self.timeNavigationTitleView.positionSlider.hidden = NO;

852 853
    [[self controlPanelController] updateButtons];
    
854