VLCMovieViewController.m 61.7 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 "VLCStatusLabel.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
34

35 36
#define FORWARD_SWIPE_DURATION 30
#define BACKWARD_SWIPE_DURATION 10
37

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

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

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

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

    UIActionSheet *_subtitleActionSheet;
    UIActionSheet *_audiotrackActionSheet;

    NSTimer *_idleTimer;

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

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

    UIView *_trackSelectorContainer;
    UITableView *_trackSelectorTableView;
86 87

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

    UIView *_sleepTimerContainer;
    UIDatePicker *_sleepTimeDatePicker;

93
    NSInteger _mediaDuration;
94 95
}

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

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

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

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

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

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

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

144 145
    self.wantsFullScreenLayout = YES;

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

173 174 175 176
    _trackSwitcherButton.accessibilityLabel = NSLocalizedString(@"OPEN_TRACK_PANEL", nil);
    _trackSwitcherButton.isAccessibilityElement = YES;
    _trackSwitcherButtonLandscape.accessibilityLabel = NSLocalizedString(@"OPEN_TRACK_PANEL", nil);
    _trackSwitcherButtonLandscape.isAccessibilityElement = YES;
177 178
    _playbackSpeedButton.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedButton.isAccessibilityElement = YES;
179 180
    _playbackSpeedButtonLandscape.accessibilityLabel = _playbackSpeedLabel.text;
    _playbackSpeedButtonLandscape.isAccessibilityElement = YES;
181
    _videoFilterButton.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER", nil);
182
    _videoFilterButton.isAccessibilityElement = YES;
183
    _videoFilterButtonLandscape.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER", nil);
184
    _videoFilterButtonLandscape.isAccessibilityElement = YES;
185
    _resetVideoFilterButton.accessibilityLabel = NSLocalizedString(@"VIDEO_FILTER_RESET_BUTTON", nil);
186
    _resetVideoFilterButton.isAccessibilityElement = YES;
187 188
    UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(playPauseLongPress:)];
    [_playPauseButton addGestureRecognizer:longPressRecognizer];
189
    _playPauseButton.accessibilityLabel = NSLocalizedString(@"PLAY_PAUSE_BUTTON", nil);
190
    _playPauseButton.accessibilityHint = NSLocalizedString(@"LONGPRESS_TO_STOP", nil);
191
    _playPauseButton.isAccessibilityElement = YES;
192 193
    UILongPressGestureRecognizer *longPressRecognizerLandscape = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(playPauseLongPress:)];
    [_playPauseButton addGestureRecognizer:longPressRecognizerLandscape];
194
    _playPauseButtonLandscape.accessibilityLabel = NSLocalizedString(@"PLAY_PAUSE_BUTTON", nil);
195
    _playPauseButtonLandscape.accessibilityHint = NSLocalizedString(@"LONGPRESS_TO_STOP", nil);
196
    _playPauseButtonLandscape.isAccessibilityElement = YES;
197
    _bwdButton.accessibilityLabel = NSLocalizedString(@"BWD_BUTTON", nil);
198
    _bwdButton.isAccessibilityElement = YES;
199
    _bwdButtonLandscape.accessibilityLabel = NSLocalizedString(@"BWD_BUTTON", nil);
200
    _bwdButtonLandscape.isAccessibilityElement = YES;
201
    _fwdButton.accessibilityLabel = NSLocalizedString(@"FWD_BUTTON", nil);
202
    _fwdButton.isAccessibilityElement = YES;
203
    _fwdButtonLandscape.accessibilityLabel = NSLocalizedString(@"FWD_BUTTON", nil);
204
    _fwdButtonLandscape.isAccessibilityElement = YES;
205 206 207
    _sleepTimerButton.accessibilityLabel = NSLocalizedString(@"BUTTON_SLEEP_TIMER", nil);
    _sleepTimerButton.isAccessibilityElement = YES;
    [_sleepTimerButton setTitle:NSLocalizedString(@"BUTTON_SLEEP_TIMER", nil) forState:UIControlStateNormal];
208 209
    if (!SYSTEM_RUNS_IOS7_OR_LATER)
        _sleepTimerButton.hidden = YES;
210

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

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

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

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

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

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

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

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

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

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

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

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

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
    } else {
        [self.toolbar setBackgroundImage:[UIImage imageNamed:@"seekbarBg"] forBarMetrics:UIBarMetricsDefault];
302 303 304
        [self.backButton setBackgroundImage:[UIImage imageNamed:@"playbackDoneButton"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        [self.backButton setBackgroundImage:[UIImage imageNamed:@"playbackDoneButtonHighlight"] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
    }
305

306
    /* FIXME: there is a saner iOS 6+ API for this! */
307
    /* this looks a bit weird, but we need to support iOS 5 and should show the same appearance */
308 309 310 311 312 313 314
    void (^initVolumeSlider)(MPVolumeView *) = ^(MPVolumeView *volumeView){
        UISlider *volumeSlider = nil;
        for (id aView in volumeView.subviews){
            if ([[[aView class] description] isEqualToString:@"MPVolumeSlider"]){
                volumeSlider = (UISlider *)aView;
                break;
            }
315
        }
316 317 318 319 320 321 322 323 324 325 326 327 328
        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);
329

330
    _playerIsSetup = NO;
331

332 333
    [self.movieView setAccessibilityLabel:NSLocalizedString(@"VO_VIDEOPLAYER_TITLE", nil)];
    [self.movieView setAccessibilityHint:NSLocalizedString(@"VO_VIDEOPLAYER_DOUBLETAP", nil)];
334 335

    rect = self.view.frame;
336 337 338 339 340 341
    CGFloat width;
    CGFloat height;
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        width = 300.;
        height = 320.;
    } else {
342
        width = 420.;
343 344
        height = 470.;
    }
345

346
    _trackSelectorTableView = [[UITableView alloc] initWithFrame:CGRectMake(0., 0., width, height) style:UITableViewStylePlain];
347 348
    _trackSelectorTableView.delegate = self;
    _trackSelectorTableView.dataSource = self;
349
    _trackSelectorTableView.separatorColor = [UIColor clearColor];
350
    _trackSelectorTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
351 352 353
    _trackSelectorTableView.rowHeight = 44.;
    _trackSelectorTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    _trackSelectorTableView.sectionHeaderHeight = 28.;
354 355
    [_trackSelectorTableView registerClass:[VLCTrackSelectorTableViewCell class] forCellReuseIdentifier:TRACK_SELECTOR_TABLEVIEW_CELL];
    [_trackSelectorTableView registerClass:[VLCTrackSelectorHeaderView class] forHeaderFooterViewReuseIdentifier:TRACK_SELECTOR_TABLEVIEW_SECTIONHEADER];
356
    _trackSelectorTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
357

358
    _trackSelectorContainer = [[VLCFrostedGlasView alloc] initWithFrame:CGRectMake((rect.size.width - width) / 2., (rect.size.height - height) / 2., width, height)];
359
    [_trackSelectorContainer addSubview:_trackSelectorTableView];
360
    _trackSelectorContainer.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleHeight;
361 362 363 364 365 366
    _trackSelectorContainer.hidden = YES;

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

    [self.view addSubview:_trackSelectorContainer];
371

372 373 374 375 376
    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.)];
    }
377
    _equalizerView.delegate = self.playbackController;
378
    _equalizerView.UIdelegate = self;
379 380 381
    _equalizerView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;
    _equalizerView.hidden = YES;
    [self.view addSubview:_equalizerView];
382

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
    /* 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]];
            }
415
        }
416 417 418 419 420
        _sleepTimeDatePicker.datePickerMode = UIDatePickerModeCountDownTimer;
        _sleepTimeDatePicker.minuteInterval = 5;
        _sleepTimeDatePicker.minimumDate = [NSDate date];
        _sleepTimeDatePicker.countDownDuration = 1200.;
        [_sleepTimeDatePicker addTarget:self action:@selector(sleepTimerAction:) forControlEvents:UIControlEventValueChanged];
421

422 423
        [self.view addSubview:_sleepTimerContainer];
    }
424 425

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

428 429
- (void)viewWillAppear:(BOOL)animated
{
430
    [super viewWillAppear:animated];
431

432 433 434 435 436 437
    /* reset audio meta data views */
    self.artworkImageView.image = nil;
    self.trackNameLabel.text = nil;
    self.artistNameLabel.text = nil;
    self.albumNameLabel.text = nil;

438
    _swipeGesturesEnabled = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingPlaybackGestures] boolValue];
439
    _variableJumpDurationEnabled = [[[NSUserDefaults standardUserDefaults] objectForKey:kVLCSettingVariableJumpDuration] boolValue];
440

441
    [self.navigationController setNavigationBarHidden:YES animated:YES];
442

Felix Paul Kühne's avatar
Felix Paul Kühne committed
443
    if (!SYSTEM_RUNS_IOS7_OR_LATER) {
444 445 446
        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
            [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackTranslucent;
    }
447

448
    VLCPlaybackController *vpc = self.playbackController;
449
    vpc.delegate = self;
450
    [vpc recoverPlaybackState];
451

452
    [self screenBrightnessChanged:nil];
453
    [self setControlsHidden:NO animated:YES];
454 455 456 457 458
}

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

461
    VLCPlaybackController *vpc = self.playbackController;
462
    [vpc recoverDisplayedMetadata];
463 464
    vpc.videoOutputView = nil;
    vpc.videoOutputView = self.movieView;
465 466
}

467 468
- (void)viewWillLayoutSubviews
{
469 470 471 472 473 474 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
    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;
502
    }
503 504

    _multiSelectionView.frame = multiSelectionFrame;
505 506
}

507
- (void)viewWillDisappear:(BOOL)animated
Felix Paul Kühne's avatar
Felix Paul Kühne committed
508
{
509
    VLCPlaybackController *vpc = self.playbackController;
510 511 512
    if (vpc.videoOutputView == self.movieView) {
        vpc.videoOutputView = nil;
    }
513

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

Felix Paul Kühne's avatar
Felix Paul Kühne committed
525 526 527 528
    // hide filter UI for next run
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

529 530 531
    if (_equalizerView.hidden == NO)
        _equalizerView.hidden = YES;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
532 533
    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

        _artistNameLabel.hidden = NO;
        _albumNameLabel.hidden = NO;
        _trackNameLabel.hidden = NO;
599
    }
600

601 602
    void (^animationBlock)() = ^() {
        _controllerPanel.alpha = alpha;
603
        _controllerPanelLandscape.alpha = alpha;
604
        _toolbar.alpha = alpha;
605
        _videoFilterView.alpha = alpha;
606
        _playbackSpeedView.alpha = alpha;
607
        _trackSelectorContainer.alpha = alpha;
608
        _equalizerView.alpha = alpha;
609
        _multiSelectionView.alpha = alpha;
610 611
        if (_sleepTimerContainer)
            _sleepTimerContainer.alpha = alpha;
612 613 614 615 616

        CGFloat metaInfoAlpha = _audioOnly ? 1.0f : alpha;
        _artistNameLabel.alpha = metaInfoAlpha;
        _albumNameLabel.alpha = metaInfoAlpha;
        _trackNameLabel.alpha = metaInfoAlpha;
617
    };
618

619
    void (^completionBlock)(BOOL finished) = ^(BOOL finished) {
620 621
        _controllerPanel.hidden = _videoFiltersHidden ? _controlsHidden : NO;
        _controllerPanelLandscape.hidden = _videoFiltersHidden ? _controlsHidden : NO;
622
        _toolbar.hidden = _controlsHidden;
623
        _videoFilterView.hidden = _videoFiltersHidden;
624
        _playbackSpeedView.hidden = _playbackSpeedViewHidden;
625
        _trackSelectorContainer.hidden = YES;
626
        _equalizerView.hidden = YES;
627 628
        if (_sleepTimerContainer)
            _sleepTimerContainer.hidden = YES;
629
        _multiSelectionView.hidden = YES;
630 631 632 633

        _artistNameLabel.hidden = _audioOnly ? NO : _controlsHidden;
        _albumNameLabel.hidden =  _audioOnly ? NO : _controlsHidden;
        _trackNameLabel.hidden =  _audioOnly ? NO : _controlsHidden;
634
    };
635

636 637
    UIStatusBarAnimation animationType = animated? UIStatusBarAnimationFade: UIStatusBarAnimationNone;
    NSTimeInterval animationDuration = animated? 0.3: 0.0;
638 639

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

642
    _volumeView.hidden = _volumeViewLandscape.hidden = _controllerPanel.hidden;
643
}
644

645
- (void)toggleControlsVisible
646
{
647 648 649 650 651
    if (!_trackSelectorContainer.hidden) {
        for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers)
            [recognizer setEnabled:YES];
    }

652 653 654
    if (_controlsHidden && !_videoFiltersHidden)
        _videoFiltersHidden = YES;

655
    [self setControlsHidden:!_controlsHidden animated:YES];
656 657
}

658
- (void)_resetIdleTimer
659 660
{
    if (!_idleTimer)
661
        _idleTimer = [NSTimer scheduledTimerWithTimeInterval:4.
662 663 664 665 666
                                                      target:self
                                                    selector:@selector(idleTimerExceeded)
                                                    userInfo:nil
                                                     repeats:NO];
    else {
667 668
        if (fabs([_idleTimer.fireDate timeIntervalSinceNow]) < 4.)
            [_idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:4.]];
669 670 671 672 673 674 675
    }
}

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

678 679 680
    if (!_videoFiltersHidden)
        _videoFiltersHidden = YES;

681 682 683
    if (_equalizerView.hidden == NO)
        _equalizerView.hidden = YES;

684 685 686
    if (!_playbackSpeedViewHidden)
        _playbackSpeedViewHidden = YES;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
687 688
    if (self.scrubIndicatorView.hidden == NO)
        self.scrubIndicatorView.hidden = YES;
689 690
}

691 692
- (UIResponder *)nextResponder
{
693
    [self _resetIdleTimer];
694 695 696
    return [super nextResponder];
}

697 698 699 700
#pragma mark - controls

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

704
}
705 706
- (IBAction)positionSliderAction:(UISlider *)sender
{
707 708
    LOCKCHECK;

709 710 711
    /* 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. */
712
    [self performSelector:@selector(_setPositionForReal) withObject:nil afterDelay:0.3];
713
    if (_mediaDuration > 0) {
714 715
        VLCTime *newPosition = [VLCTime timeWithInt:(int)(sender.value * _mediaDuration)];
        [self.timeNavigationTitleView.timeDisplayButton setTitle:newPosition.stringValue forState:UIControlStateNormal];
716
        [self.timeNavigationTitleView setNeedsLayout];
717
        self.timeNavigationTitleView.timeDisplayButton.accessibilityLabel = [NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"PLAYBACK_POSITION", nil), newPosition.stringValue];
718 719
        _positionSet = NO;
    }
720
    [self _resetIdleTimer];
721 722
}

723 724 725
- (void)_setPositionForReal
{
    if (!_positionSet) {
726 727
        self.playbackController.mediaPlayer.position = self.timeNavigationTitleView.positionSlider.value;
        [self.playbackController setNeedsMetadataUpdate];
728 729 730 731
        _positionSet = YES;
    }
}

732 733
- (IBAction)positionSliderTouchDown:(id)sender
{
734 735
    LOCKCHECK;

736 737
    [self _updateScrubLabel];
    self.scrubIndicatorView.hidden = NO;
738
    _isScrubbing = YES;
739 740 741 742
}

- (IBAction)positionSliderTouchUp:(id)sender
{
743 744
    LOCKCHECK;

745
    self.scrubIndicatorView.hidden = YES;
746
    _isScrubbing = NO;
747 748 749 750
}

- (void)_updateScrubLabel
{
751
    float speed = self.timeNavigationTitleView.positionSlider.scrubbingSpeed;
752
    if (speed == 1.)
753
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HIGH", nil);
754
    else if (speed == .5)
755
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_HALF", nil);
756
    else if (speed == .25)
757
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_QUARTER", nil);
758
    else
759
        self.currentScrubSpeedLabel.text = NSLocalizedString(@"PLAYBACK_SCRUB_FINE", nil);
760 761

    [self _resetIdleTimer];
762 763 764 765
}

- (IBAction)positionSliderDrag:(id)sender
{
766 767
    LOCKCHECK;

768 769 770
    [self _updateScrubLabel];
}

771 772
- (IBAction)volumeSliderAction:(id)sender
{
773 774
    LOCKCHECK;

775 776 777
    [self _resetIdleTimer];
}

778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
- (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];
}

807 808
#pragma mark - playback controller delegation

809 810 811 812 813 814 815 816
- (VLCPlaybackController *)playbackController
{
    if (!_playbackController) {
        _playbackController = [VLCPlaybackController sharedInstance];
    }
    return _playbackController;
}

817
- (void)playbackPositionUpdated:(VLCPlaybackController *)controller
818
{
819
    VLCMediaPlayer *mediaPlayer = controller.mediaPlayer;
820
    if (!_isScrubbing) {
821
        self.timeNavigationTitleView.positionSlider.value = [mediaPlayer position];
822 823
    }

824
    if (_displayRemainingTime)
825
        [self.timeNavigationTitleView.timeDisplayButton setTitle:[[mediaPlayer remainingTime] stringValue] forState:UIControlStateNormal];
826
    else
827
        [self.timeNavigationTitleView.timeDisplayButton setTitle:[[mediaPlayer time] stringValue] forState:UIControlStateNormal];
828
    [self.timeNavigationTitleView setNeedsLayout];