VLCMovieViewController.m 60.4 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 32 33

#import "OBSlider.h"
#import "VLCStatusLabel.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
34

35
#define INPUT_RATE_DEFAULT  1000.
36 37
#define FORWARD_SWIPE_DURATION 30
#define BACKWARD_SWIPE_DURATION 10
38

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

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

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

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

    UIActionSheet *_subtitleActionSheet;
    UIActionSheet *_audiotrackActionSheet;

    NSTimer *_idleTimer;
63
    NSTimer *_sleepTimer;
64

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

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

    UIView *_trackSelectorContainer;
    UITableView *_trackSelectorTableView;
87 88

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

    UIView *_sleepTimerContainer;
    UIDatePicker *_sleepTimeDatePicker;

94
    NSInteger _mediaDuration;
95 96
}

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

101
@implementation VLCMovieViewController
Felix Paul Kühne's avatar
Felix Paul Kühne committed
102

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

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

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

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

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

145 146
    self.wantsFullScreenLayout = YES;

147 148
    self.videoFilterView.hidden = YES;
    _videoFiltersHidden = YES;
149
    _hueLabel.text = NSLocalizedString(@"VFILTER_HUE", nil);
150 151
    _hueSlider.accessibilityLabel = _hueLabel.text;
    _hueSlider.isAccessibilityElement = YES;
152
    _contrastLabel.text = NSLocalizedString(@"VFILTER_CONTRAST", nil);
153 154
    _contrastSlider.accessibilityLabel = _contrastLabel.text;
    _contrastSlider.isAccessibilityElement = YES;
155
    _brightnessLabel.text = NSLocalizedString(@"VFILTER_BRIGHTNESS", nil);
156 157
    _brightnessSlider.accessibilityLabel = _brightnessLabel.text;
    _brightnessSlider.isAccessibilityElement = YES;
158
    _saturationLabel.text = NSLocalizedString(@"VFILTER_SATURATION", nil);
159 160
    _saturationSlider.accessibilityLabel = _saturationLabel.text;
    _saturationSlider.isAccessibilityElement = YES;
161
    _gammaLabel.text = NSLocalizedString(@"VFILTER_GAMMA", nil);
162 163
    _gammaSlider.accessibilityLabel = _gammaLabel.text;
    _gammaSlider.isAccessibilityElement = YES;
164
    _playbackSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SPEED", nil);
165 166
    _playbackSpeedSlider.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedSlider.isAccessibilityElement = YES;
167 168 169 170 171 172
    _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;
173

174
    _positionSlider.accessibilityLabel = NSLocalizedString(@"PLAYBACK_POSITION", nil);
175 176 177
    _positionSlider.isAccessibilityElement = YES;
    _timeDisplay.isAccessibilityElement = YES;

178 179 180 181
    _trackSwitcherButton.accessibilityLabel = NSLocalizedString(@"OPEN_TRACK_PANEL", nil);
    _trackSwitcherButton.isAccessibilityElement = YES;
    _trackSwitcherButtonLandscape.accessibilityLabel = NSLocalizedString(@"OPEN_TRACK_PANEL", nil);
    _trackSwitcherButtonLandscape.isAccessibilityElement = YES;
182 183
    _playbackSpeedButton.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedButton.isAccessibilityElement = YES;
184 185
    _playbackSpeedButtonLandscape.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedButtonLandscape.isAccessibilityElement = YES;
186
    _videoFilterButton.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER", nil);
187
    _videoFilterButton.isAccessibilityElement = YES;
188
    _videoFilterButtonLandscape.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER", nil);
189
    _videoFilterButtonLandscape.isAccessibilityElement = YES;
190
    _resetVideoFilterButton.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER_RESET_BUTTON", nil);
191
    _resetVideoFilterButton.isAccessibilityElement = YES;
192
    _aspectRatioButton.accessibilityLabel = NSLocalizedString(@"VIDEO_ASPECT_RATIO_BUTTON", nil);
193
    _aspectRatioButton.isAccessibilityElement = YES;
194
    _playPauseButton.accessibilityLabel = NSLocalizedString(@"PLAY_PAUSE_BUTTON", nil);
195
    _playPauseButton.isAccessibilityElement = YES;
196
    _playPauseButtonLandscape.accessibilityLabel = NSLocalizedString(@"PLAY_PAUSE_BUTTON", nil);
197
    _playPauseButtonLandscape.isAccessibilityElement = YES;
198
    _bwdButton.accessibilityLabel = NSLocalizedString(@"BWD_BUTTON", nil);
199
    _bwdButton.isAccessibilityElement = YES;
200
    _bwdButtonLandscape.accessibilityLabel = NSLocalizedString(@"BWD_BUTTON", nil);
201
    _bwdButtonLandscape.isAccessibilityElement = YES;
202
    _fwdButton.accessibilityLabel = NSLocalizedString(@"FWD_BUTTON", nil);
203
    _fwdButton.isAccessibilityElement = YES;
204
    _fwdButtonLandscape.accessibilityLabel = NSLocalizedString(@"FWD_BUTTON", nil);
205
    _fwdButtonLandscape.isAccessibilityElement = YES;
206 207 208
    _sleepTimerButton.accessibilityLabel = NSLocalizedString(@"BUTTON_SLEEP_TIMER", nil);
    _sleepTimerButton.isAccessibilityElement = YES;
    [_sleepTimerButton setTitle:NSLocalizedString(@"BUTTON_SLEEP_TIMER", nil) forState:UIControlStateNormal];
209 210
    if (!SYSTEM_RUNS_IOS7_OR_LATER)
        _sleepTimerButton.hidden = YES;
211

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

218
    _scrubHelpLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HELP", nil);
219

220 221
    self.playbackSpeedView.hidden = YES;
    _playbackSpeedViewHidden = YES;
222

223 224 225 226 227 228
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(handleExternalScreenDidConnect:)
                   name:UIScreenDidConnectNotification object:nil];
    [center addObserver:self selector:@selector(handleExternalScreenDidDisconnect:)
                   name:UIScreenDidDisconnectNotification object:nil];

229 230
    _playingExternallyTitle.text = NSLocalizedString(@"PLAYING_EXTERNALLY_TITLE", nil);
    _playingExternallyDescription.text = NSLocalizedString(@"PLAYING_EXTERNALLY_DESC", nil);
231
    if ([[UIDevice currentDevice] hasExternalDisplay])
232
        [self showOnExternalDisplay];
233

234 235
    self.trackNameLabel.text = self.artistNameLabel.text = self.albumNameLabel.text = @"";

236
    _movieView.userInteractionEnabled = NO;
237 238 239
    _tapOnVideoRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControlsVisible)];
    _tapOnVideoRecognizer.delegate = self;
    [self.view addGestureRecognizer:_tapOnVideoRecognizer];
240

241 242 243
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    _displayRemainingTime = [[defaults objectForKey:kVLCShowRemainingTime] boolValue];
    _swipeGesturesEnabled = [[defaults objectForKey:kVLCSettingPlaybackGestures] boolValue];
244
    _variableJumpDurationEnabled = [[defaults objectForKey:kVLCSettingVariableJumpDuration] boolValue];
245

246 247 248 249 250 251
    _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];
252 253

    _currentPanType = VLCPanTypeNone;
254 255 256 257 258 259 260 261
    _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;
262 263 264 265 266 267
    _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;
268 269 270

    [self.view addGestureRecognizer:_swipeRecognizerLeft];
    [self.view addGestureRecognizer:_swipeRecognizerRight];
271 272
    [self.view addGestureRecognizer:_swipeRecognizerUp];
    [self.view addGestureRecognizer:_swipeRecognizerDown];
273 274 275 276
    [self.view addGestureRecognizer:_panRecognizer];
    [self.view addGestureRecognizer:_tapRecognizer];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerLeft];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerRight];
277 278
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerUp];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerDown];
279 280 281 282

    _panRecognizer.delegate = self;
    _swipeRecognizerRight.delegate = self;
    _swipeRecognizerLeft.delegate = self;
283 284
    _swipeRecognizerUp.delegate = self;
    _swipeRecognizerDown.delegate = self;
285
    _tapRecognizer.delegate = self;
286

287
    [self.aspectRatioButton setImage:[UIImage imageNamed:@"ratioIcon"] forState:UIControlStateNormal];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
288
    if (SYSTEM_RUNS_IOS7_OR_LATER) {
289
        self.backButton.tintColor = [UIColor colorWithRed:(190.0f/255.0f) green:(190.0f/255.0f) blue:(190.0f/255.0f) alpha:1.];
290
        self.toolbar.tintColor = [UIColor whiteColor];
291
        self.toolbar.barStyle = UIBarStyleBlack;
292

293
        rect = self.resetVideoFilterButton.frame;
294 295
        rect.origin.y = rect.origin.y + 5.;
        self.resetVideoFilterButton.frame = rect;
296 297 298 299
        rect = self.toolbar.frame;
        rect.size.height = rect.size.height + rect.origin.y;
        rect.origin.y = 0;
        self.toolbar.frame = rect;
300 301 302 303 304 305 306 307 308 309
        rect = self.aspectRatioButton.frame;
        rect.size.width -= 19.;
        rect.origin.x += 19.;
        self.aspectRatioButton.frame = rect;
        rect = self.timeDisplay.frame;
        rect.origin.x += 19.;
        self.timeDisplay.frame = rect;
        rect = self.positionSlider.frame;
        rect.size.width += 19.;
        self.positionSlider.frame = rect;
310
    } else {
311
        rect = self.positionSlider.frame;
312 313
        rect.origin.y = rect.origin.y + 3.;
        self.positionSlider.frame = rect;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
314 315
        [self.aspectRatioButton setBackgroundImage:[UIImage imageNamed:@"ratioButton"] forState:UIControlStateNormal];
        [self.aspectRatioButton setBackgroundImage:[UIImage imageNamed:@"ratioButtonHighlight"] forState:UIControlStateHighlighted];
316
        [self.toolbar setBackgroundImage:[UIImage imageNamed:@"seekbarBg"] forBarMetrics:UIBarMetricsDefault];
317 318 319
        [self.backButton setBackgroundImage:[UIImage imageNamed:@"playbackDoneButton"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        [self.backButton setBackgroundImage:[UIImage imageNamed:@"playbackDoneButtonHighlight"] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
    }
320

321
    /* FIXME: there is a saner iOS 6+ API for this! */
322
    /* this looks a bit weird, but we need to support iOS 5 and should show the same appearance */
323 324 325 326 327 328 329
    void (^initVolumeSlider)(MPVolumeView *) = ^(MPVolumeView *volumeView){
        UISlider *volumeSlider = nil;
        for (id aView in volumeView.subviews){
            if ([[[aView class] description] isEqualToString:@"MPVolumeSlider"]){
                volumeSlider = (UISlider *)aView;
                break;
            }
330
        }
331 332 333 334 335 336 337 338 339 340 341 342 343
        if (!SYSTEM_RUNS_IOS7_OR_LATER) {
            [volumeSlider setMinimumTrackImage:[[UIImage imageNamed:@"sliderminiValue"]resizableImageWithCapInsets:UIEdgeInsetsMake(0, 4, 0, 0)] forState:UIControlStateNormal];
            [volumeSlider setMaximumTrackImage:[[UIImage imageNamed:@"slidermaxValue"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 4)] forState:UIControlStateNormal];
            [volumeSlider setThumbImage:[UIImage imageNamed:@"volumeballslider"] forState:UIControlStateNormal];
        } else
            [volumeView setVolumeThumbImage:[UIImage imageNamed:@"modernSliderKnob"] forState:UIControlStateNormal];
        [volumeSlider addTarget:self
                         action:@selector(volumeSliderAction:)
               forControlEvents:UIControlEventValueChanged];
    };

    initVolumeSlider(self.volumeView);
    initVolumeSlider(self.volumeViewLandscape);
344

345 346
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
        self.positionSlider.scrubbingSpeedChangePositions = @[@(0.), @(100.), @(200.), @(300)];
347 348

    _playerIsSetup = NO;
349

350 351
    [self.movieView setAccessibilityLabel:NSLocalizedString(@"VO_VIDEOPLAYER_TITLE", nil)];
    [self.movieView setAccessibilityHint:NSLocalizedString(@"VO_VIDEOPLAYER_DOUBLETAP", nil)];
352 353

    rect = self.view.frame;
354 355 356 357 358 359 360 361 362
    CGFloat width;
    CGFloat height;
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        width = 300.;
        height = 320.;
    } else {
        width = 450.;
        height = 470.;
    }
363

364
    _trackSelectorTableView = [[UITableView alloc] initWithFrame:CGRectMake(0., 0., width, height) style:UITableViewStylePlain];
365 366
    _trackSelectorTableView.delegate = self;
    _trackSelectorTableView.dataSource = self;
367
    _trackSelectorTableView.separatorColor = [UIColor clearColor];
368 369 370
    _trackSelectorTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
    [_trackSelectorTableView registerClass:[VLCTrackSelectorTableViewCell class] forCellReuseIdentifier:TRACK_SELECTOR_TABLEVIEW_CELL];
    [_trackSelectorTableView registerClass:[VLCTrackSelectorHeaderView class] forHeaderFooterViewReuseIdentifier:TRACK_SELECTOR_TABLEVIEW_SECTIONHEADER];
371
    _trackSelectorTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
372

373
    _trackSelectorContainer = [[VLCFrostedGlasView alloc] initWithFrame:CGRectMake((rect.size.width - width) / 2., (rect.size.height - height) / 2., width, height)];
374
    [_trackSelectorContainer addSubview:_trackSelectorTableView];
375
    _trackSelectorContainer.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleHeight;
376 377 378 379 380 381
    _trackSelectorContainer.hidden = YES;

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

    [self.view addSubview:_trackSelectorContainer];
386

387 388 389 390 391
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        _equalizerView = [[VLCEqualizerView alloc] initWithFrame:CGRectMake((rect.size.width - 450.) / 2., self.controllerPanel.frame.origin.y - 200., 450., 240.)];
    } else {
        _equalizerView = [[VLCEqualizerView alloc] initWithFrame:CGRectMake((rect.size.width - 450.) / 2., self.controllerPanel.frame.origin.y - 240., 450., 240.)];
    }
392 393
    _equalizerView.delegate = [VLCPlaybackController sharedInstance];
    _equalizerView.UIdelegate = self;
394 395 396
    _equalizerView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;
    _equalizerView.hidden = YES;
    [self.view addSubview:_equalizerView];
397

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
    /* add sleep timer UI, requires iOS 7 or later */
    if (SYSTEM_RUNS_IOS7_OR_LATER) {
        _sleepTimerContainer = [[VLCFrostedGlasView alloc] initWithFrame:CGRectMake(0., 0., 300., 162.)];
        _sleepTimerContainer.center = self.view.center;
        _sleepTimerContainer.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;

        _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];
        _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]];
            }
430
        }
431 432 433 434 435
        _sleepTimeDatePicker.datePickerMode = UIDatePickerModeCountDownTimer;
        _sleepTimeDatePicker.minuteInterval = 5;
        _sleepTimeDatePicker.minimumDate = [NSDate date];
        _sleepTimeDatePicker.countDownDuration = 1200.;
        [_sleepTimeDatePicker addTarget:self action:@selector(sleepTimerAction:) forControlEvents:UIControlEventValueChanged];
436

437 438
        [self.view addSubview:_sleepTimerContainer];
    }
439
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
440

441 442
- (void)viewWillAppear:(BOOL)animated
{
443
    [super viewWillAppear:animated];
444

445 446 447 448 449 450
    /* reset audio meta data views */
    self.artworkImageView.image = nil;
    self.trackNameLabel.text = nil;
    self.artistNameLabel.text = nil;
    self.albumNameLabel.text = nil;

451
    _swipeGesturesEnabled = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingPlaybackGestures] boolValue];
452
    _variableJumpDurationEnabled = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingVariableJumpDuration] boolValue];
453

454
    [self.navigationController setNavigationBarHidden:YES animated:YES];
455

Felix Paul Kühne's avatar
Felix Paul Kühne committed
456
    if (!SYSTEM_RUNS_IOS7_OR_LATER) {
457 458 459
        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
            [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackTranslucent;
    }
460

461
    [VLCPlaybackController sharedInstance].videoOutputView = self.movieView;
462 463

    [self setControlsHidden:NO animated:YES];
464 465 466 467 468
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
469
    _viewAppeared = YES;
470
    [[VLCPlaybackController sharedInstance] recoverDisplayedMetadata];
471 472
}

473 474
- (void)viewWillLayoutSubviews
{
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
    CGRect multiSelectionFrame;
    CGRect controllerPanelFrame;

    if (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPhone) {
        controllerPanelFrame = _controllerPanel.frame;
        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;
    }

    CGSize viewSize = self.view.frame.size;

    if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
        [_controllerPanel removeFromSuperview];
        controllerPanelFrame = (CGRect){CGPointMake(0, viewSize.height - _controllerPanelLandscape.frame.size.height), CGSizeMake(viewSize.width, _controllerPanelLandscape.frame.size.height)};
        _controllerPanelLandscape.frame = controllerPanelFrame;
        [self.view addSubview:_controllerPanelLandscape];
        _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 {
        [_controllerPanelLandscape removeFromSuperview];
        controllerPanelFrame = (CGRect){CGPointMake(0, viewSize.height - _controllerPanel.frame.size.height), CGSizeMake(viewSize.width, _controllerPanel.frame.size.height)};
        _controllerPanel.frame = controllerPanelFrame;
        [self.view addSubview:_controllerPanel];
        _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;
508
    }
509 510

    _multiSelectionView.frame = multiSelectionFrame;
511 512
}

513
- (void)viewWillDisappear:(BOOL)animated
Felix Paul Kühne's avatar
Felix Paul Kühne committed
514
{
515 516
    [VLCPlaybackController sharedInstance].videoOutputView = nil;

517
    _viewAppeared = NO;
518
    if (_idleTimer) {
519
        [_idleTimer invalidate];
520 521
        _idleTimer = nil;
    }
522
    [self.navigationController setNavigationBarHidden:NO animated:YES];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
523
    if (!SYSTEM_RUNS_IOS7_OR_LATER)
524
        [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackOpaque;
525
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
526
    [super viewWillDisappear:animated];
527

Felix Paul Kühne's avatar
Felix Paul Kühne committed
528 529 530 531 532 533
    // hide filter UI for next run
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

    if (!_playbackSpeedViewHidden)
        _playbackSpeedViewHidden = YES;
534 535

    [[NSUserDefaults standardUserDefaults] setBool:_displayRemainingTime forKey:kVLCShowRemainingTime];
536 537 538 539 540 541 542
}

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

543 544
#pragma mark - controls visibility

545 546
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer
{
547 548
    LOCKCHECK;

549 550 551
    if (!_swipeGesturesEnabled)
        return;

552 553 554 555
    if (recognizer.velocity < 0.)
        [self closePlayback:nil];
}

556 557
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
558
    if (touch.view != self.view)
559 560 561 562 563
        return NO;

    return YES;
}

Ahmad Harb's avatar
Ahmad Harb committed
564 565 566 567 568
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

569
- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated
570
{
571
    _controlsHidden = hidden;
572
    CGFloat alpha = _controlsHidden? 0.0f: 1.0f;
573

574 575
    if (!_controlsHidden) {
        _controllerPanel.alpha = 0.0f;
576
        _controllerPanel.hidden = !_videoFiltersHidden;
577 578
        _controllerPanelLandscape.alpha = 0.0f;
        _controllerPanelLandscape.hidden = !_videoFiltersHidden;
579 580
        _toolbar.alpha = 0.0f;
        _toolbar.hidden = NO;
581 582
        _videoFilterView.alpha = 0.0f;
        _videoFilterView.hidden = _videoFiltersHidden;
583 584
        _playbackSpeedView.alpha = 0.0f;
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
585 586
        _trackSelectorContainer.alpha = 0.0f;
        _trackSelectorContainer.hidden = YES;
587 588
        _equalizerView.alpha = 0.0f;
        _equalizerView.hidden = YES;
589 590 591 592
        if (_sleepTimerContainer) {
            _sleepTimerContainer.alpha = 0.0f;
            _sleepTimerContainer.hidden = YES;
        }
593 594
        _multiSelectionView.alpha = 0.0f;
        _multiSelectionView.hidden = YES;
595
    }
596

597 598
    void (^animationBlock)() = ^() {
        _controllerPanel.alpha = alpha;
599
        _controllerPanelLandscape.alpha = alpha;
600
        _toolbar.alpha = alpha;
601
        _videoFilterView.alpha = alpha;
602
        _playbackSpeedView.alpha = alpha;
603
        _trackSelectorContainer.alpha = alpha;
604
        _equalizerView.alpha = alpha;
605
        _multiSelectionView.alpha = alpha;
606 607
        if (_sleepTimerContainer)
            _sleepTimerContainer.alpha = alpha;
608
    };
609

610
    void (^completionBlock)(BOOL finished) = ^(BOOL finished) {
611 612
        _controllerPanel.hidden = _videoFiltersHidden ? _controlsHidden : NO;
        _controllerPanelLandscape.hidden = _videoFiltersHidden ? _controlsHidden : NO;
613
        _toolbar.hidden = _controlsHidden;
614
        _videoFilterView.hidden = _videoFiltersHidden;
615
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
616
        _trackSelectorContainer.hidden = YES;
617
        _equalizerView.hidden = YES;
618 619
        if (_sleepTimerContainer)
            _sleepTimerContainer.hidden = YES;
620
        _multiSelectionView.hidden = YES;
621
    };
622

623 624
    UIStatusBarAnimation animationType = animated? UIStatusBarAnimationFade: UIStatusBarAnimationNone;
    NSTimeInterval animationDuration = animated? 0.3: 0.0;
625 626

    [[UIApplication sharedApplication] setStatusBarHidden:_viewAppeared ? _controlsHidden : NO withAnimation:animationType];
627
    [UIView animateWithDuration:animationDuration animations:animationBlock completion:completionBlock];
628

629
    _volumeView.hidden = _volumeViewLandscape.hidden = _controllerPanel.hidden;
630
}
631

632
- (void)toggleControlsVisible
633
{
634 635 636 637 638
    if (!_trackSelectorContainer.hidden) {
        for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers)
            [recognizer setEnabled:YES];
    }

639 640 641
    if (_controlsHidden && !_videoFiltersHidden)
        _videoFiltersHidden = YES;

642
    [self setControlsHidden:!_controlsHidden animated:YES];
643 644
}

645
- (void)_resetIdleTimer
646 647
{
    if (!_idleTimer)
648
        _idleTimer = [NSTimer scheduledTimerWithTimeInterval:4.
649 650 651 652 653
                                                      target:self
                                                    selector:@selector(idleTimerExceeded)
                                                    userInfo:nil
                                                     repeats:NO];
    else {
654 655
        if (fabs([_idleTimer.fireDate timeIntervalSinceNow]) < 4.)
            [_idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:4.]];
656 657 658 659 660 661 662
    }
}

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

665 666 667 668 669 670
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

    if (!_playbackSpeedViewHidden)
        _playbackSpeedViewHidden = YES;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
671 672
    if (self.scrubIndicatorView.hidden == NO)
        self.scrubIndicatorView.hidden = YES;
673 674
}

675 676
- (UIResponder *)nextResponder
{
677
    [self _resetIdleTimer];
678 679 680
    return [super nextResponder];
}

681 682 683 684
#pragma mark - controls

- (IBAction)closePlayback:(id)sender
{
685 686
    LOCKCHECK;

687
    [self presentingViewControllerShouldBeClosed:nil];
688

689
    [[VLCPlaybackController sharedInstance] stopPlayback];
690 691
}

692 693
- (IBAction)positionSliderAction:(UISlider *)sender
{
694 695
    LOCKCHECK;

696 697 698
    /* 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. */
699
    [self performSelector:@selector(_setPositionForReal) withObject:nil afterDelay:0.3];
700 701 702 703 704 705
    if (_mediaDuration > 0) {
        VLCTime *newPosition = [VLCTime timeWithInt:(int)(_positionSlider.value * _mediaDuration)];
        [self.timeDisplay setTitle:newPosition.stringValue forState:UIControlStateNormal];
        self.timeDisplay.accessibilityLabel = [NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"PLAYBACK_POSITION", nil), newPosition.stringValue];
        _positionSet = NO;
    }
706
    [self _resetIdleTimer];
707 708
}

709 710 711
- (void)_setPositionForReal
{
    if (!_positionSet) {
712
        [VLCPlaybackController sharedInstance].mediaPlayer.position = _positionSlider.value;
713 714 715 716
        _positionSet = YES;
    }
}

717 718
- (IBAction)positionSliderTouchDown:(id)sender
{
719 720
    LOCKCHECK;

721 722
    [self _updateScrubLabel];
    self.scrubIndicatorView.hidden = NO;
723
    _isScrubbing = YES;
724 725 726 727
}

- (IBAction)positionSliderTouchUp:(id)sender
{
728 729
    LOCKCHECK;

730
    self.scrubIndicatorView.hidden = YES;
731
    _isScrubbing = NO;
732 733 734 735 736 737
}

- (void)_updateScrubLabel
{
    float speed = self.positionSlider.scrubbingSpeed;
    if (speed == 1.)
738
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HIGH", nil);
739
    else if (speed == .5)
740
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HALF", nil);
741
    else if (speed == .25)
742
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_QUARTER", nil);
743
    else
744
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_FINE", nil);
745 746

    [self _resetIdleTimer];
747 748 749 750
}

- (IBAction)positionSliderDrag:(id)sender
{
751 752
    LOCKCHECK;

753 754 755
    [self _updateScrubLabel];
}

756 757
- (IBAction)volumeSliderAction:(id)sender
{
758 759
    LOCKCHECK;

760 761 762
    [self _resetIdleTimer];
}

763 764 765
#pragma mark - playback controller delegation

- (void)playbackPositionUpdated:(VLCPlaybackController *)controller
766
{
767
    VLCMediaPlayer *mediaPlayer = controller.mediaPlayer;
768
    if (!_isScrubbing) {
769
        self.positionSlider.value = [mediaPlayer position];
770 771
    }

772
    if (_displayRemainingTime)
773
        [self.timeDisplay setTitle:[[mediaPlayer remainingTime] stringValue] forState:UIControlStateNormal];
774
    else
775
        [self.timeDisplay setTitle:[[mediaPlayer time] stringValue] forState:UIControlStateNormal];
776 777
}

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

    float playbackRate = controller.playbackRate;
    self.playbackSpeedSlider.value = playbackRate;
    double speed = pow(2, playbackRate / 17.);
    self.playbackSpeedIndicator.text = [NSString stringWithFormat:@"%.2fx", speed];
792

793
    VLCMediaPlayer *mediaPlayer = [VLCPlaybackController sharedInstance].mediaPlayer;
794

795 796
    self.audioDelaySlider.value = mediaPlayer.currentAudioPlaybackDelay / 1000000;
    self.audioDelayIndicator.text = [NSString stringWithFormat:@"%1.00f s", self.audioDelaySlider.value];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
797

798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817