VLCFullscreenMovieTVViewController.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /*****************************************************************************
  2. * VLC for iOS
  3. *****************************************************************************
  4. * Copyright (c) 2015 VideoLAN. All rights reserved.
  5. * $Id$
  6. *
  7. * Authors: Felix Paul Kühne <fkuehne # videolan.org>
  8. *
  9. * Refer to the COPYING file of the official project for license.
  10. *****************************************************************************/
  11. #import "VLCFullscreenMovieTVViewController.h"
  12. #import "VLCPlaybackInfoTVViewController.h"
  13. #import "VLCPlaybackInfoTVAnimators.h"
  14. @interface VLCFullscreenMovieTVViewController (UIViewControllerTransitioningDelegate) <UIViewControllerTransitioningDelegate, UIGestureRecognizerDelegate>
  15. @end
  16. @interface VLCFullscreenMovieTVViewController ()
  17. @property (nonatomic) NSTimer *hidePlaybackControlsViewAfterDeleayTimer;
  18. @property (nonatomic) VLCPlaybackInfoTVViewController *infoViewController;
  19. @end
  20. @implementation VLCFullscreenMovieTVViewController
  21. + (instancetype)fullscreenMovieTVViewController
  22. {
  23. return [[self alloc] initWithNibName:nil bundle:nil];
  24. }
  25. - (void)viewDidLoad {
  26. [super viewDidLoad];
  27. self.extendedLayoutIncludesOpaqueBars = YES;
  28. self.edgesForExtendedLayout = UIRectEdgeAll;
  29. NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  30. [center addObserver:self
  31. selector:@selector(playbackDidStop:)
  32. name:VLCPlaybackControllerPlaybackDidStop
  33. object:nil];
  34. _movieView.userInteractionEnabled = NO;
  35. self.titleLabel.text = @"";
  36. self.transportBar.bufferStartFraction = 0.0;
  37. self.transportBar.bufferEndFraction = 1.0;
  38. self.transportBar.playbackFraction = 0.0;
  39. self.transportBar.scrubbingFraction = 0.0;
  40. self.dimmingView.alpha = 0.0;
  41. self.bottomOverlayView.alpha = 0.0;
  42. self.bufferingLabel.text = NSLocalizedString(@"PLEASE_WAIT", nil);
  43. // Panning and Swiping
  44. UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)];
  45. [self.view addGestureRecognizer:panGestureRecognizer];
  46. // Button presses
  47. UITapGestureRecognizer *playpauseGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(playPausePressed)];
  48. playpauseGesture.allowedPressTypes = @[@(UIPressTypePlayPause)];
  49. [self.view addGestureRecognizer:playpauseGesture];
  50. UITapGestureRecognizer *selectTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(selectButtonPressed:)];
  51. selectTapGestureRecognizer.allowedPressTypes = @[@(UIPressTypeSelect)];
  52. [self.view addGestureRecognizer:selectTapGestureRecognizer];
  53. UITapGestureRecognizer *menuTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(menuButtonPressed:)];
  54. menuTapGestureRecognizer.allowedPressTypes = @[@(UIPressTypeMenu)];
  55. menuTapGestureRecognizer.delegate = self;
  56. [self.view addGestureRecognizer:menuTapGestureRecognizer];
  57. UITapGestureRecognizer *upArrowRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showInfoVCIfNotScrubbing)];
  58. upArrowRecognizer.allowedPressTypes = @[@(UIPressTypeUpArrow)];
  59. [self.view addGestureRecognizer:upArrowRecognizer];
  60. }
  61. - (void)didReceiveMemoryWarning
  62. {
  63. [super didReceiveMemoryWarning];
  64. self.infoViewController = nil;
  65. }
  66. #pragma mark - view events
  67. - (void)viewWillAppear:(BOOL)animated
  68. {
  69. [super viewWillAppear:animated];
  70. VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  71. vpc.delegate = self;
  72. [vpc recoverPlaybackState];
  73. }
  74. - (void)viewDidAppear:(BOOL)animated
  75. {
  76. [super viewDidAppear:animated];
  77. VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  78. [vpc recoverDisplayedMetadata];
  79. vpc.videoOutputView = nil;
  80. vpc.videoOutputView = self.movieView;
  81. [[NSNotificationCenter defaultCenter] removeObserver:self];
  82. }
  83. - (void)viewWillDisappear:(BOOL)animated
  84. {
  85. VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  86. if (vpc.videoOutputView == self.movieView) {
  87. vpc.videoOutputView = nil;
  88. }
  89. [vpc stopPlayback];
  90. [super viewWillDisappear:animated];
  91. }
  92. - (BOOL)canBecomeFirstResponder
  93. {
  94. return YES;
  95. }
  96. #pragma mark - UIActions
  97. - (void)playPausePressed
  98. {
  99. [self showPlaybackControlsIfNeededForUserInteraction];
  100. VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  101. if (self.transportBar.scrubbing) {
  102. [self selectButtonPressed:nil];
  103. } else {
  104. [vpc playPause];
  105. }
  106. }
  107. - (void)panGesture:(UIPanGestureRecognizer *)panGestureRecognizer
  108. {
  109. switch (panGestureRecognizer.state) {
  110. case UIGestureRecognizerStateCancelled:
  111. case UIGestureRecognizerStateFailed:
  112. return;
  113. default:
  114. break;
  115. }
  116. VLCTransportBar *bar = self.transportBar;
  117. UIView *view = self.view;
  118. CGPoint translation = [panGestureRecognizer translationInView:view];
  119. if (!bar.scrubbing) {
  120. if (ABS(translation.x) > 150.0) {
  121. [self startScrubbing];
  122. } else if (translation.y > 200.0) {
  123. panGestureRecognizer.enabled = NO;
  124. panGestureRecognizer.enabled = YES;
  125. [self showInfoVCIfNotScrubbing];
  126. return;
  127. } else {
  128. return;
  129. }
  130. }
  131. [self showPlaybackControlsIfNeededForUserInteraction];
  132. const CGFloat scaleFactor = 8.0;
  133. CGFloat fractionInView = translation.x/CGRectGetWidth(view.bounds)/scaleFactor;
  134. CGFloat scrubbingFraction = MAX(0.0, MIN(bar.scrubbingFraction + fractionInView,1.0));
  135. if (ABS(scrubbingFraction - bar.playbackFraction)<0.01) {
  136. scrubbingFraction = bar.playbackFraction;
  137. } else {
  138. translation.x = 0.0;
  139. [panGestureRecognizer setTranslation:translation inView:view];
  140. }
  141. [UIView animateWithDuration:0.3
  142. delay:0.0
  143. options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState
  144. animations:^{
  145. bar.scrubbingFraction = scrubbingFraction;
  146. }
  147. completion:nil];
  148. [self updateTimeLabelsForScrubbingFraction:scrubbingFraction];
  149. }
  150. - (void)selectButtonPressed:(UITapGestureRecognizer *)recognizer
  151. {
  152. [self showPlaybackControlsIfNeededForUserInteraction];
  153. VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  154. VLCTransportBar *bar = self.transportBar;
  155. if (bar.scrubbing) {
  156. bar.playbackFraction = bar.scrubbingFraction;
  157. [vpc.mediaPlayer setPosition:bar.scrubbingFraction];
  158. [self stopScrubbing];
  159. } else if(vpc.mediaPlayer.playing) {
  160. [vpc.mediaPlayer pause];
  161. }
  162. }
  163. - (void)menuButtonPressed:(UITapGestureRecognizer *)recognizer
  164. {
  165. VLCTransportBar *bar = self.transportBar;
  166. if (bar.scrubbing) {
  167. [UIView animateWithDuration:0.3 animations:^{
  168. bar.scrubbingFraction = bar.playbackFraction;
  169. [bar layoutIfNeeded];
  170. }];
  171. [self updateTimeLabelsForScrubbingFraction:bar.playbackFraction];
  172. [self stopScrubbing];
  173. [self hidePlaybackControlsIfNeededAfterDelay];
  174. }
  175. }
  176. - (void)showInfoVCIfNotScrubbing
  177. {
  178. if (self.transportBar.scrubbing) {
  179. return;
  180. }
  181. // TODO: configure with player info
  182. VLCPlaybackInfoTVViewController *infoViewController = self.infoViewController;
  183. infoViewController.transitioningDelegate = self;
  184. [self presentViewController:infoViewController animated:YES completion:nil];
  185. [self animatePlaybackControlsToVisibility:NO];
  186. }
  187. #pragma mark -
  188. - (void)updateTimeLabelsForScrubbingFraction:(CGFloat)scrubbingFraction
  189. {
  190. VLCTransportBar *bar = self.transportBar;
  191. VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  192. // MAX 1, _ is ugly hack to prevent --:-- instead of 00:00
  193. int scrubbingTimeInt = MAX(1,vpc.mediaDuration*scrubbingFraction);
  194. VLCTime *scrubbingTime = [VLCTime timeWithInt:scrubbingTimeInt];
  195. bar.markerTimeLabel.text = [scrubbingTime stringValue];
  196. VLCTime *remainingTime = [VLCTime timeWithInt:(int)vpc.mediaDuration-scrubbingTime.intValue];
  197. bar.remainingTimeLabel.text = [remainingTime stringValue];
  198. }
  199. - (void)startScrubbing
  200. {
  201. VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  202. self.transportBar.scrubbing = YES;
  203. [self updateDimmingView];
  204. if (vpc.isPlaying) {
  205. [vpc playPause];
  206. }
  207. }
  208. - (void)stopScrubbing
  209. {
  210. self.transportBar.scrubbing = NO;
  211. [self updateDimmingView];
  212. VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  213. [vpc.mediaPlayer play];
  214. }
  215. - (void)updateDimmingView
  216. {
  217. BOOL shouldBeVisible = self.transportBar.scrubbing;
  218. BOOL isVisible = self.dimmingView.alpha == 1.0;
  219. if (shouldBeVisible != isVisible) {
  220. [UIView animateWithDuration:0.3 animations:^{
  221. self.dimmingView.alpha = shouldBeVisible ? 1.0 : 0.0;
  222. }];
  223. }
  224. }
  225. - (void)updateActivityIndicatorForState:(VLCMediaPlayerState)state {
  226. UIActivityIndicatorView *indicator = self.activityIndicator;
  227. switch (state) {
  228. case VLCMediaPlayerStateBuffering:
  229. if (!indicator.isAnimating) {
  230. self.activityIndicator.alpha = 1.0;
  231. [self.activityIndicator startAnimating];
  232. }
  233. break;
  234. default:
  235. if (indicator.isAnimating) {
  236. [self.activityIndicator stopAnimating];
  237. self.activityIndicator.alpha = 0.0;
  238. }
  239. break;
  240. }
  241. }
  242. #pragma mark - PlaybackControls
  243. - (void)fireHidePlaybackControlsIfNotPlayingTimer:(NSTimer *)timer
  244. {
  245. BOOL playing = [[VLCPlaybackController sharedInstance] isPlaying];
  246. if (playing) {
  247. [self animatePlaybackControlsToVisibility:NO];
  248. }
  249. }
  250. - (void)showPlaybackControlsIfNeededForUserInteraction
  251. {
  252. if (self.bottomOverlayView.alpha == 0.0) {
  253. [self animatePlaybackControlsToVisibility:YES];
  254. }
  255. [self hidePlaybackControlsIfNeededAfterDelay];
  256. }
  257. - (void)hidePlaybackControlsIfNeededAfterDelay
  258. {
  259. self.hidePlaybackControlsViewAfterDeleayTimer = [NSTimer scheduledTimerWithTimeInterval:3.0
  260. target:self
  261. selector:@selector(fireHidePlaybackControlsIfNotPlayingTimer:)
  262. userInfo:nil repeats:NO];
  263. }
  264. - (void)animatePlaybackControlsToVisibility:(BOOL)visible
  265. {
  266. NSTimeInterval duration = visible ? 0.3 : 1.0;
  267. CGFloat alpha = visible ? 1.0 : 0.0;
  268. [UIView animateWithDuration:duration
  269. animations:^{
  270. self.bottomOverlayView.alpha = alpha;
  271. }];
  272. }
  273. #pragma mark - Properties
  274. - (void)setHidePlaybackControlsViewAfterDeleayTimer:(NSTimer *)hidePlaybackControlsViewAfterDeleayTimer {
  275. [_hidePlaybackControlsViewAfterDeleayTimer invalidate];
  276. _hidePlaybackControlsViewAfterDeleayTimer = hidePlaybackControlsViewAfterDeleayTimer;
  277. }
  278. - (VLCPlaybackInfoTVViewController *)infoViewController
  279. {
  280. if (!_infoViewController) {
  281. _infoViewController = [[VLCPlaybackInfoTVViewController alloc] initWithNibName:nil bundle:nil];
  282. }
  283. return _infoViewController;
  284. }
  285. #pragma mark - playback controller delegation
  286. - (void)prepareForMediaPlayback:(VLCPlaybackController *)controller
  287. {
  288. APLog(@"%s", __PRETTY_FUNCTION__);
  289. }
  290. - (void)playbackDidStop:(NSNotification *)aNotification
  291. {
  292. [self dismissViewControllerAnimated:YES completion:nil];
  293. }
  294. - (void)mediaPlayerStateChanged:(VLCMediaPlayerState)currentState
  295. isPlaying:(BOOL)isPlaying
  296. currentMediaHasTrackToChooseFrom:(BOOL)currentMediaHasTrackToChooseFrom
  297. currentMediaHasChapters:(BOOL)currentMediaHasChapters
  298. forPlaybackController:(VLCPlaybackController *)controller
  299. {
  300. [self updateActivityIndicatorForState:currentState];
  301. if (controller.isPlaying) {
  302. [self hidePlaybackControlsIfNeededAfterDelay];
  303. } else {
  304. [self showPlaybackControlsIfNeededForUserInteraction];
  305. }
  306. if (controller.isPlaying && !self.bufferingLabel.hidden) {
  307. [UIView animateWithDuration:.3 animations:^{
  308. self.bufferingLabel.hidden = YES;
  309. }];
  310. }
  311. }
  312. - (void)displayMetadataForPlaybackController:(VLCPlaybackController *)controller
  313. title:(NSString *)title
  314. artwork:(UIImage *)artwork
  315. artist:(NSString *)artist
  316. album:(NSString *)album
  317. audioOnly:(BOOL)audioOnly
  318. {
  319. self.titleLabel.text = title;
  320. }
  321. #pragma mark -
  322. - (void)playbackPositionUpdated:(VLCPlaybackController *)controller
  323. {
  324. VLCMediaPlayer *mediaPlayer = [VLCPlaybackController sharedInstance].mediaPlayer;
  325. // FIXME: hard coded state since the state in mediaPlayer is incorrectly still buffering
  326. [self updateActivityIndicatorForState:VLCMediaPlayerStatePlaying];
  327. VLCTransportBar *transportBar = self.transportBar;
  328. transportBar.remainingTimeLabel.text = [[mediaPlayer remainingTime] stringValue];
  329. transportBar.markerTimeLabel.text = [[mediaPlayer time] stringValue];
  330. transportBar.playbackFraction = mediaPlayer.position;
  331. }
  332. #pragma mark - gesture recognizer delegate
  333. - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
  334. if ([gestureRecognizer.allowedPressTypes containsObject:@(UIPressTypeMenu)]) {
  335. return self.transportBar.scrubbing;
  336. }
  337. return YES;
  338. }
  339. @end
  340. @implementation VLCFullscreenMovieTVViewController (UIViewControllerTransitioningDelegate)
  341. - (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
  342. {
  343. return [[VLCPlaybackInfoTVTransitioningAnimator alloc] init];
  344. }
  345. - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
  346. {
  347. return [[VLCPlaybackInfoTVTransitioningAnimator alloc] init];
  348. }
  349. @end