VLCMovieViewController.m 65.8 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 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
    _sleepTimerContainer.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;

    _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];

    /* 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];
}

593 594
#pragma mark - controls visibility

595 596
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer
{
597 598
    LOCKCHECK;

Soomin Lee's avatar
Soomin Lee committed
599
    if (!_closeGestureEnabled || isnan(recognizer.velocity))
600 601
        return;

Soomin Lee's avatar
Soomin Lee committed
602 603 604 605 606 607 608 609
    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.) {
610
        [self minimizePlayback:nil];
Soomin Lee's avatar
Soomin Lee committed
611
    }
612 613
}

614 615
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
616
    if (touch.view != self.view)
617 618 619 620 621
        return NO;

    return YES;
}

Ahmad Harb's avatar
Ahmad Harb committed
622 623 624 625 626
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

627
- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated
628
{
629
    _controlsHidden = hidden;
630
    CGFloat alpha = _controlsHidden? 0.0f: 1.0f;
631

632
    [self.controlPanelController beginAppearanceTransition:!hidden animated:animated];
633

634 635
    if (!_controlsHidden) {
        _controllerPanel.alpha = 0.0f;
636
        _controllerPanel.hidden = !_videoFiltersHidden;
637 638
        _toolbar.alpha = 0.0f;
        _toolbar.hidden = NO;
639 640
        _videoFilterView.alpha = 0.0f;
        _videoFilterView.hidden = _videoFiltersHidden;
641 642
        _playbackSpeedView.alpha = 0.0f;
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
643 644
        _trackSelectorContainer.alpha = 0.0f;
        _trackSelectorContainer.hidden = YES;
645 646
        _equalizerView.alpha = 0.0f;
        _equalizerView.hidden = YES;
647 648 649 650
        if (_sleepTimerContainer) {
            _sleepTimerContainer.alpha = 0.0f;
            _sleepTimerContainer.hidden = YES;
        }
651 652
        _multiSelectionView.alpha = 0.0f;
        _multiSelectionView.hidden = YES;
653 654 655 656

        _artistNameLabel.hidden = NO;
        _albumNameLabel.hidden = NO;
        _trackNameLabel.hidden = NO;
657
    }
658

659 660
    void (^animationBlock)() = ^() {
        _controllerPanel.alpha = alpha;
661
        _toolbar.alpha = alpha;
662
        _videoFilterView.alpha = alpha;
663
        _playbackSpeedView.alpha = alpha;
664
        _trackSelectorContainer.alpha = alpha;
665
        _equalizerView.alpha = alpha;
666
        _multiSelectionView.alpha = alpha;
667 668
        if (_sleepTimerContainer)
            _sleepTimerContainer.alpha = alpha;
669 670 671 672 673

        CGFloat metaInfoAlpha = _audioOnly ? 1.0f : alpha;
        _artistNameLabel.alpha = metaInfoAlpha;
        _albumNameLabel.alpha = metaInfoAlpha;
        _trackNameLabel.alpha = metaInfoAlpha;
674
    };
675

676
    void (^completionBlock)(BOOL finished) = ^(BOOL finished) {
677
        _controllerPanel.hidden = _videoFiltersHidden ? _controlsHidden : NO;
678
        _toolbar.hidden = _controlsHidden;
679
        _videoFilterView.hidden = _videoFiltersHidden;
680
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
681
        _trackSelectorContainer.hidden = YES;
682
        _equalizerView.hidden = YES;
683 684
        if (_sleepTimerContainer)
            _sleepTimerContainer.hidden = YES;
685
        _multiSelectionView.hidden = YES;
686 687 688 689

        _artistNameLabel.hidden = _audioOnly ? NO : _controlsHidden;
        _albumNameLabel.hidden =  _audioOnly ? NO : _controlsHidden;
        _trackNameLabel.hidden =  _audioOnly ? NO : _controlsHidden;
690 691

        [self.controlPanelController endAppearanceTransition];
692
    };
693

694 695
    UIStatusBarAnimation animationType = animated? UIStatusBarAnimationFade: UIStatusBarAnimationNone;
    NSTimeInterval animationDuration = animated? 0.3: 0.0;
696 697

    [[UIApplication sharedApplication] setStatusBarHidden:_viewAppeared ? _controlsHidden : NO withAnimation:animationType];
698 699
    [UIView animateWithDuration:animationDuration animations:animationBlock completion:completionBlock];
}
700

701
- (void)toggleControlsVisible
702
{
703 704 705 706 707
    if (!_trackSelectorContainer.hidden) {
        for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers)
            [recognizer setEnabled:YES];
    }

708 709 710
    if (_controlsHidden && !_videoFiltersHidden)
        _videoFiltersHidden = YES;

711 712 713
    if (_isTapSeeking)
        _numberOfTapSeek = 0;

714
    [self setControlsHidden:!_controlsHidden animated:YES];
715 716
}

717
- (void)_resetIdleTimer
718 719
{
    if (!_idleTimer)
720
        _idleTimer = [NSTimer scheduledTimerWithTimeInterval:4.
721 722 723 724 725
                                                      target:self
                                                    selector:@selector(idleTimerExceeded)
                                                    userInfo:nil
                                                     repeats:NO];
    else {
726 727
        if (fabs([_idleTimer.fireDate timeIntervalSinceNow]) < 4.)
            [_idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:4.]];
728 729 730
    }
}

731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
- (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];
}

761 762
- (void)idleTimerExceeded
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
763 764 765 766 767
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(idleTimerExceeded) withObject:nil waitUntilDone:NO];
        return;
    }

768 769
    _idleTimer = nil;
    if (!_controlsHidden)
770
        [self toggleControlsVisible];
771

772 773 774 775 776
    if (_isTapSeeking) {
        _isTapSeeking = NO;
        _numberOfTapSeek = 0;
    }

777 778 779
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

780 781 782
    if (_equalizerView.hidden == NO)
        _equalizerView.hidden = YES;

783 784 785
    if (!_playbackSpeedViewHidden)
        _playbackSpeedViewHidden = YES;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
786 787
    if (self.scrubIndicatorView.hidden == NO)
        self.scrubIndicatorView.hidden = YES;
788 789
}

790 791
- (UIResponder *)nextResponder
{
792
    [self _resetIdleTimer];
793 794 795
    return [super nextResponder];
}

Soomin Lee's avatar
Soomin Lee committed
796 797 798 799 800 801 802 803 804
- (void)enableNormalVideoGestures:(BOOL)enable
{
    [_tapRecognizer setEnabled:enable];
    [_swipeRecognizerUp setEnabled:enable];
    [_swipeRecognizerDown setEnabled:enable];
    [_swipeRecognizerLeft setEnabled:enable];
    [_swipeRecognizerRight setEnabled:enable];
}

805 806 807 808
#pragma mark - controls

- (IBAction)closePlayback:(id)sender
{
809
    LOCKCHECK;
810
    _playbackWillClose = YES;
811
    [_vpc stopPlayback];
812
}
813

814 815 816 817
- (IBAction)minimizePlayback:(id)sender
{
    LOCKCHECK;
    [[UIApplication sharedApplication] sendAction:@selector(closeFullscreenPlayback) to:nil from:self forEvent:nil];
818
}
819

820 821
- (IBAction)positionSliderAction:(UISlider *)sender
{
822 823
    LOCKCHECK;

824 825 826
    /* 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. */
827
    [self performSelector:@selector(_setPositionForReal) withObject:nil afterDelay:0.3];
828
    if (_mediaDuration > 0) {
829 830
        VLCTime *newPosition = [VLCTime timeWithInt:(int)(sender.value * _mediaDuration)];
        [self.timeNavigationTitleView.timeDisplayButton setTitle:newPosition.stringValue forState:UIControlStateNormal];
831
        [self.timeNavigationTitleView setNeedsLayout];
832
        self.timeNavigationTitleView.timeDisplayButton.accessibilityLabel = [NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"PLAYBACK_POSITION", nil), newPosition.stringValue];
833 834
        _positionSet = NO;
    }
835
    [self _resetIdleTimer];
836 837
}

838 839 840
- (void)_setPositionForReal
{
    if (!_positionSet) {
841 842
        _vpc.mediaPlayer.position = self.timeNavigationTitleView.positionSlider.value;
        [_vpc setNeedsMetadataUpdate];
843 844 845 846
        _positionSet = YES;
    }
}

847 848
- (IBAction)positionSliderTouchDown:(id)sender
{
849 850
    LOCKCHECK;

851 852
    [self _updateScrubLabel];
    self.scrubIndicatorView.hidden = NO;
853
    _isScrubbing = YES;
854 855 856 857
}

- (IBAction)positionSliderTouchUp:(id)sender
{
858 859
    LOCKCHECK;

860
    self.scrubIndicatorView.hidden = YES;
861
    _isScrubbing = NO;
862 863 864 865
}

- (void)_updateScrubLabel
{
866
    float speed = self.timeNavigationTitleView.positionSlider.scrubbingSpeed;
867
    if (speed == 1.)
868
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HIGH", nil);
869
    else if (speed == .5)
870
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HALF", nil);
871
    else if (speed == .25)
872
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_QUARTER", nil);
873
    else