VLCMovieViewController.m 60.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"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
35

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 63
    BOOL _controlsHidden;
    BOOL _videoFiltersHidden;
    BOOL _playbackSpeedViewHidden;

    UIActionSheet *_subtitleActionSheet;
    UIActionSheet *_audiotrackActionSheet;

    NSTimer *_idleTimer;

64
    BOOL _viewAppeared;
65
    BOOL _displayRemainingTime;
66
    BOOL _positionSet;
67
    BOOL _playerIsSetup;
68
    BOOL _isScrubbing;
69
    BOOL _interfaceIsLocked;
70
    BOOL _switchingTracksNotChapters;
71
    BOOL _audioOnly;
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 175 176 177
    _trackSwitcherButton.accessibilityLabel = NSLocalizedString(@"OPEN_TRACK_PANEL", nil);
    _trackSwitcherButton.isAccessibilityElement = YES;
    _trackSwitcherButtonLandscape.accessibilityLabel = NSLocalizedString(@"OPEN_TRACK_PANEL", nil);
    _trackSwitcherButtonLandscape.isAccessibilityElement = YES;
178 179
    _playbackSpeedButton.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedButton.isAccessibilityElement = YES;
180 181
    _playbackSpeedButtonLandscape.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedButtonLandscape.isAccessibilityElement = YES;
182
    _videoFilterButton.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER", nil);
183
    _videoFilterButton.isAccessibilityElement = YES;
184
    _videoFilterButtonLandscape.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER", nil);
185
    _videoFilterButtonLandscape.isAccessibilityElement = YES;
186
    _resetVideoFilterButton.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER_RESET_BUTTON", nil);
187
    _resetVideoFilterButton.isAccessibilityElement = YES;
188 189
    UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(playPauseLongPress:)];
    [_playPauseButton addGestureRecognizer:longPressRecognizer];
190
    _playPauseButton.accessibilityLabel = NSLocalizedString(@"PLAY_PAUSE_BUTTON", nil);
191
    _playPauseButton.accessibilityHint = NSLocalizedString(@"LONGPRESS_TO_STOP", nil);
192
    _playPauseButton.isAccessibilityElement = YES;
193 194
    UILongPressGestureRecognizer *longPressRecognizerLandscape = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(playPauseLongPress:)];
    [_playPauseButton addGestureRecognizer:longPressRecognizerLandscape];
195
    _playPauseButtonLandscape.accessibilityLabel = NSLocalizedString(@"PLAY_PAUSE_BUTTON", nil);
196
    _playPauseButtonLandscape.accessibilityHint = NSLocalizedString(@"LONGPRESS_TO_STOP", 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 211 212 213 214 215
    _multiSelectionView = [[VLCMultiSelectionMenuView alloc] init];
    _multiSelectionView.delegate = self;
    _multiSelectionView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin;
    _multiSelectionView.hidden = YES;
    [self.view addSubview:_multiSelectionView];

216
    _scrubHelpLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HELP", nil);
217

218 219
    self.playbackSpeedView.hidden = YES;
    _playbackSpeedViewHidden = YES;
220

221 222 223 224 225
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(handleExternalScreenDidConnect:)
                   name:UIScreenDidConnectNotification object:nil];
    [center addObserver:self selector:@selector(handleExternalScreenDidDisconnect:)
                   name:UIScreenDidDisconnectNotification object:nil];
226 227
    [center addObserver:self selector:@selector(screenBrightnessChanged:)
                   name:UIScreenBrightnessDidChangeNotification object:nil];
228 229 230 231
    [center addObserver:self
               selector:@selector(appBecameActive:)
                   name:UIApplicationDidBecomeActiveNotification
                 object:nil];
232

233 234
    _playingExternallyTitle.text = NSLocalizedString(@"PLAYING_EXTERNALLY_TITLE", nil);
    _playingExternallyDescription.text = NSLocalizedString(@"PLAYING_EXTERNALLY_DESC", nil);
235
    if ([[UIDevice currentDevice] hasExternalDisplay])
236
        [self showOnExternalDisplay];
237

238 239
    self.trackNameLabel.text = self.artistNameLabel.text = self.albumNameLabel.text = @"";

240
    _movieView.userInteractionEnabled = NO;
241 242 243
    _tapOnVideoRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControlsVisible)];
    _tapOnVideoRecognizer.delegate = self;
    [self.view addGestureRecognizer:_tapOnVideoRecognizer];
244

245 246 247
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    _displayRemainingTime = [[defaults objectForKey:kVLCShowRemainingTime] boolValue];
    _swipeGesturesEnabled = [[defaults objectForKey:kVLCSettingPlaybackGestures] boolValue];
248
    _variableJumpDurationEnabled = [[defaults objectForKey:kVLCSettingVariableJumpDuration] boolValue];
249

250 251 252 253 254 255
    _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];
256 257

    _currentPanType = VLCPanTypeNone;
258 259 260 261 262 263 264 265
    _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;
266 267 268 269 270 271
    _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;
272 273 274

    [self.view addGestureRecognizer:_swipeRecognizerLeft];
    [self.view addGestureRecognizer:_swipeRecognizerRight];
275 276
    [self.view addGestureRecognizer:_swipeRecognizerUp];
    [self.view addGestureRecognizer:_swipeRecognizerDown];
277 278 279 280
    [self.view addGestureRecognizer:_panRecognizer];
    [self.view addGestureRecognizer:_tapRecognizer];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerLeft];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerRight];
281 282
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerUp];
    [_panRecognizer requireGestureRecognizerToFail:_swipeRecognizerDown];
283 284 285 286

    _panRecognizer.delegate = self;
    _swipeRecognizerRight.delegate = self;
    _swipeRecognizerLeft.delegate = self;
287 288
    _swipeRecognizerUp.delegate = self;
    _swipeRecognizerDown.delegate = self;
289
    _tapRecognizer.delegate = self;
290

Felix Paul Kühne's avatar
Felix Paul Kühne committed
291 292 293 294 295 296 297 298 299 300 301
    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;
302

303
    /* FIXME: there is a saner iOS 6+ API for this! */
304
    /* this looks a bit weird, but we need to support iOS 5 and should show the same appearance */
305 306 307 308 309 310 311
    void (^initVolumeSlider)(MPVolumeView *) = ^(MPVolumeView *volumeView){
        UISlider *volumeSlider = nil;
        for (id aView in volumeView.subviews){
            if ([[[aView class] description] isEqualToString:@"MPVolumeSlider"]){
                volumeSlider = (UISlider *)aView;
                break;
            }
312
        }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
313
        [volumeView setVolumeThumbImage:[UIImage imageNamed:@"modernSliderKnob"] forState:UIControlStateNormal];
314 315 316 317 318 319 320
        [volumeSlider addTarget:self
                         action:@selector(volumeSliderAction:)
               forControlEvents:UIControlEventValueChanged];
    };

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

322
    _playerIsSetup = NO;
323

324 325
    [self.movieView setAccessibilityLabel:NSLocalizedString(@"VO_VIDEOPLAYER_TITLE", nil)];
    [self.movieView setAccessibilityHint:NSLocalizedString(@"VO_VIDEOPLAYER_DOUBLETAP", nil)];
326 327

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

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

350
    _trackSelectorContainer = [[VLCFrostedGlasView alloc] initWithFrame:CGRectMake((rect.size.width - width) / 2., (rect.size.height - height) / 2., width, height)];
351
    [_trackSelectorContainer addSubview:_trackSelectorTableView];
352
    _trackSelectorContainer.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleHeight;
353 354 355 356 357 358
    _trackSelectorContainer.hidden = YES;

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

    [self.view addSubview:_trackSelectorContainer];
363

364 365 366 367 368
    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.)];
    }
369
    _equalizerView.delegate = self.playbackController;
370
    _equalizerView.UIdelegate = self;
371 372 373
    _equalizerView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;
    _equalizerView.hidden = YES;
    [self.view addSubview:_equalizerView];
374

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

Felix Paul Kühne's avatar
Felix Paul Kühne committed
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
    _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]];
        }
406
    }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
407 408 409 410 411 412 413
    _sleepTimeDatePicker.datePickerMode = UIDatePickerModeCountDownTimer;
    _sleepTimeDatePicker.minuteInterval = 5;
    _sleepTimeDatePicker.minimumDate = [NSDate date];
    _sleepTimeDatePicker.countDownDuration = 1200.;
    [_sleepTimeDatePicker addTarget:self action:@selector(sleepTimerAction:) forControlEvents:UIControlEventValueChanged];

    [self.view addSubview:_sleepTimerContainer];
414 415

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackDidStop:) name:VLCPlaybackControllerPlaybackDidStop object:nil];
416
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
417

418 419
- (void)viewWillAppear:(BOOL)animated
{
420
    [super viewWillAppear:animated];
421

422 423 424 425 426 427
    /* reset audio meta data views */
    self.artworkImageView.image = nil;
    self.trackNameLabel.text = nil;
    self.artistNameLabel.text = nil;
    self.albumNameLabel.text = nil;

428
    _swipeGesturesEnabled = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingPlaybackGestures] boolValue];
429
    _variableJumpDurationEnabled = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingVariableJumpDuration] boolValue];
430

431
    [self.navigationController setNavigationBarHidden:YES animated:YES];
432

433
    VLCPlaybackController *vpc = self.playbackController;
434
    vpc.delegate = self;
435
    [vpc recoverPlaybackState];
436

437
    [self screenBrightnessChanged:nil];
438
    [self setControlsHidden:NO animated:YES];
439 440 441 442 443
}

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

446
    VLCPlaybackController *vpc = self.playbackController;
447
    [vpc recoverDisplayedMetadata];
448 449
    vpc.videoOutputView = nil;
    vpc.videoOutputView = self.movieView;
450 451
}

452 453
- (void)viewWillLayoutSubviews
{
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
    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;
487
    }
488 489

    _multiSelectionView.frame = multiSelectionFrame;
490 491
}

492
- (void)viewWillDisappear:(BOOL)animated
Felix Paul Kühne's avatar
Felix Paul Kühne committed
493
{
494
    VLCPlaybackController *vpc = self.playbackController;
495 496 497
    if (vpc.videoOutputView == self.movieView) {
        vpc.videoOutputView = nil;
    }
498

499
    _viewAppeared = NO;
500
    if (_idleTimer) {
501
        [_idleTimer invalidate];
502 503
        _idleTimer = nil;
    }
504
    [self.navigationController setNavigationBarHidden:NO animated:YES];
505
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
506
    [super viewWillDisappear:animated];
507

Felix Paul Kühne's avatar
Felix Paul Kühne committed
508 509 510 511
    // hide filter UI for next run
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

512 513 514
    if (_equalizerView.hidden == NO)
        _equalizerView.hidden = YES;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
515 516
    if (!_playbackSpeedViewHidden)
        _playbackSpeedViewHidden = YES;
517 518

    [[NSUserDefaults standardUserDefaults] setBool:_displayRemainingTime forKey:kVLCShowRemainingTime];
519 520 521 522 523 524 525
}

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

526 527
#pragma mark - controls visibility

528 529
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer
{
530 531
    LOCKCHECK;

532 533 534
    if (!_swipeGesturesEnabled)
        return;

535 536 537 538
    if (recognizer.velocity < 0.)
        [self closePlayback:nil];
}

539 540
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
541
    if (touch.view != self.view)
542 543 544 545 546
        return NO;

    return YES;
}

Ahmad Harb's avatar
Ahmad Harb committed
547 548 549 550 551
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

552
- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated
553
{
554
    _controlsHidden = hidden;
555
    CGFloat alpha = _controlsHidden? 0.0f: 1.0f;
556

557 558
    if (!_controlsHidden) {
        _controllerPanel.alpha = 0.0f;
559
        _controllerPanel.hidden = !_videoFiltersHidden;
560 561
        _controllerPanelLandscape.alpha = 0.0f;
        _controllerPanelLandscape.hidden = !_videoFiltersHidden;
562 563
        _toolbar.alpha = 0.0f;
        _toolbar.hidden = NO;
564 565
        _videoFilterView.alpha = 0.0f;
        _videoFilterView.hidden = _videoFiltersHidden;
566 567
        _playbackSpeedView.alpha = 0.0f;
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
568 569
        _trackSelectorContainer.alpha = 0.0f;
        _trackSelectorContainer.hidden = YES;
570 571
        _equalizerView.alpha = 0.0f;
        _equalizerView.hidden = YES;
572 573 574 575
        if (_sleepTimerContainer) {
            _sleepTimerContainer.alpha = 0.0f;
            _sleepTimerContainer.hidden = YES;
        }
576 577
        _multiSelectionView.alpha = 0.0f;
        _multiSelectionView.hidden = YES;
578 579 580 581

        _artistNameLabel.hidden = NO;
        _albumNameLabel.hidden = NO;
        _trackNameLabel.hidden = NO;
582
    }
583

584 585
    void (^animationBlock)() = ^() {
        _controllerPanel.alpha = alpha;
586
        _controllerPanelLandscape.alpha = alpha;
587
        _toolbar.alpha = alpha;
588
        _videoFilterView.alpha = alpha;
589
        _playbackSpeedView.alpha = alpha;
590
        _trackSelectorContainer.alpha = alpha;
591
        _equalizerView.alpha = alpha;
592
        _multiSelectionView.alpha = alpha;
593 594
        if (_sleepTimerContainer)
            _sleepTimerContainer.alpha = alpha;
595 596 597 598 599

        CGFloat metaInfoAlpha = _audioOnly ? 1.0f : alpha;
        _artistNameLabel.alpha = metaInfoAlpha;
        _albumNameLabel.alpha = metaInfoAlpha;
        _trackNameLabel.alpha = metaInfoAlpha;
600
    };
601

602
    void (^completionBlock)(BOOL finished) = ^(BOOL finished) {
603 604
        _controllerPanel.hidden = _videoFiltersHidden ? _controlsHidden : NO;
        _controllerPanelLandscape.hidden = _videoFiltersHidden ? _controlsHidden : NO;
605
        _toolbar.hidden = _controlsHidden;
606
        _videoFilterView.hidden = _videoFiltersHidden;
607
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
608
        _trackSelectorContainer.hidden = YES;
609
        _equalizerView.hidden = YES;
610 611
        if (_sleepTimerContainer)
            _sleepTimerContainer.hidden = YES;
612
        _multiSelectionView.hidden = YES;
613 614 615 616

        _artistNameLabel.hidden = _audioOnly ? NO : _controlsHidden;
        _albumNameLabel.hidden =  _audioOnly ? NO : _controlsHidden;
        _trackNameLabel.hidden =  _audioOnly ? NO : _controlsHidden;
617
    };
618

619 620
    UIStatusBarAnimation animationType = animated? UIStatusBarAnimationFade: UIStatusBarAnimationNone;
    NSTimeInterval animationDuration = animated? 0.3: 0.0;
621 622

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

625
    _volumeView.hidden = _volumeViewLandscape.hidden = _controllerPanel.hidden;
626
}
627

628
- (void)toggleControlsVisible
629
{
630 631 632 633 634
    if (!_trackSelectorContainer.hidden) {
        for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers)
            [recognizer setEnabled:YES];
    }

635 636 637
    if (_controlsHidden && !_videoFiltersHidden)
        _videoFiltersHidden = YES;

638
    [self setControlsHidden:!_controlsHidden animated:YES];
639 640
}

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

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

661 662 663
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

664 665 666
    if (_equalizerView.hidden == NO)
        _equalizerView.hidden = YES;

667 668 669
    if (!_playbackSpeedViewHidden)
        _playbackSpeedViewHidden = YES;

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

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

680 681 682 683
#pragma mark - controls

- (IBAction)closePlayback:(id)sender
{
684
    LOCKCHECK;
685
    [[UIApplication sharedApplication] sendAction:@selector(closeFullscreenPlayback) to:nil from:self forEvent:nil];
686

687
}
688 689
- (IBAction)positionSliderAction:(UISlider *)sender
{
690 691
    LOCKCHECK;

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

706 707 708
- (void)_setPositionForReal
{
    if (!_positionSet) {
709 710
        self.playbackController.mediaPlayer.position = self.timeNavigationTitleView.positionSlider.value;
        [self.playbackController setNeedsMetadataUpdate];
711 712 713 714
        _positionSet = YES;
    }
}

715 716
- (IBAction)positionSliderTouchDown:(id)sender
{
717 718
    LOCKCHECK;

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

- (IBAction)positionSliderTouchUp:(id)sender
{
726 727
    LOCKCHECK;

728
    self.scrubIndicatorView.hidden = YES;
729
    _isScrubbing = NO;
730 731 732 733
}

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

    [self _resetIdleTimer];
745 746 747 748
}

- (IBAction)positionSliderDrag:(id)sender
{
749 750
    LOCKCHECK;

751 752 753
    [self _updateScrubLabel];
}

754 755
- (IBAction)volumeSliderAction:(id)sender
{
756 757
    LOCKCHECK;

758 759 760
    [self _resetIdleTimer];
}

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
- (void)playPauseLongPress:(UILongPressGestureRecognizer *)recognizer
{
    switch (recognizer.state) {
        case UIGestureRecognizerStateBegan:
        {   UIImage *image = [UIImage imageNamed:@"stopIcon"];
            [_playPauseButton setImage:image forState:UIControlStateNormal];
            [_playPauseButtonLandscape setImage:image forState:UIControlStateNormal];
        }
            break;
        case UIGestureRecognizerStateEnded:
            [self.playbackController stopPlayback];
            break;
        case UIGestureRecognizerStateCancelled:
        case UIGestureRecognizerStateFailed:
            [self updatePlayPauseButton];
            break;
        default:
            break;
    }
}

- (void)updatePlayPauseButton
{
    const BOOL isPlaying = self.playbackController.isPlaying;
    UIImage *playPauseImage = isPlaying ? [UIImage imageNamed:@"pauseIcon"] : [UIImage imageNamed:@"playIcon"];
    [_playPauseButton setImage:playPauseImage forState:UIControlStateNormal];
    [_playPauseButtonLandscape setImage:playPauseImage forState:UIControlStateNormal];
}

790 791
#pragma mark - playback controller delegation

792 793 794 795 796 797 798 799
- (VLCPlaybackController *)playbackController
{
    if (!_playbackController) {
        _playbackController = [VLCPlaybackController sharedInstance];
    }
    return _playbackController;
}

800
- (void)playbackPositionUpdated:(VLCPlaybackController *)controller
801
{
802
    VLCMediaPlayer *mediaPlayer = controller.mediaPlayer;
803
    if (!_isScrubbing) {
804
        self.timeNavigationTitleView.positionSlider.value = [mediaPlayer position];
805 806
    }

807
    if (_displayRemainingTime)
808
        [self.timeNavigationTitleView.timeDisplayButton setTitle:[[mediaPlayer remainingTime] stringValue] forState:UIControlStateNormal];
809
    else
810
        [self.timeNavigationTitleView.timeDisplayButton setTitle:[[mediaPlayer time] stringValue] forState:UIControlStateNormal];