VLCOpenNetworkStreamViewController.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /*****************************************************************************
  2. * VLCOpenNetworkStreamViewController.m
  3. * VLC for iOS
  4. *****************************************************************************
  5. * Copyright (c) 2013-2015 VideoLAN. All rights reserved.
  6. * $Id$
  7. *
  8. * Authors: Felix Paul Kühne <fkuehne # videolan.org>
  9. * Gleb Pinigin <gpinigin # gmail.com>
  10. * Pierre Sagaspe <pierre.sagaspe # me.com>
  11. *
  12. * Refer to the COPYING file of the official project for license.
  13. *****************************************************************************/
  14. #import "VLCOpenNetworkStreamViewController.h"
  15. #import "VLCPlaybackController.h"
  16. #import "VLCLibraryViewController.h"
  17. #import "VLCMenuTableViewController.h"
  18. #import "UIDevice+VLC.h"
  19. @interface VLCOpenNetworkStreamViewController () <UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate>
  20. {
  21. NSMutableArray *_recentURLs;
  22. }
  23. @end
  24. @implementation VLCOpenNetworkStreamViewController
  25. + (void)initialize
  26. {
  27. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  28. NSDictionary *appDefaults = @{kVLCRecentURLs : @[], kVLCPrivateWebStreaming : @(NO)};
  29. [defaults registerDefaults:appDefaults];
  30. }
  31. - (void)applicationDidBecomeActive:(NSNotification *)notification
  32. {
  33. [self updatePasteboardTextInURLField];
  34. }
  35. - (void)ubiquitousKeyValueStoreDidChange:(NSNotification *)notification
  36. {
  37. /* TODO: don't blindly trust that the Cloud knows best */
  38. _recentURLs = [NSMutableArray arrayWithArray:[[NSUbiquitousKeyValueStore defaultStore] arrayForKey:kVLCRecentURLs]];
  39. [self.historyTableView reloadData];
  40. }
  41. - (void)dealloc
  42. {
  43. [[NSNotificationCenter defaultCenter] removeObserver:self];
  44. }
  45. - (void)viewDidLoad
  46. {
  47. [super viewDidLoad];
  48. NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
  49. [notificationCenter addObserver:self
  50. selector:@selector(ubiquitousKeyValueStoreDidChange:)
  51. name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
  52. object:[NSUbiquitousKeyValueStore defaultStore]];
  53. /* force store update */
  54. NSUbiquitousKeyValueStore *ubiquitousKeyValueStore = [NSUbiquitousKeyValueStore defaultStore];
  55. [ubiquitousKeyValueStore synchronize];
  56. /* fetch data from cloud */
  57. _recentURLs = [NSMutableArray arrayWithArray:[[NSUbiquitousKeyValueStore defaultStore] arrayForKey:kVLCRecentURLs]];
  58. /* merge data from local storage (aka legacy VLC versions) */
  59. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  60. NSArray *localRecentUrls = [defaults objectForKey:kVLCRecentURLs];
  61. if (localRecentUrls != nil) {
  62. if (localRecentUrls.count != 0) {
  63. [_recentURLs addObjectsFromArray:localRecentUrls];
  64. [defaults setObject:nil forKey:kVLCRecentURLs];
  65. [ubiquitousKeyValueStore setArray:_recentURLs forKey:kVLCRecentURLs];
  66. [ubiquitousKeyValueStore synchronize];
  67. }
  68. }
  69. /*
  70. * Observe changes to the pasteboard so we can automatically paste it into the URL field.
  71. * Do not use UIPasteboardChangedNotification because we have copy actions that will trigger it on this screen.
  72. * Instead when the user comes back to the application from the background (or the inactive state by pulling down notification center), update the URL field.
  73. * Using the 'active' rather than 'foreground' notification for future proofing if iOS ever allows running multiple apps on the same screen (which would allow the pasteboard to be changed without truly backgrounding the app).
  74. */
  75. [notificationCenter addObserver:self
  76. selector:@selector(applicationDidBecomeActive:)
  77. name:UIApplicationDidBecomeActiveNotification
  78. object:[UIApplication sharedApplication]];
  79. [self.openButton setTitle:NSLocalizedString(@"OPEN_NETWORK", nil) forState:UIControlStateNormal];
  80. [self.privateModeLabel setText:NSLocalizedString(@"PRIVATE_PLAYBACK_TOGGLE", nil)];
  81. UILabel *scanSubModelabel = self.ScanSubModeLabel;
  82. [scanSubModelabel setText:NSLocalizedString(@"SCAN_SUBTITLE_TOGGLE", nil)];
  83. [scanSubModelabel setAdjustsFontSizeToFitWidth:YES];
  84. [scanSubModelabel setNumberOfLines:0];
  85. self.title = NSLocalizedString(@"OPEN_NETWORK", nil);
  86. self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedRevealMenuButtonWithTarget:self andSelector:@selector(goBack:)];
  87. [self.whatToOpenHelpLabel setText:NSLocalizedString(@"OPEN_NETWORK_HELP", nil)];
  88. self.urlField.delegate = self;
  89. self.urlField.keyboardType = UIKeyboardTypeURL;
  90. self.historyTableView.backgroundColor = [UIColor VLCDarkBackgroundColor];
  91. NSAttributedString *coloredAttributedPlaceholder = [[NSAttributedString alloc] initWithString:@"http://myserver.com/file.mkv" attributes:@{NSForegroundColorAttributeName: [UIColor VLCLightTextColor]}];
  92. self.urlField.attributedPlaceholder = coloredAttributedPlaceholder;
  93. self.edgesForExtendedLayout = UIRectEdgeNone;
  94. // This will be called every time this VC is opened by the side menu controller
  95. [self updatePasteboardTextInURLField];
  96. }
  97. - (void)updatePasteboardTextInURLField
  98. {
  99. UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
  100. if ([pasteboard containsPasteboardTypes:@[@"public.url"]])
  101. self.urlField.text = [[pasteboard valueForPasteboardType:@"public.url"] absoluteString];
  102. }
  103. - (void)viewWillAppear:(BOOL)animated
  104. {
  105. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  106. self.privateToggleSwitch.on = [defaults boolForKey:kVLCPrivateWebStreaming];
  107. self.ScanSubToggleSwitch.on = [defaults boolForKey:kVLChttpScanSubtitle];
  108. [super viewWillAppear:animated];
  109. }
  110. - (void)viewWillDisappear:(BOOL)animated
  111. {
  112. [[NSNotificationCenter defaultCenter] removeObserver:self
  113. name:UIApplicationDidBecomeActiveNotification
  114. object:[UIApplication sharedApplication]];
  115. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  116. [defaults setBool:self.privateToggleSwitch.on forKey:kVLCPrivateWebStreaming];
  117. [defaults setBool:self.ScanSubToggleSwitch.on forKey:kVLChttpScanSubtitle];
  118. [defaults synchronize];
  119. /* force update before we leave */
  120. [[NSUbiquitousKeyValueStore defaultStore] synchronize];
  121. [super viewWillDisappear:animated];
  122. }
  123. - (CGSize)contentSizeForViewInPopover {
  124. return [self.view sizeThatFits:CGSizeMake(320, 800)];
  125. }
  126. #pragma mark - UI interaction
  127. - (BOOL)shouldAutorotate
  128. {
  129. UIInterfaceOrientation toInterfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
  130. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
  131. return NO;
  132. return YES;
  133. }
  134. - (IBAction)goBack:(id)sender
  135. {
  136. [self.view endEditing:YES];
  137. [[VLCSidebarController sharedInstance] toggleSidebar];
  138. }
  139. - (IBAction)openButtonAction:(id)sender
  140. {
  141. if ([self.urlField.text length] > 0) {
  142. if (!self.privateToggleSwitch.on) {
  143. if ([_recentURLs indexOfObject:self.urlField.text] != NSNotFound)
  144. [_recentURLs removeObject:self.urlField.text];
  145. if (_recentURLs.count >= 100)
  146. [_recentURLs removeLastObject];
  147. [_recentURLs addObject:self.urlField.text];
  148. [[NSUbiquitousKeyValueStore defaultStore] setArray:_recentURLs forKey:kVLCRecentURLs];
  149. [self.historyTableView reloadData];
  150. }
  151. [self.urlField resignFirstResponder];
  152. [self performSelectorInBackground:@selector(_openURLStringAndDismiss:) withObject:self.urlField.text];
  153. }
  154. }
  155. #pragma mark - table view data source
  156. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  157. {
  158. return 1;
  159. }
  160. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  161. {
  162. return _recentURLs.count;
  163. }
  164. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  165. {
  166. static NSString *CellIdentifier = @"StreamingHistoryCell";
  167. UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  168. if (cell == nil) {
  169. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
  170. cell.textLabel.textColor = [UIColor whiteColor];
  171. cell.textLabel.highlightedTextColor = [UIColor blackColor];
  172. cell.detailTextLabel.textColor = [UIColor VLCLightTextColor];
  173. cell.detailTextLabel.highlightedTextColor = [UIColor blackColor];
  174. }
  175. NSString *content = _recentURLs[indexPath.row];
  176. cell.textLabel.text = [content lastPathComponent];
  177. cell.detailTextLabel.text = content;
  178. return cell;
  179. }
  180. #pragma mark - table view delegate
  181. - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
  182. {
  183. cell.backgroundColor = (indexPath.row % 2 == 0)? [UIColor blackColor]: [UIColor VLCDarkBackgroundColor];
  184. }
  185. - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
  186. {
  187. return YES;
  188. }
  189. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
  190. {
  191. if (editingStyle == UITableViewCellEditingStyleDelete) {
  192. [_recentURLs removeObjectAtIndex:indexPath.row];
  193. [[NSUbiquitousKeyValueStore defaultStore] setArray:_recentURLs forKey:kVLCRecentURLs];
  194. [tableView reloadData];
  195. }
  196. }
  197. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  198. {
  199. [self.historyTableView deselectRowAtIndexPath:indexPath animated:NO];
  200. [self performSelectorInBackground:@selector(_openURLStringAndDismiss:) withObject:_recentURLs[indexPath.row]];
  201. }
  202. - (void)tableView:(UITableView *)tableView
  203. performAction:(SEL)action
  204. forRowAtIndexPath:(NSIndexPath *)indexPath
  205. withSender:(id)sender
  206. {
  207. NSString *actionText = NSStringFromSelector(action);
  208. if ([actionText isEqualToString:@"copy:"])
  209. [UIPasteboard generalPasteboard].string = _recentURLs[indexPath.row];
  210. }
  211. - (BOOL)tableView:(UITableView *)tableView
  212. canPerformAction:(SEL)action
  213. forRowAtIndexPath:(NSIndexPath *)indexPath
  214. withSender:(id)sender
  215. {
  216. NSString *actionText = NSStringFromSelector(action);
  217. if ([actionText isEqualToString:@"copy:"])
  218. return YES;
  219. return NO;
  220. }
  221. - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath
  222. {
  223. return YES;
  224. }
  225. #pragma mark - internals
  226. - (void)_openURLStringAndDismiss:(NSString *)url
  227. {
  228. NSURL *URLscheme = [NSURL URLWithString:url];
  229. NSString *URLofSubtitle = nil;
  230. if ([URLscheme.scheme isEqualToString:@"http"])
  231. if (self.ScanSubToggleSwitch.on)
  232. URLofSubtitle = [self _checkURLofSubtitle:url];
  233. VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  234. [vpc playURL:[NSURL URLWithString:url] subtitlesFilePath:URLofSubtitle];
  235. }
  236. - (NSString *)_checkURLofSubtitle:(NSString *)url
  237. {
  238. NSCharacterSet *characterFilter = [NSCharacterSet characterSetWithCharactersInString:@"\\.():$"];
  239. NSString *subtitleFileExtensions = [[kSupportedSubtitleFileExtensions componentsSeparatedByCharactersInSet:characterFilter] componentsJoinedByString:@""];
  240. NSArray *arraySubtitleFileExtensions = [subtitleFileExtensions componentsSeparatedByString:@"|"];
  241. NSString *urlTemp = [[url stringByDeletingPathExtension] stringByAppendingString:@"."];
  242. NSUInteger count = arraySubtitleFileExtensions.count;
  243. for (int i = 0; i < count; i++) {
  244. NSString *checkAddress = [urlTemp stringByAppendingString:arraySubtitleFileExtensions[i]];
  245. NSURL *checkURL = [NSURL URLWithString:checkAddress];
  246. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:checkURL];
  247. request.HTTPMethod = @"HEAD";
  248. NSURLResponse *response = nil;
  249. [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
  250. NSInteger httpStatus = [(NSHTTPURLResponse *)response statusCode];
  251. if (httpStatus == 200) {
  252. NSString *fileSubtitlePath = [self _getFileSubtitleFromServer:checkURL];
  253. return fileSubtitlePath;
  254. }
  255. }
  256. return nil;
  257. }
  258. - (NSString *)_getFileSubtitleFromServer:(NSURL *)url
  259. {
  260. NSString *fileSubtitlePath = nil;
  261. NSString *fileName = [[url path] lastPathComponent];
  262. NSData *receivedSub = [NSData dataWithContentsOfURL:url];
  263. if (receivedSub.length < [[UIDevice currentDevice] freeDiskspace].longLongValue) {
  264. NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
  265. NSString *directoryPath = [searchPaths objectAtIndex:0];
  266. fileSubtitlePath = [directoryPath stringByAppendingPathComponent:fileName];
  267. NSFileManager *fileManager = [NSFileManager defaultManager];
  268. if (![fileManager fileExistsAtPath:fileSubtitlePath]) {
  269. [fileManager createFileAtPath:fileSubtitlePath contents:nil attributes:nil];
  270. if (![fileManager fileExistsAtPath:fileSubtitlePath])
  271. APLog(@"file creation failed, no data was saved");
  272. }
  273. [receivedSub writeToFile:fileSubtitlePath atomically:YES];
  274. } else {
  275. VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"DISK_FULL", nil)
  276. message:[NSString stringWithFormat:NSLocalizedString(@"DISK_FULL_FORMAT", nil), fileName, [[UIDevice currentDevice] model]]
  277. delegate:self
  278. cancelButtonTitle:NSLocalizedString(@"BUTTON_OK", nil)
  279. otherButtonTitles:nil];
  280. [alert performSelectorOnMainThread:@selector(show) withObject:nil waitUntilDone:NO];
  281. }
  282. return fileSubtitlePath;
  283. }
  284. #pragma mark - text view delegate
  285. - (BOOL)textFieldShouldReturn:(UITextField *)textField
  286. {
  287. [self.urlField resignFirstResponder];
  288. return NO;
  289. }
  290. @end