VLCMovieViewController.m 68.1 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"
36
#import "VLCSlider.h"
37
#import "VLCLibraryViewController.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
38

39 40
#define FORWARD_SWIPE_DURATION 30
#define BACKWARD_SWIPE_DURATION 10
41
#define SHORT_JUMP_DURATION 10
42

Soomin Lee's avatar
Soomin Lee committed
43 44 45 46 47
#define ZOOM_SENSITIVITY 2.99f
#define DEFAULT_FOV 80.f
#define MAX_FOV 150.f
#define MIN_FOV 20.f

48 49 50
#define TRACK_SELECTOR_TABLEVIEW_CELL @"track selector table view cell"
#define TRACK_SELECTOR_TABLEVIEW_SECTIONHEADER @"track selector table view section header"

51 52 53 54
#define LOCKCHECK \
if (_interfaceIsLocked) \
return

55 56 57 58 59
typedef NS_ENUM(NSInteger, VLCPanType) {
  VLCPanTypeNone,
  VLCPanTypeBrightness,
  VLCPanTypeSeek,
  VLCPanTypeVolume,
Soomin Lee's avatar
Soomin Lee committed
60
  VLCPanTypeProjection
61 62
};

63
@interface VLCMovieViewController () <UIGestureRecognizerDelegate, UITableViewDataSource, UITableViewDelegate, VLCMultiSelectionViewDelegate, VLCEqualizerViewUIDelegate>
64
{
65 66 67 68 69 70 71 72 73
    BOOL _controlsHidden;
    BOOL _videoFiltersHidden;
    BOOL _playbackSpeedViewHidden;

    UIActionSheet *_subtitleActionSheet;
    UIActionSheet *_audiotrackActionSheet;

    NSTimer *_idleTimer;

74
    BOOL _viewAppeared;
75
    BOOL _displayRemainingTime;
76
    BOOL _positionSet;
77
    BOOL _playerIsSetup;
78
    BOOL _isScrubbing;
79
    BOOL _interfaceIsLocked;
80
    BOOL _switchingTracksNotChapters;
81
    BOOL _audioOnly;
Ahmad Harb's avatar
Ahmad Harb committed
82

83 84 85 86 87
    BOOL _volumeGestureEnabled;
    BOOL _playPauseGestureEnabled;
    BOOL _brightnessGestureEnabled;
    BOOL _seekGestureEnabled;
    BOOL _closeGestureEnabled;
88
    BOOL _variableJumpDurationEnabled;
Soomin Lee's avatar
Soomin Lee committed
89
    BOOL _mediaHasProjection;
90
    BOOL _playbackWillClose;
91 92
    BOOL _isTapSeeking;
    VLCMovieJumpState _previousJumpState;
Soomin Lee's avatar
Soomin Lee committed
93

94
    UIPinchGestureRecognizer *_pinchRecognizer;
95
    VLCPanType _currentPanType;
Ahmad Harb's avatar
Ahmad Harb committed
96 97 98
    UIPanGestureRecognizer *_panRecognizer;
    UISwipeGestureRecognizer *_swipeRecognizerLeft;
    UISwipeGestureRecognizer *_swipeRecognizerRight;
99 100
    UISwipeGestureRecognizer *_swipeRecognizerUp;
    UISwipeGestureRecognizer *_swipeRecognizerDown;
101
    UITapGestureRecognizer *_tapRecognizer;
102
    UITapGestureRecognizer *_tapOnVideoRecognizer;
103
    UITapGestureRecognizer *_tapToSeekRecognizer;
104 105 106

    UIView *_trackSelectorContainer;
    UITableView *_trackSelectorTableView;
107 108

    VLCEqualizerView *_equalizerView;
109
    VLCMultiSelectionMenuView *_multiSelectionView;
110

111 112
    VLCPlaybackController *_vpc;

113 114
    UIView *_sleepTimerContainer;
    UIDatePicker *_sleepTimeDatePicker;
115
    NSTimer *_sleepCountDownTimer;
116

117
    NSInteger _mediaDuration;
118
    NSInteger _numberOfTapSeek;
Soomin Lee's avatar
Soomin Lee committed
119 120 121 122

    CGFloat _fov;
    CGPoint _saveLocation;
    CGSize _screenSizePixel;
123 124
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
125 126
@property (nonatomic, strong) UIPopoverController *masterPopoverController;
@property (nonatomic, strong) UIWindow *externalWindow;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
127 128
@end

129
@implementation VLCMovieViewController
Felix Paul Kühne's avatar
Felix Paul Kühne committed
130

131 132 133 134 135 136 137
+ (void)initialize
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *appDefaults = @{kVLCShowRemainingTime : @(YES)};
    [defaults registerDefaults:appDefaults];
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
138 139
- (void)dealloc
{
140 141
    [[NSNotificationCenter defaultCenter] removeObserver:self];

142
    if (_tapRecognizer)
143
        [self.view removeGestureRecognizer:_tapRecognizer];
Ahmad Harb's avatar
Ahmad Harb committed
144
    if (_swipeRecognizerLeft)
145
        [self.view removeGestureRecognizer:_swipeRecognizerLeft];
Ahmad Harb's avatar
Ahmad Harb committed
146
    if (_swipeRecognizerRight)
147
        [self.view removeGestureRecognizer:_swipeRecognizerRight];
148 149 150 151
    if (_swipeRecognizerUp)
        [self.view removeGestureRecognizer:_swipeRecognizerUp];
    if (_swipeRecognizerDown)
        [self.view removeGestureRecognizer:_swipeRecognizerDown];
Ahmad Harb's avatar
Ahmad Harb committed
152
    if (_panRecognizer)
153
        [self.view removeGestureRecognizer:_panRecognizer];
154 155
    if (_pinchRecognizer)
        [self.view removeGestureRecognizer:_pinchRecognizer];
156 157
    if (_tapToSeekRecognizer)
        [self.view removeGestureRecognizer:_tapToSeekRecognizer];
158 159
    [self.view removeGestureRecognizer:_tapOnVideoRecognizer];

160 161 162
    _tapRecognizer = nil;
    _swipeRecognizerLeft = nil;
    _swipeRecognizerRight = nil;
163 164
    _swipeRecognizerUp = nil;
    _swipeRecognizerDown = nil;
165
    _panRecognizer = nil;
166 167
    _pinchRecognizer = nil;
    _tapOnVideoRecognizer = nil;
168
    _tapToSeekRecognizer = nil;
169 170
}

171
- (void)viewDidLoad
Felix Paul Kühne's avatar
Felix Paul Kühne committed
172
{
173
    [super viewDidLoad];
174 175
    CGRect rect;

176 177
    int deviceSpeedCategory = [[UIDevice currentDevice] VLCSpeedCategory];

178 179
    self.extendedLayoutIncludesOpaqueBars = YES;
    self.edgesForExtendedLayout = UIRectEdgeAll;
180

181 182
    self.videoFilterView.hidden = YES;
    _videoFiltersHidden = YES;
183
    _hueLabel.text = NSLocalizedString(@"VFILTER_HUE", nil);
184 185
    _hueSlider.accessibilityLabel = _hueLabel.text;
    _hueSlider.isAccessibilityElement = YES;
186
    _contrastLabel.text = NSLocalizedString(@"VFILTER_CONTRAST", nil);
187 188
    _contrastSlider.accessibilityLabel = _contrastLabel.text;
    _contrastSlider.isAccessibilityElement = YES;
189
    _brightnessLabel.text = NSLocalizedString(@"VFILTER_BRIGHTNESS", nil);
190 191
    _brightnessSlider.accessibilityLabel = _brightnessLabel.text;
    _brightnessSlider.isAccessibilityElement = YES;
192
    _saturationLabel.text = NSLocalizedString(@"VFILTER_SATURATION", nil);
193 194
    _saturationSlider.accessibilityLabel = _saturationLabel.text;
    _saturationSlider.isAccessibilityElement = YES;
195
    _gammaLabel.text = NSLocalizedString(@"VFILTER_GAMMA", nil);
196 197
    _gammaSlider.accessibilityLabel = _gammaLabel.text;
    _gammaSlider.isAccessibilityElement = YES;
198
    _playbackSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SPEED", nil);
199 200
    _playbackSpeedSlider.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedSlider.isAccessibilityElement = YES;
201 202 203 204 205 206
    _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;
207

208
    _resetVideoFilterButton.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER_RESET_BUTTON", nil);
209
    _resetVideoFilterButton.isAccessibilityElement = YES;
210 211 212
    _sleepTimerButton.accessibilityLabel = NSLocalizedString(@"BUTTON_SLEEP_TIMER", nil);
    _sleepTimerButton.isAccessibilityElement = YES;
    [_sleepTimerButton setTitle:NSLocalizedString(@"BUTTON_SLEEP_TIMER", nil) forState:UIControlStateNormal];
213

214 215 216 217 218 219
    _multiSelectionView = [[VLCMultiSelectionMenuView alloc] init];
    _multiSelectionView.delegate = self;
    _multiSelectionView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin;
    _multiSelectionView.hidden = YES;
    [self.view addSubview:_multiSelectionView];

220
    _scrubHelpLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HELP", nil);
221

222 223
    self.playbackSpeedView.hidden = YES;
    _playbackSpeedViewHidden = YES;
224

225 226 227 228 229
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(handleExternalScreenDidConnect:)
                   name:UIScreenDidConnectNotification object:nil];
    [center addObserver:self selector:@selector(handleExternalScreenDidDisconnect:)
                   name:UIScreenDidDisconnectNotification object:nil];
230 231
    [center addObserver:self selector:@selector(screenBrightnessChanged:)
                   name:UIScreenBrightnessDidChangeNotification object:nil];
232 233 234 235
    [center addObserver:self
               selector:@selector(appBecameActive:)
                   name:UIApplicationDidBecomeActiveNotification
                 object:nil];
236 237 238 239
    [center addObserver:self
               selector:@selector(playbackDidStop:)
                   name:VLCPlaybackControllerPlaybackDidStop
                 object:nil];
240

241 242
    _playingExternallyTitle.text = NSLocalizedString(@"PLAYING_EXTERNALLY_TITLE", nil);
    _playingExternallyDescription.text = NSLocalizedString(@"PLAYING_EXTERNALLY_DESC", nil);
243
    if ([[UIDevice currentDevice] VLCHasExternalDisplay])
244
        [self showOnExternalDisplay];
245

246 247
    self.trackNameLabel.text = self.artistNameLabel.text = self.albumNameLabel.text = @"";

248
    _movieView.userInteractionEnabled = NO;
249 250 251
    _tapOnVideoRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControlsVisible)];
    _tapOnVideoRecognizer.delegate = self;
    [self.view addGestureRecognizer:_tapOnVideoRecognizer];
252

253 254 255
    _tapToSeekRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapToSeekRecognized:)];
    [_tapToSeekRecognizer setNumberOfTapsRequired:2];

256 257 258 259 260 261
    _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];
262 263

    _currentPanType = VLCPanTypeNone;
264 265 266 267
    _panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panRecognized:)];
    [_panRecognizer setMinimumNumberOfTouches:1];
    [_panRecognizer setMaximumNumberOfTouches:1];

268 269
    [_tapOnVideoRecognizer requireGestureRecognizerToFail:_tapToSeekRecognizer];

270 271 272 273
    _swipeRecognizerLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRecognized:)];
    _swipeRecognizerLeft.direction = UISwipeGestureRecognizerDirectionLeft;
    _swipeRecognizerRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRecognized:)];
    _swipeRecognizerRight.direction = UISwipeGestureRecognizerDirectionRight;
274 275 276 277 278 279
    _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;
280

281

282 283
    [self.view addGestureRecognizer:_swipeRecognizerLeft];
    [self.view addGestureRecognizer:_swipeRecognizerRight];
284 285
    [self.view addGestureRecognizer:_swipeRecognizerUp];
    [self.view addGestureRecognizer:_swipeRecognizerDown];
286 287
    [self.view addGestureRecognizer:_panRecognizer];
    [self.view addGestureRecognizer:_tapRecognizer];
288 289
    [self.view addGestureRecognizer:_tapToSeekRecognizer];

290 291
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerLeft];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerRight];
292 293
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerUp];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerDown];
294 295 296 297

    _panRecognizer.delegate = self;
    _swipeRecognizerRight.delegate = self;
    _swipeRecognizerLeft.delegate = self;
298 299
    _swipeRecognizerUp.delegate = self;
    _swipeRecognizerDown.delegate = self;
300
    _tapRecognizer.delegate = self;
301 302 303 304 305
    _tapToSeekRecognizer.delegate = self;

    _isTapSeeking = NO;
    _previousJumpState = VLCMovieJumpStateDefault;
    _numberOfTapSeek = 0;
306

Felix Paul Kühne's avatar
Felix Paul Kühne committed
307 308 309 310 311 312 313 314 315 316 317
    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;
318

319
    _playerIsSetup = NO;
320

321 322
    [self.movieView setAccessibilityLabel:NSLocalizedString(@"VO_VIDEOPLAYER_TITLE", nil)];
    [self.movieView setAccessibilityHint:NSLocalizedString(@"VO_VIDEOPLAYER_DOUBLETAP", nil)];
323 324

    rect = self.view.frame;
325 326 327 328 329 330
    CGFloat width;
    CGFloat height;
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        width = 300.;
        height = 320.;
    } else {
331
        width = 420.;
332 333
        height = 470.;
    }
334

335
    _trackSelectorTableView = [[UITableView alloc] initWithFrame:CGRectMake(0., 0., width, height) style:UITableViewStylePlain];
336 337
    _trackSelectorTableView.delegate = self;
    _trackSelectorTableView.dataSource = self;
338
    _trackSelectorTableView.separatorColor = [UIColor clearColor];
339
    _trackSelectorTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
340 341 342
    _trackSelectorTableView.rowHeight = 44.;
    _trackSelectorTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    _trackSelectorTableView.sectionHeaderHeight = 28.;
343 344
    [_trackSelectorTableView registerClass:[VLCTrackSelectorTableViewCell class] forCellReuseIdentifier:TRACK_SELECTOR_TABLEVIEW_CELL];
    [_trackSelectorTableView registerClass:[VLCTrackSelectorHeaderView class] forHeaderFooterViewReuseIdentifier:TRACK_SELECTOR_TABLEVIEW_SECTIONHEADER];
345
    _trackSelectorTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
346

347
    _trackSelectorContainer = [[VLCFrostedGlasView alloc] initWithFrame:CGRectMake((rect.size.width - width) / 2., (rect.size.height - height) / 2., width, height)];
348
    [_trackSelectorContainer addSubview:_trackSelectorTableView];
349
    _trackSelectorContainer.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleHeight;
350 351
    _trackSelectorContainer.hidden = YES;

352
    if (deviceSpeedCategory >= 3) {
353 354 355
        _trackSelectorTableView.opaque = NO;
        _trackSelectorTableView.backgroundColor = [UIColor clearColor];
    } else
356
        _trackSelectorTableView.backgroundColor = [UIColor blackColor];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
357
    _trackSelectorTableView.allowsMultipleSelection = YES;
358 359

    [self.view addSubview:_trackSelectorContainer];
360

361
    _equalizerView = [[VLCEqualizerView alloc] initWithFrame:CGRectMake(0, 0, 450., 240.)];
362
    _equalizerView.delegate = [VLCPlaybackController sharedInstance];
363
    _equalizerView.UIdelegate = self;
364 365 366
    _equalizerView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;
    _equalizerView.hidden = YES;
    [self.view addSubview:_equalizerView];
367

368 369
    //Sleep Timer initialization
    [self sleepTimerInitializer:deviceSpeedCategory];
370

371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
    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;
Soomin Lee's avatar
Soomin Lee committed
392 393 394 395 396

    CGRect screenBounds = [[UIScreen mainScreen] bounds];
    CGFloat screenScale = [[UIScreen mainScreen] scale];
    _screenSizePixel = CGSizeMake(screenBounds.size.width * screenScale, screenBounds.size.height * screenScale);
    _saveLocation = CGPointMake(-1.f, -1.f);
397
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
398

399 400
- (void)viewWillAppear:(BOOL)animated
{
401
    [super viewWillAppear:animated];
402

403 404 405 406 407 408
    /* reset audio meta data views */
    self.artworkImageView.image = nil;
    self.trackNameLabel.text = nil;
    self.artistNameLabel.text = nil;
    self.albumNameLabel.text = nil;

409
    [self.navigationController setNavigationBarHidden:YES animated:animated];
410

411 412 413
    _vpc = [VLCPlaybackController sharedInstance];
    _vpc.delegate = self;
    [_vpc recoverPlaybackState];
414

415
    [self screenBrightnessChanged:nil];
416
    [self setControlsHidden:NO animated:animated];
417 418 419

    [self updateDefaults];
    [NSUserDefaults standardUserDefaults];
Soomin Lee's avatar
Soomin Lee committed
420 421 422

    //Disabling video gestures, media not init in the player yet.
    [self enableNormalVideoGestures:NO];
423
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDefaults) name:NSUserDefaultsDidChangeNotification object:nil];
424 425 426 427 428
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
429
    _viewAppeared = YES;
430
    _playbackWillClose = NO;
431

432 433 434 435
    [_vpc recoverDisplayedMetadata];
    _vpc.videoOutputView = nil;
    _vpc.videoOutputView = self.movieView;
    _multiSelectionView.repeatMode = _vpc.repeatMode;
Soomin Lee's avatar
Soomin Lee committed
436
    _multiSelectionView.shuffleMode = _vpc.isShuffleMode;
Soomin Lee's avatar
Soomin Lee committed
437 438 439 440 441 442 443 444 445 446

    //Media is loaded in the media player, checking the projection type and configuring accordingly.
    _fov = 0.f;
    _mediaHasProjection = NO;
    if ([_vpc currentMediaProjection] == VLCMediaProjectionEquiRectangular) {
        _fov = DEFAULT_FOV;
        _mediaHasProjection = YES;
    }

    [self enableNormalVideoGestures:!_mediaHasProjection];
447 448
}

449
- (void)viewDidLayoutSubviews
450
{
451 452 453 454 455
    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;

456
    CGRect multiSelectionFrame;
457
    CGRect controllerPanelFrame = _controllerPanel.frame;;
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477

    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;
478
    }
479
    _multiSelectionView.frame = multiSelectionFrame;
480 481
}

482
- (void)viewWillDisappear:(BOOL)animated
Felix Paul Kühne's avatar
Felix Paul Kühne committed
483
{
484 485
    if (_vpc.videoOutputView == self.movieView) {
        _vpc.videoOutputView = nil;
486
    }
487

488
    _viewAppeared = NO;
489
    if (_idleTimer) {
490
        [_idleTimer invalidate];
491 492
        _idleTimer = nil;
    }
493
    [self.navigationController setNavigationBarHidden:NO animated:YES];
494
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
495
    [super viewWillDisappear:animated];
496

Felix Paul Kühne's avatar
Felix Paul Kühne committed
497 498 499 500
    // hide filter UI for next run
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

501 502 503
    if (_equalizerView.hidden == NO)
        _equalizerView.hidden = YES;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
504 505
    if (!_playbackSpeedViewHidden)
        _playbackSpeedViewHidden = YES;
506

507 508 509 510 511
    // reset tap to seek values
    _isTapSeeking = NO;
    _previousJumpState = VLCMovieJumpStateDefault;
    _numberOfTapSeek = 0;

512
    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSUserDefaultsDidChangeNotification object:nil];
513
    [[NSUserDefaults standardUserDefaults] setBool:_displayRemainingTime forKey:kVLCShowRemainingTime];
514 515 516 517 518 519 520
}

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

521 522
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
{
523
    [self setControlsHidden:YES animated:flag];
524 525 526
    [super dismissViewControllerAnimated:flag completion:completion];
}

527
- (void)updateDefaults
528 529
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
530 531 532 533 534

    if (!_playbackWillClose) {
        _displayRemainingTime = [[defaults objectForKey:kVLCShowRemainingTime] boolValue];
        [self updateTimeDisplayButton];
    }
535 536 537 538 539 540 541 542 543

    _volumeGestureEnabled = [[defaults objectForKey:kVLCSettingVolumeGesture] boolValue];
    _playPauseGestureEnabled = [[defaults objectForKey:kVLCSettingPlayPauseGesture] boolValue];
    _brightnessGestureEnabled = [[defaults objectForKey:kVLCSettingBrightnessGesture] boolValue];
    _seekGestureEnabled = [[defaults objectForKey:kVLCSettingSeekGesture] boolValue];
    _closeGestureEnabled = [[defaults objectForKey:kVLCSettingCloseGesture] boolValue];
    _variableJumpDurationEnabled = [[defaults objectForKey:kVLCSettingVariableJumpDuration] boolValue];
}

544 545 546 547 548 549 550 551
#pragma mark - Initializer helper

- (void)sleepTimerInitializer:(int)deviceSpeedCategory
{

    /* add sleep timer UI */
    _sleepTimerContainer = [[VLCFrostedGlasView alloc] initWithFrame:CGRectMake(0., 0., 300., 200.)];
    _sleepTimerContainer.center = self.view.center;
552 553
    _sleepTimerContainer.clipsToBounds = YES;
    _sleepTimerContainer.layer.cornerRadius = 5;
554 555
    _sleepTimerContainer.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;

556 557 558 559 560 561 562 563 564
    //Layers used to create a separator for the buttons.
    CALayer *horizontalSeparator = [CALayer layer];
    horizontalSeparator.frame = CGRectMake(0., 162., 300., 1.);
    horizontalSeparator.backgroundColor = [UIColor VLCLightTextColor].CGColor;

    CALayer *verticalSeparator = [CALayer layer];
    verticalSeparator.frame = CGRectMake(150., 162., 1., 48.);
    verticalSeparator.backgroundColor = [UIColor VLCLightTextColor].CGColor;

565 566 567 568 569 570 571 572 573 574 575
    _sleepTimeDatePicker = [[UIDatePicker alloc] init];
    if (deviceSpeedCategory >= 3) {
        _sleepTimeDatePicker.opaque = NO;
        _sleepTimeDatePicker.backgroundColor = [UIColor clearColor];
    } else
        _sleepTimeDatePicker.backgroundColor = [UIColor blackColor];
    _sleepTimeDatePicker.tintColor = [UIColor VLCLightTextColor];
    _sleepTimeDatePicker.frame = CGRectMake(0., 0., 300., 162.);
    _sleepTimeDatePicker.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;

    [_sleepTimerContainer addSubview:_sleepTimeDatePicker];
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
    [_sleepTimerContainer.layer addSublayer:horizontalSeparator];
    [_sleepTimerContainer.layer addSublayer:verticalSeparator];

    UIButton *cancelSleepTimer = [[UIButton alloc] initWithFrame:CGRectMake(0., 162., 150., 48.)];
    cancelSleepTimer.backgroundColor = [UIColor clearColor];
    [cancelSleepTimer setTitle:NSLocalizedString(@"BUTTON_CANCEL", nil) forState:UIControlStateNormal];
    [cancelSleepTimer setTintColor:[UIColor VLCLightTextColor]];
    [cancelSleepTimer setTitleColor:[UIColor VLCDarkTextShadowColor] forState:UIControlStateHighlighted];
    [cancelSleepTimer addTarget:self action:@selector(sleepTimerCancel:) forControlEvents:UIControlEventTouchDown];
    cancelSleepTimer.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 10, 0);
    [_sleepTimerContainer addSubview:cancelSleepTimer];

    UIButton *confirmSleepTimer = [[UIButton alloc] initWithFrame:CGRectMake(150., 162., 150., 48.)];
    confirmSleepTimer.backgroundColor = [UIColor clearColor];
    [confirmSleepTimer setTitle:NSLocalizedString(@"BUTTON_DONE", nil) forState:UIControlStateNormal];
    [confirmSleepTimer setTintColor:[UIColor VLCLightTextColor]];
    [confirmSleepTimer setTitleColor:[UIColor VLCDarkTextShadowColor] forState:UIControlStateHighlighted];
    [confirmSleepTimer addTarget:self action:@selector(sleepTimerAction:) forControlEvents:UIControlEventTouchDown];
    confirmSleepTimer.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 10, 0);
    [_sleepTimerContainer addSubview:confirmSleepTimer];
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621

    /* 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]];
        }
    }
    _sleepTimeDatePicker.datePickerMode = UIDatePickerModeCountDownTimer;
    _sleepTimeDatePicker.minuteInterval = 1;
    _sleepTimeDatePicker.minimumDate = [NSDate date];
    _sleepTimeDatePicker.countDownDuration = 1200.;

    [self.view addSubview:_sleepTimerContainer];
}

622 623
#pragma mark - controls visibility

624 625
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer
{
626 627
    LOCKCHECK;

Soomin Lee's avatar
Soomin Lee committed
628
    if (!_closeGestureEnabled || isnan(recognizer.velocity))
629 630
        return;

Soomin Lee's avatar
Soomin Lee committed
631 632 633 634 635 636 637 638
    CGFloat diff = DEFAULT_FOV * -(ZOOM_SENSITIVITY * recognizer.velocity / _screenSizePixel.width);

    if (_mediaHasProjection) {
        if ([_vpc updateViewpoint:0 pitch:0 roll:0 fov:diff absolute:NO]) {
            //Checking for fov value in case of
            _fov = MAX(MIN(_fov + diff, MAX_FOV), MIN_FOV);
        }
    } else if (recognizer.velocity < 0.) {
639
        [self minimizePlayback:nil];
Soomin Lee's avatar
Soomin Lee committed
640
    }
641 642
}

643 644
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
645
    if (touch.view != self.view)
646 647 648 649 650
        return NO;

    return YES;
}

Ahmad Harb's avatar
Ahmad Harb committed
651 652 653 654 655
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

656
- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated
657
{
658
    _controlsHidden = hidden;
659
    CGFloat alpha = _controlsHidden? 0.0f: 1.0f;
660

661
    [self.controlPanelController beginAppearanceTransition:!hidden animated:animated];
662

663 664
    if (!_controlsHidden) {
        _controllerPanel.alpha = 0.0f;
665
        _controllerPanel.hidden = !_videoFiltersHidden;
666 667
        _toolbar.alpha = 0.0f;
        _toolbar.hidden = NO;
668 669
        _videoFilterView.alpha = 0.0f;
        _videoFilterView.hidden = _videoFiltersHidden;
670 671
        _playbackSpeedView.alpha = 0.0f;
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
672 673
        _trackSelectorContainer.alpha = 0.0f;
        _trackSelectorContainer.hidden = YES;
674 675
        _equalizerView.alpha = 0.0f;
        _equalizerView.hidden = YES;
676 677 678 679
        if (_sleepTimerContainer) {
            _sleepTimerContainer.alpha = 0.0f;
            _sleepTimerContainer.hidden = YES;
        }
680 681
        _multiSelectionView.alpha = 0.0f;
        _multiSelectionView.hidden = YES;
682 683 684 685

        _artistNameLabel.hidden = NO;
        _albumNameLabel.hidden = NO;
        _trackNameLabel.hidden = NO;
686
    }
687

688 689
    void (^animationBlock)() = ^() {
        _controllerPanel.alpha = alpha;
690
        _toolbar.alpha = alpha;
691
        _videoFilterView.alpha = alpha;
692
        _playbackSpeedView.alpha = alpha;
693
        _trackSelectorContainer.alpha = alpha;
694
        _equalizerView.alpha = alpha;
695
        _multiSelectionView.alpha = alpha;
696 697
        if (_sleepTimerContainer)
            _sleepTimerContainer.alpha = alpha;
698 699 700 701 702

        CGFloat metaInfoAlpha = _audioOnly ? 1.0f : alpha;
        _artistNameLabel.alpha = metaInfoAlpha;
        _albumNameLabel.alpha = metaInfoAlpha;
        _trackNameLabel.alpha = metaInfoAlpha;
703
    };
704

705
    void (^completionBlock)(BOOL finished) = ^(BOOL finished) {
706
        _controllerPanel.hidden = _videoFiltersHidden ? _controlsHidden : NO;
707
        _toolbar.hidden = _controlsHidden;
708
        _videoFilterView.hidden = _videoFiltersHidden;
709
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
710
        _trackSelectorContainer.hidden = YES;
711
        _equalizerView.hidden = YES;
712 713
        if (_sleepTimerContainer)
            _sleepTimerContainer.hidden = YES;
714
        _multiSelectionView.hidden = YES;
715 716 717 718

        _artistNameLabel.hidden = _audioOnly ? NO : _controlsHidden;
        _albumNameLabel.hidden =  _audioOnly ? NO : _controlsHidden;
        _trackNameLabel.hidden =  _audioOnly ? NO : _controlsHidden;
719 720

        [self.controlPanelController endAppearanceTransition];
721
    };
722

723 724
    UIStatusBarAnimation animationType = animated? UIStatusBarAnimationFade: UIStatusBarAnimationNone;
    NSTimeInterval animationDuration = animated? 0.3: 0.0;
725 726

    [[UIApplication sharedApplication] setStatusBarHidden:_viewAppeared ? _controlsHidden : NO withAnimation:animationType];
727 728
    [UIView animateWithDuration:animationDuration animations:animationBlock completion:completionBlock];
}
729

730
- (void)toggleControlsVisible
731
{
732 733 734 735 736
    if (!_trackSelectorContainer.hidden) {
        for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers)
            [recognizer setEnabled:YES];
    }

737 738 739
    if (_controlsHidden && !_videoFiltersHidden)
        _videoFiltersHidden = YES;

740 741 742
    if (_isTapSeeking)
        _numberOfTapSeek = 0;

743
    [self setControlsHidden:!_controlsHidden animated:YES];
744 745
}

746
- (void)_resetIdleTimer
747 748
{
    if (!_idleTimer)
749
        _idleTimer = [NSTimer scheduledTimerWithTimeInterval:4.
750 751 752 753 754
                                                      target:self
                                                    selector:@selector(idleTimerExceeded)
                                                    userInfo:nil
                                                     repeats:NO];
    else {
755 756
        if (fabs([_idleTimer.fireDate timeIntervalSinceNow]) < 4.)
            [_idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:4.]];
757 758 759
    }
}

760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
- (NSString *)_stringInTimeFormatFrom:(int)duration
{
    if (duration < 60) {
        return [NSString stringWithFormat:@"%is", duration];
    } else {
        return [NSString stringWithFormat:@"%im%is", duration / 60, duration % 60];
    }
}

- (void)_seekFromTap
{
    NSMutableString *hudString = [NSMutableString string];
    VLCMediaPlayer *mediaPlayer = _vpc.mediaPlayer;
    int seekDuration = (int)_numberOfTapSeek * SHORT_JUMP_DURATION;

    if (seekDuration > 0) {
        [mediaPlayer shortJumpForward];
        [hudString appendString:@"⇒ "];
        _previousJumpState = VLCMovieJumpStateForward;
    } else {
        [mediaPlayer shortJumpBackward];
        [hudString appendString:@"⇐ "];
        _previousJumpState = VLCMovieJumpStateBackward;
    }
    [hudString appendString: [self _stringInTimeFormatFrom:abs(seekDuration)]];
    [self.statusLabel showStatusMessage:[NSString stringWithString:hudString]];
    if (_controlsHidden)
        [self setControlsHidden:NO animated:NO];
}

790 791
- (void)idleTimerExceeded
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
792 793 794 795 796
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(idleTimerExceeded) withObject:nil waitUntilDone:NO];
        return;
    }

797 798
    _idleTimer = nil;
    if (!_controlsHidden)
799
        [self toggleControlsVisible];
800

801 802 803 804 805
    if (_isTapSeeking) {
        _isTapSeeking = NO;
        _numberOfTapSeek = 0;
    }

806 807 808
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

809 810 811
    if (_equalizerView.hidden == NO)
        _equalizerView.hidden = YES;

812 813 814
    if (!_playbackSpeedViewHidden)
        _playbackSpeedViewHidden = YES;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
815 816
    if (self.scrubIndicatorView.hidden == NO)
        self.scrubIndicatorView.hidden = YES;
817 818
}

819 820
- (UIResponder *)nextResponder
{
821
    [self _resetIdleTimer];
822 823 824
    return [super nextResponder];
}

Soomin Lee's avatar
Soomin Lee committed
825 826 827 828 829 830 831 832 833
- (void)enableNormalVideoGestures:(BOOL)enable
{
    [_tapRecognizer setEnabled:enable];
    [_swipeRecognizerUp setEnabled:enable];
    [_swipeRecognizerDown setEnabled:enable];
    [_swipeRecognizerLeft setEnabled:enable];
    [_swipeRecognizerRight setEnabled:enable];
}

834 835 836 837
#pragma mark - controls

- (IBAction)closePlayback:(id)sender
{
838
    LOCKCHECK;
839
    _playbackWillClose = YES;
840
    [_vpc stopPlayback];
841
}
842

843 844 845 846
- (IBAction)minimizePlayback:(id)sender
{
    LOCKCHECK;
    [[UIApplication sharedApplication] sendAction:@selector(closeFullscreenPlayback) to:nil from:self forEvent:nil];
847
}
848

849 850
- (IBAction)positionSliderAction:(UISlider *)sender
{
851 852
    LOCKCHECK;

853 854 855
    /* 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. */
856
    [self performSelector:@selector(_setPositionForReal) withObject:nil afterDelay:0.3];
857
    if (_mediaDuration > 0) {
858 859
        VLCTime *newPosition = [VLCTime timeWithInt:(int)(sender.value * _mediaDuration)];
        [self.timeNavigationTitleView.timeDisplayButton setTitle:newPosition.stringValue forState:UIControlStateNormal];
860
        [self.timeNavigationTitleView setNeedsLayout];
861
        self.timeNavigationTitleView.timeDisplayButton.accessibilityLabel = [NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"PLAYBACK_POSITION", nil), newPosition.stringValue];
862 863
        _positionSet = NO;
    }
864
    [self _resetIdleTimer];
865 866
}

867 868 869
- (void)_setPositionForReal
{
    if (!_positionSet) {
870 871
        _vpc.mediaPlayer.position = self.timeNavigationTitleView.positionSlider.value;
        [_vpc setNeedsMetadataUpdate];
872 873 874 875
        _positionSet = YES;
    }
}

876 877
- (IBAction)positionSliderTouchDown:(id)sender
{
878 879
    LOCKCHECK;

880 881
    [self _updateScrubLabel];
    self.scrubIndicatorView.hidden = NO;
882
    _isScrubbing = YES;
883 884 885 886
}

- (IBAction)positionSliderTouchUp:(id)sender
{
887 888
    LOCKCHECK;

889
    self.scrubIndicatorView.hidden = YES;