VLCPlaylistViewController.m 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282
  1. /*****************************************************************************
  2. * VLCPlaylistViewController.m
  3. * VLC for iOS
  4. *****************************************************************************
  5. * Copyright (c) 2013-2014 VideoLAN. All rights reserved.
  6. * $Id$
  7. *
  8. * Authors: Felix Paul Kühne <fkuehne # videolan.org>
  9. * Gleb Pinigin <gpinigin # gmail.com>
  10. * Tamas Timar <ttimar.vlc # gmail.com>
  11. * Carola Nitz <nitz.carola # gmail.com>
  12. *
  13. * Refer to the COPYING file of the official project for license.
  14. *****************************************************************************/
  15. #import "VLCPlaylistViewController.h"
  16. #import "VLCMovieViewController.h"
  17. #import "VLCPlaylistTableViewCell.h"
  18. #import "VLCPlaylistCollectionViewCell.h"
  19. #import "UINavigationController+Theme.h"
  20. #import "NSString+SupportedMedia.h"
  21. #import "VLCBugreporter.h"
  22. #import "VLCAppDelegate.h"
  23. #import "UIBarButtonItem+Theme.h"
  24. #import "VLCFirstStepsViewController.h"
  25. #import "VLCFolderCollectionViewFlowLayout.h"
  26. #import "LXReorderableCollectionViewFlowLayout.h"
  27. #import "VLCAlertView.h"
  28. /* prefs keys */
  29. static NSString *kDisplayedFirstSteps = @"Did we display the first steps tutorial?";
  30. @implementation EmptyLibraryView
  31. - (IBAction)learnMore:(id)sender
  32. {
  33. UIViewController *firstStepsVC = [[VLCFirstStepsViewController alloc] initWithNibName:nil bundle:nil];
  34. UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:firstStepsVC];
  35. navCon.modalPresentationStyle = UIModalPresentationFormSheet;
  36. [navCon loadTheme];
  37. [self.window.rootViewController presentViewController:navCon animated:YES completion:nil];
  38. }
  39. @end
  40. @interface VLCPlaylistViewController () <VLCFolderCollectionViewDelegateFlowLayout, LXReorderableCollectionViewDataSource, LXReorderableCollectionViewDelegateFlowLayout, UITableViewDataSource, UITableViewDelegate, MLMediaLibrary, VLCMediaListDelegate, UISearchBarDelegate, UISearchDisplayDelegate> {
  41. NSMutableArray *_foundMedia;
  42. VLCLibraryMode _libraryMode;
  43. VLCLibraryMode _previousLibraryMode;
  44. UIBarButtonItem *_menuButton;
  45. NSMutableArray *_indexPaths;
  46. id _folderObject;
  47. VLCFolderCollectionViewFlowLayout *_folderLayout;
  48. LXReorderableCollectionViewFlowLayout *_reorderLayout;
  49. BOOL inFolder;
  50. UILongPressGestureRecognizer *_longPressGestureRecognizer;
  51. NSInteger _mediaToPlayIndex;
  52. VLCMediaList *_list;
  53. NSArray *_tracks;
  54. NSMutableArray *_searchData;
  55. UISearchBar *_searchBar;
  56. UISearchDisplayController *_searchDisplayController;
  57. BOOL _usingTableViewToShowData;
  58. }
  59. @property (nonatomic, strong) UITableView *tableView;
  60. @property (nonatomic, strong) UICollectionView *collectionView;
  61. @property (nonatomic, strong) EmptyLibraryView *emptyLibraryView;
  62. @end
  63. @implementation VLCPlaylistViewController
  64. + (void)initialize
  65. {
  66. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  67. [defaults registerDefaults:@{kDisplayedFirstSteps : [NSNumber numberWithBool:NO]}];
  68. }
  69. - (void)loadView
  70. {
  71. _usingTableViewToShowData = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone;
  72. if (_usingTableViewToShowData) {
  73. _tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain];
  74. _tableView.backgroundColor = [UIColor VLCDarkBackgroundColor];
  75. _tableView.rowHeight = [VLCPlaylistTableViewCell heightOfCell];
  76. _tableView.separatorColor = [UIColor VLCDarkBackgroundColor];
  77. _tableView.delegate = self;
  78. _tableView.dataSource = self;
  79. _tableView.opaque = YES;
  80. self.view = _tableView;
  81. } else {
  82. _folderLayout = [[VLCFolderCollectionViewFlowLayout alloc] init];
  83. _collectionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:_folderLayout];
  84. _collectionView.alwaysBounceVertical = YES;
  85. _collectionView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
  86. _collectionView.delegate = self;
  87. _collectionView.dataSource = self;
  88. _collectionView.opaque = YES;
  89. _collectionView.backgroundColor = [UIColor VLCDarkBackgroundColor];
  90. self.view = _collectionView;
  91. _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_collectionViewHandleLongPressGesture:)];
  92. [_collectionView addGestureRecognizer:_longPressGestureRecognizer];
  93. if (SYSTEM_RUNS_IOS7_OR_LATER)
  94. [_collectionView registerNib:[UINib nibWithNibName:@"VLCFuturePlaylistCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"PlaylistCell"];
  95. else
  96. [_collectionView registerNib:[UINib nibWithNibName:@"VLCPlaylistCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"PlaylistCell"];
  97. self.view.backgroundColor = [UIColor VLCDarkBackgroundColor];
  98. }
  99. _libraryMode = VLCLibraryModeAllFiles;
  100. self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  101. self.emptyLibraryView = [[[NSBundle mainBundle] loadNibNamed:@"VLCEmptyLibraryView" owner:self options:nil] lastObject];
  102. _emptyLibraryView.emptyLibraryLongDescriptionLabel.lineBreakMode = NSLineBreakByWordWrapping;
  103. _emptyLibraryView.emptyLibraryLongDescriptionLabel.numberOfLines = 0;
  104. }
  105. #pragma mark -
  106. - (void)viewDidLoad
  107. {
  108. [super viewDidLoad];
  109. self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", @"");
  110. _menuButton = [UIBarButtonItem themedRevealMenuButtonWithTarget:self andSelector:@selector(leftButtonAction:)];
  111. self.navigationItem.leftBarButtonItem = _menuButton;
  112. if (SYSTEM_RUNS_IOS7_OR_LATER)
  113. self.editButtonItem.tintColor = [UIColor whiteColor];
  114. else {
  115. [self.editButtonItem setBackgroundImage:[UIImage imageNamed:@"button"]
  116. forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
  117. [self.editButtonItem setBackgroundImage:[UIImage imageNamed:@"buttonHighlight"]
  118. forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
  119. }
  120. _emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"EMPTY_LIBRARY", @"");
  121. _emptyLibraryView.emptyLibraryLongDescriptionLabel.text = NSLocalizedString(@"EMPTY_LIBRARY_LONG", @"");
  122. [_emptyLibraryView.emptyLibraryLongDescriptionLabel sizeToFit];
  123. [_emptyLibraryView.learnMoreButton setTitle:NSLocalizedString(@"BUTTON_LEARN_MORE", @"") forState:UIControlStateNormal];
  124. UIBarButtonItem *createFolderItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemOrganize target:self action:@selector(createFolder)];
  125. [self setToolbarItems:@[createFolderItem, [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil], [UIBarButtonItem themedDarkToolbarButtonWithTitle:NSLocalizedString(@"BUTTON_RENAME", @"") target:self andSelector:@selector(renameSelection)], [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil], [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(deleteSelection)]]];
  126. self.navigationController.toolbar.barStyle = UIBarStyleBlack;
  127. if (SYSTEM_RUNS_IOS7_OR_LATER) {
  128. self.navigationController.toolbar.tintColor = [UIColor whiteColor];
  129. [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
  130. } else
  131. [self.navigationController.toolbar setBackgroundImage:[UIImage imageNamed:@"bottomBlackBar"] forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
  132. if (_usingTableViewToShowData) {
  133. _searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
  134. UINavigationBar *navBar = self.navigationController.navigationBar;
  135. _searchBar.barTintColor = navBar.barTintColor;
  136. _searchBar.tintColor = navBar.tintColor;
  137. _searchBar.translucent = navBar.translucent;
  138. _searchBar.opaque = navBar.opaque;
  139. _searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:_searchBar contentsController:self];
  140. _searchDisplayController.delegate = self;
  141. _searchDisplayController.searchResultsDataSource = self;
  142. _searchDisplayController.searchResultsDelegate = self;
  143. _searchBar.delegate = self;
  144. UITapGestureRecognizer *tapTwiceGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTwiceGestureAction:)];
  145. [tapTwiceGesture setNumberOfTapsRequired:2];
  146. [self.navigationController.navigationBar addGestureRecognizer:tapTwiceGesture];
  147. }
  148. _searchData = [[NSMutableArray alloc] init];
  149. }
  150. - (void)viewWillAppear:(BOOL)animated
  151. {
  152. [super viewWillAppear:animated];
  153. [self.collectionView.collectionViewLayout invalidateLayout];
  154. [self _displayEmptyLibraryViewIfNeeded];
  155. }
  156. - (void)viewDidAppear:(BOOL)animated
  157. {
  158. [super viewDidAppear:animated];
  159. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  160. if (![[defaults objectForKey:kDisplayedFirstSteps] boolValue]) {
  161. [self.emptyLibraryView performSelector:@selector(learnMore:) withObject:nil afterDelay:1.];
  162. [defaults setObject:[NSNumber numberWithBool:YES] forKey:kDisplayedFirstSteps];
  163. [defaults synchronize];
  164. }
  165. if ([[MLMediaLibrary sharedMediaLibrary] libraryNeedsUpgrade]) {
  166. self.navigationItem.rightBarButtonItem = nil;
  167. self.navigationItem.leftBarButtonItem = nil;
  168. self.title = @"";
  169. self.emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"UPGRADING_LIBRARY", @"");
  170. self.emptyLibraryView.emptyLibraryLongDescriptionLabel.hidden = YES;
  171. [self.emptyLibraryView.activityIndicator startAnimating];
  172. self.emptyLibraryView.frame = self.view.bounds;
  173. [self.view addSubview:self.emptyLibraryView];
  174. [[MLMediaLibrary sharedMediaLibrary] setDelegate: self];
  175. [[MLMediaLibrary sharedMediaLibrary] performSelectorInBackground:@selector(upgradeLibrary) withObject:nil];
  176. return;
  177. }
  178. if (_foundMedia.count < 1)
  179. [self updateViewContents];
  180. [[MLMediaLibrary sharedMediaLibrary] performSelector:@selector(libraryDidAppear) withObject:nil afterDelay:1.];
  181. }
  182. - (void)viewDidDisappear:(BOOL)animated
  183. {
  184. [super viewDidDisappear:animated];
  185. [[MLMediaLibrary sharedMediaLibrary] libraryDidDisappear];
  186. }
  187. - (BOOL)canBecomeFirstResponder
  188. {
  189. return YES;
  190. }
  191. - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
  192. {
  193. if (motion == UIEventSubtypeMotionShake)
  194. [[VLCBugreporter sharedInstance] handleBugreportRequest];
  195. }
  196. - (void)openMediaObject:(NSManagedObject *)mediaObject
  197. {
  198. if ([mediaObject isKindOfClass:[MLAlbum class]]) {
  199. _foundMedia = [NSMutableArray arrayWithArray:[(MLAlbum *)mediaObject sortedTracks]];
  200. self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
  201. if (_libraryMode == VLCLibraryModeAllFiles)
  202. self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", @"");
  203. else
  204. [self.navigationItem.leftBarButtonItem setTitle:NSLocalizedString(@"LIBRARY_MUSIC", @"")];
  205. self.title = [(MLAlbum*)mediaObject name];
  206. [self reloadViews];
  207. } else if ([mediaObject isKindOfClass:[MLShow class]]) {
  208. _foundMedia = [NSMutableArray arrayWithArray:[(MLShow *)mediaObject sortedEpisodes]];
  209. self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
  210. if (_libraryMode == VLCLibraryModeAllFiles)
  211. self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", @"");
  212. else
  213. [self.navigationItem.leftBarButtonItem setTitle:NSLocalizedString(@"LIBRARY_SERIES", @"")];
  214. self.title = [(MLShow*)mediaObject name];
  215. [self reloadViews];
  216. } else if ([mediaObject isKindOfClass:[MLLabel class]]) {
  217. MLLabel *folder = (MLLabel*) mediaObject;
  218. inFolder = YES;
  219. if (!_usingTableViewToShowData) {
  220. if (![self.collectionView.collectionViewLayout isEqual:_reorderLayout]) {
  221. for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers) {
  222. if (recognizer == _folderLayout.panGestureRecognizer || recognizer == _folderLayout.longPressGestureRecognizer || recognizer == _longPressGestureRecognizer)
  223. [self.collectionView removeGestureRecognizer:recognizer];
  224. }
  225. _reorderLayout = [[LXReorderableCollectionViewFlowLayout alloc] init];
  226. [self.collectionView setCollectionViewLayout:_reorderLayout animated:NO];
  227. }
  228. }
  229. _foundMedia = [NSMutableArray arrayWithArray:[folder sortedFolderItems]];
  230. self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
  231. self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", @"");
  232. self.title = [folder name];
  233. UIBarButtonItem *removeFromFolder = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemReply target:self action:@selector(removeFromFolder)];
  234. NSMutableArray *toolbarItems = [self.toolbarItems mutableCopy];
  235. toolbarItems[0] = removeFromFolder;
  236. self.toolbarItems = toolbarItems;
  237. [self reloadViews];
  238. return;
  239. } else
  240. [(VLCAppDelegate*)[UIApplication sharedApplication].delegate openMediaFromManagedObject:mediaObject];
  241. }
  242. - (void)removeMediaObject:(id)managedObject updateDatabase:(BOOL)updateDB
  243. {
  244. // delete all tracks from an album
  245. if ([managedObject isKindOfClass:[MLAlbum class]]) {
  246. MLAlbum *album = managedObject;
  247. NSSet *iterAlbumTrack = [NSSet setWithSet:album.tracks];
  248. for (MLAlbumTrack *track in iterAlbumTrack) {
  249. NSSet *iterFiles = [NSSet setWithSet:track.files];
  250. for (MLFile *file in iterFiles)
  251. [self _deleteMediaObject:file];
  252. }
  253. // delete all episodes from a show
  254. } else if ([managedObject isKindOfClass:[MLShow class]]) {
  255. MLShow *show = managedObject;
  256. NSSet *iterShowEpisodes = [NSSet setWithSet:show.episodes];
  257. for (MLShowEpisode *episode in iterShowEpisodes) {
  258. NSSet *iterFiles = [NSSet setWithSet:episode.files];
  259. for (MLFile *file in iterFiles)
  260. [self _deleteMediaObject:file];
  261. }
  262. // delete all files from an episode
  263. } else if ([managedObject isKindOfClass:[MLShowEpisode class]]) {
  264. MLShowEpisode *episode = managedObject;
  265. NSSet *iterFiles = [NSSet setWithSet:episode.files];
  266. for (MLFile *file in iterFiles)
  267. [self _deleteMediaObject:file];
  268. // delete all files from a track
  269. } else if ([managedObject isKindOfClass:[MLAlbumTrack class]]) {
  270. MLAlbumTrack *track = managedObject;
  271. NSSet *iterFiles = [NSSet setWithSet:track.files];
  272. for (MLFile *file in iterFiles)
  273. [self _deleteMediaObject:file];
  274. } else if ([managedObject isKindOfClass:[MLLabel class]]) {
  275. MLLabel *folder = managedObject;
  276. NSSet *iterFiles = [NSSet setWithSet:folder.files];
  277. [folder removeFiles:folder.files];
  278. for (MLFile *file in iterFiles)
  279. [self _deleteMediaObject:file];
  280. [[MLMediaLibrary sharedMediaLibrary] removeObject:folder];
  281. }
  282. else
  283. [self _deleteMediaObject:managedObject];
  284. if (updateDB) {
  285. [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
  286. [self updateViewContents];
  287. }
  288. }
  289. - (void)_deleteMediaObject:(MLFile *)mediaObject
  290. {
  291. if (inFolder)
  292. [self rearrangeFolderTrackNumbersForRemovedItem:mediaObject];
  293. NSFileManager *fileManager = [NSFileManager defaultManager];
  294. NSString *folderLocation = [[[NSURL URLWithString:mediaObject.url] path] stringByDeletingLastPathComponent];
  295. NSArray *allfiles = [fileManager contentsOfDirectoryAtPath:folderLocation error:nil];
  296. NSString *fileName = [[[[NSURL URLWithString:mediaObject.url] path] lastPathComponent] stringByDeletingPathExtension];
  297. NSIndexSet *indexSet = [allfiles indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
  298. return ([obj rangeOfString:fileName].location != NSNotFound);
  299. }];
  300. NSUInteger count = indexSet.count;
  301. NSString *additionalFilePath;
  302. NSUInteger currentIndex = [indexSet firstIndex];
  303. for (unsigned int x = 0; x < count; x++) {
  304. additionalFilePath = allfiles[currentIndex];
  305. if ([additionalFilePath isSupportedSubtitleFormat])
  306. [fileManager removeItemAtPath:[folderLocation stringByAppendingPathComponent:additionalFilePath] error:nil];
  307. currentIndex = [indexSet indexGreaterThanIndex:currentIndex];
  308. }
  309. [fileManager removeItemAtPath:[[NSURL URLWithString:mediaObject.url] path] error:nil];
  310. }
  311. - (void)_displayEmptyLibraryViewIfNeeded
  312. {
  313. if (self.emptyLibraryView.superview)
  314. [self.emptyLibraryView removeFromSuperview];
  315. if (_foundMedia.count == 0) {
  316. self.emptyLibraryView.emptyLibraryLabel.text = inFolder ? NSLocalizedString(@"FOLDER_EMPTY", @"") : NSLocalizedString(@"EMPTY_LIBRARY", @"");
  317. self.emptyLibraryView.emptyLibraryLongDescriptionLabel.text = inFolder ? NSLocalizedString(@"FOLDER_EMPTY_LONG", @"") : NSLocalizedString(@"EMPTY_LIBRARY_LONG", @"");
  318. self.emptyLibraryView.learnMoreButton.hidden = inFolder;
  319. self.emptyLibraryView.frame = self.view.bounds;
  320. [self.view addSubview:self.emptyLibraryView];
  321. self.navigationItem.rightBarButtonItem = nil;
  322. } else
  323. self.navigationItem.rightBarButtonItem = self.editButtonItem;
  324. if (_usingTableViewToShowData)
  325. _tableView.separatorStyle = (_foundMedia.count > 0)? UITableViewCellSeparatorStyleSingleLine:
  326. UITableViewCellSeparatorStyleNone;
  327. else
  328. [self.collectionView.collectionViewLayout invalidateLayout];
  329. }
  330. - (void)libraryUpgradeComplete
  331. {
  332. self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", @"");
  333. self.navigationItem.leftBarButtonItem = _menuButton;
  334. self.emptyLibraryView.emptyLibraryLongDescriptionLabel.hidden = NO;
  335. self.emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"EMPTY_LIBRARY", @"");
  336. [self.emptyLibraryView.activityIndicator stopAnimating];
  337. [self.emptyLibraryView removeFromSuperview];
  338. [self updateViewContents];
  339. }
  340. - (void)libraryWasUpdated
  341. {
  342. [self updateViewContents];
  343. }
  344. - (void)updateViewContents
  345. {
  346. _foundMedia = [[NSMutableArray alloc] init];
  347. self.navigationItem.leftBarButtonItem = _menuButton;
  348. if (_libraryMode == VLCLibraryModeAllAlbums)
  349. self.title = NSLocalizedString(@"LIBRARY_MUSIC", @"");
  350. else if( _libraryMode == VLCLibraryModeAllSeries)
  351. self.title = NSLocalizedString(@"LIBRARY_SERIES", @"");
  352. else
  353. self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", @"");
  354. /* add all albums */
  355. if (_libraryMode != VLCLibraryModeAllSeries) {
  356. NSArray *rawAlbums = [MLAlbum allAlbums];
  357. for (MLAlbum *album in rawAlbums) {
  358. if (album.name.length > 0 && album.tracks.count > 1)
  359. [_foundMedia addObject:album];
  360. }
  361. }
  362. if (_libraryMode == VLCLibraryModeAllAlbums) {
  363. [self reloadViews];
  364. return;
  365. }
  366. /* add all shows */
  367. NSArray *rawShows = [MLShow allShows];
  368. for (MLShow *show in rawShows) {
  369. if (show.name.length > 0 && show.episodes.count > 1)
  370. [_foundMedia addObject:show];
  371. }
  372. if (_libraryMode == VLCLibraryModeAllSeries) {
  373. [self reloadViews];
  374. return;
  375. }
  376. /* add all folders*/
  377. NSArray *allFolders = [MLLabel allLabels];
  378. for (MLLabel *folder in allFolders)
  379. [_foundMedia addObject:folder];
  380. /* add all remaining files */
  381. NSArray *allFiles = [MLFile allFiles];
  382. for (MLFile *file in allFiles) {
  383. if (file.labels.count > 0) continue;
  384. if (!file.isShowEpisode && !file.isAlbumTrack)
  385. [_foundMedia addObject:file];
  386. else if (file.isShowEpisode) {
  387. if (file.showEpisode.show.episodes.count < 2)
  388. [_foundMedia addObject:file];
  389. /* older MediaLibraryKit versions don't send a show name in a popular
  390. * corner case. hence, we need to work-around here and force a reload
  391. * afterwards as this could lead to the 'all my shows are gone'
  392. * syndrome (see #10435, #10464, #10432 et al) */
  393. if (file.showEpisode.show.name.length == 0) {
  394. file.showEpisode.show.name = NSLocalizedString(@"UNTITLED_SHOW", @"");
  395. [self performSelector:@selector(updateViewContents) withObject:nil afterDelay:0.1];
  396. }
  397. } else if (file.isAlbumTrack) {
  398. if (file.albumTrack.album.tracks.count < 2)
  399. [_foundMedia addObject:file];
  400. }
  401. }
  402. [self reloadViews];
  403. }
  404. - (void)reloadViews
  405. {
  406. if (_usingTableViewToShowData)
  407. [self.tableView reloadData];
  408. else
  409. [self.collectionView reloadData];
  410. [self _displayEmptyLibraryViewIfNeeded];
  411. }
  412. #pragma mark - Table View
  413. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  414. {
  415. return 1;
  416. }
  417. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  418. {
  419. if (tableView == self.searchDisplayController.searchResultsTableView)
  420. return _searchData.count;
  421. return _foundMedia.count;
  422. }
  423. // Customize the appearance of table view cells.
  424. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  425. {
  426. static NSString *CellIdentifier = @"PlaylistCell";
  427. VLCPlaylistTableViewCell *cell = (VLCPlaylistTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  428. if (cell == nil)
  429. cell = [VLCPlaylistTableViewCell cellWithReuseIdentifier:CellIdentifier];
  430. UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRightGestureAction:)];
  431. [swipeRight setDirection:(UISwipeGestureRecognizerDirectionRight)];
  432. [cell addGestureRecognizer:swipeRight];
  433. NSInteger row = indexPath.row;
  434. if (tableView == self.searchDisplayController.searchResultsTableView)
  435. cell.mediaObject = _searchData[row];
  436. else
  437. cell.mediaObject = _foundMedia[row];
  438. return cell;
  439. }
  440. - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
  441. {
  442. MLFile* object = [_foundMedia objectAtIndex:fromIndexPath.item];
  443. [_foundMedia removeObjectAtIndex:fromIndexPath.item];
  444. [_foundMedia insertObject:object atIndex:toIndexPath.item];
  445. object.folderTrackNumber = @(toIndexPath.item - 1);
  446. object = [_foundMedia objectAtIndex:fromIndexPath.item];
  447. object.folderTrackNumber = @(fromIndexPath.item - 1);
  448. }
  449. - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
  450. {
  451. return inFolder;
  452. }
  453. - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
  454. {
  455. cell.backgroundColor = (indexPath.row % 2 == 0)? [UIColor blackColor]: [UIColor VLCDarkBackgroundColor];
  456. cell.multipleSelectionBackgroundView.backgroundColor = cell.backgroundColor;
  457. }
  458. - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
  459. {
  460. return YES;
  461. }
  462. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
  463. {
  464. if (editingStyle == UITableViewCellEditingStyleDelete)
  465. [self removeMediaObject: _foundMedia[indexPath.row] updateDatabase:YES];
  466. }
  467. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  468. {
  469. if (tableView.isEditing) {
  470. if (_libraryMode == VLCLibraryModeCreateFolder) {
  471. _folderObject = _foundMedia[indexPath.row];
  472. _libraryMode = _previousLibraryMode;
  473. [self updateViewContents];
  474. [self createFolderWithName:nil];
  475. }
  476. return;
  477. }
  478. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  479. NSManagedObject *selectedObject;
  480. if (tableView == self.searchDisplayController.searchResultsTableView)
  481. selectedObject = _searchData[indexPath.row];
  482. else
  483. selectedObject = _foundMedia[indexPath.row];
  484. if ([selectedObject isKindOfClass:[MLAlbumTrack class]]) {
  485. _tracks = [[(MLAlbumTrack*)selectedObject album] sortedTracks];
  486. NSUInteger count = _tracks.count;
  487. _list = [[VLCMediaList alloc] init];
  488. _list.delegate = self;
  489. MLFile *file;
  490. VLCMedia *media;
  491. for (NSInteger x = count - 1; x > -1; x--) {
  492. file = [(MLAlbumTrack*)_tracks[x] files].anyObject;
  493. media = [VLCMedia mediaWithURL: [NSURL URLWithString:file.url]];
  494. [media parse];
  495. [_list addMedia:media];
  496. }
  497. _mediaToPlayIndex = indexPath.row;
  498. } else if ([selectedObject isKindOfClass:[MLFile class]] && [((MLFile *)selectedObject).labels count] > 0) {
  499. MLLabel *folder = [((MLFile *)selectedObject).labels anyObject];
  500. _tracks = [folder sortedFolderItems];
  501. NSUInteger count = _tracks.count;
  502. _list = [[VLCMediaList alloc] init];
  503. _list.delegate = self;
  504. MLFile *file;
  505. for (NSInteger x = count - 1; x > -1; x--) {
  506. file = (MLFile *)_tracks[x];
  507. [_list addMedia:[VLCMedia mediaWithURL:[NSURL URLWithString:file.url]]];
  508. }
  509. _mediaToPlayIndex = indexPath.row;
  510. }
  511. if (_searchDisplayController.active == YES)
  512. [_searchDisplayController setActive:NO animated:YES];
  513. [self openMediaObject:selectedObject];
  514. }
  515. #pragma mark - VLCMedialistDelegate
  516. - (void)mediaList:(VLCMediaList *)aMediaList mediaAdded:(VLCMedia *)media atIndex:(NSInteger)index
  517. {
  518. if (index == _tracks.count - 1) {
  519. NSManagedObject *selectedObject = _foundMedia[_mediaToPlayIndex];
  520. [(VLCAppDelegate *)[UIApplication sharedApplication].delegate openMediaList:_list atIndex:(int)[_tracks indexOfObject:selectedObject]];
  521. }
  522. }
  523. #pragma mark - Gesture Action
  524. - (void)swipeRightGestureAction:(UIGestureRecognizer *)recognizer
  525. {
  526. if ([[self.editButtonItem title] isEqualToString:NSLocalizedString(@"BUTTON_CANCEL",@"")])
  527. [self setEditing:NO animated:YES];
  528. else {
  529. [self setEditing:YES animated:YES];
  530. NSIndexPath *path = [(UITableView *)self.view indexPathForRowAtPoint:[recognizer locationInView:self.view]];
  531. [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:path.row inSection:path.section]
  532. animated:YES
  533. scrollPosition:UITableViewScrollPositionNone];
  534. }
  535. }
  536. - (void)tapTwiceGestureAction:(UIGestureRecognizer *)recognizer
  537. {
  538. _searchBar.hidden = !_searchBar.hidden;
  539. if (_searchBar.hidden)
  540. self.tableView.tableHeaderView = nil;
  541. else
  542. self.tableView.tableHeaderView = _searchBar;
  543. }
  544. #pragma mark - Collection View
  545. - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
  546. {
  547. return _foundMedia.count;
  548. }
  549. - (UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
  550. {
  551. VLCPlaylistCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"PlaylistCell" forIndexPath:indexPath];
  552. cell.mediaObject = _foundMedia[indexPath.row];
  553. cell.collectionView = _collectionView;
  554. [cell setEditing:self.editing animated:NO];
  555. return cell;
  556. }
  557. - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
  558. {
  559. if (SYSTEM_RUNS_IOS7_OR_LATER) {
  560. if (UIDeviceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation))
  561. return CGSizeMake(341., 190.);
  562. else
  563. return CGSizeMake(384., 216.);
  564. }
  565. return CGSizeMake(298.0, 220.0);
  566. }
  567. - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
  568. {
  569. if (SYSTEM_RUNS_IOS7_OR_LATER)
  570. return UIEdgeInsetsZero;
  571. return UIEdgeInsetsMake(0.0, 34.0, 0.0, 34.0);
  572. }
  573. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
  574. {
  575. if (SYSTEM_RUNS_IOS7_OR_LATER)
  576. return 0.;
  577. return 10.0;
  578. }
  579. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
  580. {
  581. if (SYSTEM_RUNS_IOS7_OR_LATER)
  582. return 0.;
  583. return 10.0;
  584. }
  585. - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
  586. {
  587. if (self.editing) {
  588. if (_libraryMode == VLCLibraryModeCreateFolder) {
  589. _folderObject = _foundMedia[indexPath.item];
  590. [self createFolderWithName:nil];
  591. _libraryMode = _previousLibraryMode;
  592. }
  593. [(VLCPlaylistCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath] selectionUpdate];
  594. return;
  595. }
  596. NSManagedObject *selectedObject = _foundMedia[indexPath.row];
  597. if ([selectedObject isKindOfClass:[MLAlbumTrack class]]) {
  598. VLCMediaList *list;
  599. NSArray *tracks = [[(MLAlbumTrack*)selectedObject album] sortedTracks];
  600. NSUInteger count = tracks.count;
  601. list = [[VLCMediaList alloc] init];
  602. MLFile *file;
  603. for (NSInteger x = count - 1; x > -1; x--) {
  604. file = [(MLAlbumTrack*)tracks[x] files].anyObject;
  605. [list addMedia:[VLCMedia mediaWithURL: [NSURL URLWithString:file.url]]];
  606. }
  607. [(VLCAppDelegate*)[UIApplication sharedApplication].delegate openMediaList:list atIndex:(int)[tracks indexOfObject:selectedObject]];
  608. } else if ([selectedObject isKindOfClass:[MLFile class]] && [((MLFile *)selectedObject).labels count] > 0) {
  609. VLCMediaList *list;
  610. MLLabel *folder = [((MLFile *)selectedObject).labels anyObject];
  611. NSArray *folderTracks = [folder sortedFolderItems];
  612. NSUInteger count = folderTracks.count;
  613. list = [[VLCMediaList alloc] init];
  614. MLFile *file;
  615. for (NSInteger x = count - 1; x > -1; x--) {
  616. file = (MLFile *)folderTracks[x];
  617. [list addMedia:[VLCMedia mediaWithURL:[NSURL URLWithString:file.url]]];
  618. }
  619. [(VLCAppDelegate *)[UIApplication sharedApplication].delegate openMediaList:list atIndex:(int)[folderTracks indexOfObject:selectedObject]];
  620. } else
  621. [self openMediaObject:selectedObject];
  622. }
  623. - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
  624. {
  625. [(VLCPlaylistCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath] selectionUpdate];
  626. }
  627. - (void)collectionView:(UICollectionView *)collectionView removeItemFromFolderAtIndexPathIfNeeded:(NSIndexPath *)indexPath
  628. {
  629. MLFile *mediaObject = (MLFile *)_foundMedia[indexPath.item];
  630. [self rearrangeFolderTrackNumbersForRemovedItem:mediaObject];
  631. mediaObject.labels = nil;
  632. mediaObject.folderTrackNumber = nil;
  633. [self backToAllItems:nil];
  634. }
  635. - (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath willMoveToIndexPath:(NSIndexPath *)toIndexPath
  636. {
  637. MLFile* object = [_foundMedia objectAtIndex:fromIndexPath.item];
  638. [_foundMedia removeObjectAtIndex:fromIndexPath.item];
  639. [_foundMedia insertObject:object atIndex:toIndexPath.item];
  640. object.folderTrackNumber = @(toIndexPath.item - 1);
  641. object = [_foundMedia objectAtIndex:fromIndexPath.item];
  642. object.folderTrackNumber = @(fromIndexPath.item - 1);
  643. }
  644. - (void)collectionView:(UICollectionView *)collectionView requestToMoveItemAtIndexPath:(NSIndexPath *)itemPath intoFolderAtIndexPath:(NSIndexPath *)folderPath
  645. {
  646. BOOL validFileTypeAtFolderPath = ([_foundMedia[folderPath.item] isKindOfClass:[MLFile class]] || [_foundMedia[folderPath.item] isKindOfClass:[MLLabel class]]) && [_foundMedia[itemPath.item] isKindOfClass:[MLFile class]];
  647. if (!validFileTypeAtFolderPath) {
  648. VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FOLDER_INVALID_TYPE_TITLE", @"") message:NSLocalizedString(@"FOLDER_INVALID_TYPE_MESSAGE", @"") cancelButtonTitle:nil otherButtonTitles:@[NSLocalizedString(@"BUTTON_OK", @"")]];
  649. alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
  650. [self updateViewContents];
  651. };
  652. [alert show];
  653. return;
  654. }
  655. BOOL isFolder = [_foundMedia[folderPath.item] isKindOfClass:[MLLabel class]];
  656. if (isFolder){
  657. MLLabel *folder = _foundMedia[folderPath.item];
  658. MLFile *file = _foundMedia[itemPath.item];
  659. [file setLabels:[[NSSet alloc] initWithObjects:folder, nil]];
  660. file.folderTrackNumber = @([folder.files count] - 1);
  661. [_foundMedia removeObjectAtIndex:itemPath.item];
  662. [self updateViewContents];
  663. } else {
  664. _folderObject = _foundMedia[folderPath.item];
  665. _indexPaths = [NSMutableArray arrayWithArray:@[itemPath]];
  666. [self showCreateFolderAlert];
  667. }
  668. }
  669. #pragma mark - Folder implementation
  670. - (void)rearrangeFolderTrackNumbersForRemovedItem:(MLFile *) mediaObject
  671. {
  672. MLLabel *label = [mediaObject.labels anyObject];
  673. NSSet *allFiles = label.files;
  674. for (MLFile *file in allFiles) {
  675. if (file.folderTrackNumber > mediaObject.folderTrackNumber) {
  676. int value = [file.folderTrackNumber intValue];
  677. file.folderTrackNumber = [NSNumber numberWithInt:value - 1];
  678. }
  679. }
  680. }
  681. - (void)showCreateFolderAlert
  682. {
  683. VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FOLDER_CHOOSE_NAME_TITLE", @"") message:NSLocalizedString(@"FOLDER_CHOOSE_NAME_MESSAGE", @"") cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", @"") otherButtonTitles:@[NSLocalizedString(@"BUTTON_SAVE", @"")]];
  684. [alert setAlertViewStyle:UIAlertViewStylePlainTextInput];
  685. [[alert textFieldAtIndex:0] setText:NSLocalizedString(@"FOLDER_NAME_PLACEHOLDER", @"")];
  686. [[alert textFieldAtIndex:0] setClearButtonMode:UITextFieldViewModeAlways];
  687. __weak VLCAlertView *weakAlert = alert;
  688. alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
  689. if (cancelled)
  690. [self updateViewContents];
  691. else
  692. [self createFolderWithName:[weakAlert textFieldAtIndex:0].text];
  693. };
  694. [alert show];
  695. }
  696. - (void)createFolder
  697. {
  698. if (_libraryMode == VLCLibraryModeCreateFolder) {
  699. _libraryMode = _previousLibraryMode;
  700. [self updateViewContents];
  701. [self showCreateFolderAlert];
  702. return;
  703. }
  704. if (!_usingTableViewToShowData)
  705. _indexPaths = [NSMutableArray arrayWithArray:[self.collectionView indexPathsForSelectedItems]];
  706. else
  707. _indexPaths = [NSMutableArray arrayWithArray:[self.tableView indexPathsForSelectedRows]];
  708. for (NSInteger i = _indexPaths.count - 1; i >=0; i--) {
  709. NSIndexPath *path = _indexPaths[i];
  710. id mediaObject;
  711. if (!_usingTableViewToShowData)
  712. mediaObject = _foundMedia[path.item];
  713. else
  714. mediaObject = _foundMedia[path.row];
  715. if ([mediaObject isKindOfClass:[MLLabel class]])
  716. [_indexPaths removeObject:path];
  717. }
  718. if ([_indexPaths count] != 0) {
  719. NSArray *folder = [MLLabel allLabels];
  720. //if we already have folders display them
  721. if ([folder count] > 0) {
  722. _foundMedia = [NSMutableArray arrayWithArray:folder];
  723. self.title = NSLocalizedString(@"SELECT_FOLDER", @"");
  724. _previousLibraryMode = _libraryMode;
  725. _libraryMode = VLCLibraryModeCreateFolder;
  726. [self reloadViews];
  727. return;
  728. }
  729. }
  730. //no selected items or no existing folder ask for foldername
  731. [self showCreateFolderAlert];
  732. }
  733. - (void)removeFromFolder
  734. {
  735. if (!_usingTableViewToShowData)
  736. _indexPaths = [NSMutableArray arrayWithArray:[self.collectionView indexPathsForSelectedItems]];
  737. else
  738. _indexPaths = [NSMutableArray arrayWithArray:[self.tableView indexPathsForSelectedRows]];
  739. [_indexPaths sortUsingSelector:@selector(compare:)];
  740. for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
  741. NSIndexPath *path = _indexPaths[i];
  742. MLFile *file = (MLFile *)_foundMedia[_usingTableViewToShowData ? path.row : path.item];
  743. MLLabel *folder = [file.labels anyObject];
  744. [self rearrangeFolderTrackNumbersForRemovedItem:file];
  745. file.labels = nil;
  746. file.folderTrackNumber = nil;
  747. [_foundMedia removeObject:file];
  748. if ([folder.files count] == 0) {
  749. [self removeMediaObject:folder updateDatabase:YES];
  750. [self setEditing:NO];
  751. [self backToAllItems:nil];
  752. }
  753. }
  754. [self reloadViews];
  755. }
  756. - (void)createFolderWithName:(NSString *)folderName
  757. {
  758. NSArray *labels = [MLLabel allLabels];
  759. for (MLLabel *label in labels){
  760. if ([label.name isEqualToString:folderName]) {
  761. _folderObject = nil;
  762. _indexPaths = nil;
  763. VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FOLDER_NAME_DUPLICATE_TITLE", @"") message:NSLocalizedString(@"FOLDER_NAME_DUPLICATE_MESSAGE", @"") cancelButtonTitle:nil otherButtonTitles:@[NSLocalizedString(@"BUTTON_OK", @"")]];
  764. alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
  765. [self updateViewContents];
  766. };
  767. [alert show];
  768. return;
  769. }
  770. }
  771. if (_folderObject != nil) {
  772. NSUInteger folderIndex = [_foundMedia indexOfObject:_folderObject];
  773. //item got dragged onto item
  774. if ([_foundMedia[folderIndex] isKindOfClass:[MLFile class]]) {
  775. MLFile *file = _foundMedia[folderIndex];
  776. MLLabel *label = [[MLMediaLibrary sharedMediaLibrary] createObjectForEntity:@"Label"];
  777. label.name = folderName;
  778. file.labels = [NSSet setWithObjects:label,nil];
  779. NSNumber *folderTrackNumber = [NSNumber numberWithInt:(int)[label files].count - 1];
  780. file.folderTrackNumber = folderTrackNumber;
  781. [_foundMedia removeObjectAtIndex:folderIndex];
  782. [_foundMedia insertObject:label atIndex:folderIndex];
  783. MLFile *itemFile = _foundMedia[((NSIndexPath *)_indexPaths[0]).item];
  784. itemFile.labels = file.labels;
  785. [_foundMedia removeObjectAtIndex:((NSIndexPath *)_indexPaths[0]).item];
  786. itemFile.folderTrackNumber = @([label files].count - 1);
  787. } else {
  788. //item got dragged onto folder or items should be added to folder
  789. MLLabel *label = _foundMedia[folderIndex];
  790. [_indexPaths sortUsingSelector:@selector(compare:)];
  791. for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
  792. NSIndexPath *path = _indexPaths[i];
  793. if (_libraryMode != VLCLibraryModeCreateFolder && ![_foundMedia[path.row] isKindOfClass:[MLFile class]])
  794. continue;
  795. if (_libraryMode == VLCLibraryModeCreateFolder)
  796. [self updateViewContents];
  797. MLFile *file = _foundMedia[path.row];
  798. file.labels = [NSSet setWithObjects:label, nil];
  799. [_foundMedia removeObjectAtIndex:path.row];
  800. file.folderTrackNumber = @([label files].count - 1);
  801. }
  802. }
  803. _folderObject = nil;
  804. } else {
  805. //create new folder
  806. MLLabel *label = [[MLMediaLibrary sharedMediaLibrary] createObjectForEntity:@"Label"];
  807. label.name = folderName;
  808. //if items were selected
  809. if ([_indexPaths count] != 0) {
  810. [_indexPaths sortUsingSelector:@selector(compare:)];
  811. for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
  812. NSIndexPath *path = _indexPaths[i];
  813. if (!_usingTableViewToShowData) {
  814. MLFile *file = _foundMedia[path.item];
  815. file.labels = [NSSet setWithObjects:label, nil];
  816. file.folderTrackNumber = @([label files].count - 1);
  817. [_foundMedia removeObjectAtIndex:path.item];
  818. } else {
  819. MLFile *file = _foundMedia[path.row];
  820. file.labels = [NSSet setWithObjects:label, nil];
  821. file.folderTrackNumber = @([label files].count - 1);
  822. [_foundMedia removeObjectAtIndex:path.row];
  823. }
  824. }
  825. }
  826. }
  827. _indexPaths = nil;
  828. [self setEditing:NO];
  829. [self updateViewContents];
  830. }
  831. - (void)_collectionViewHandleLongPressGesture:(UIGestureRecognizer *) sender
  832. {
  833. [self setEditing:YES animated:YES];
  834. }
  835. #pragma mark - UI implementation
  836. - (void)setEditing:(BOOL)editing animated:(BOOL)animated
  837. {
  838. [super setEditing:editing animated:animated];
  839. UIBarButtonItem *editButton = self.editButtonItem;
  840. NSString *editImage = editing? @"doneButton": @"button";
  841. NSString *editImageHighlight = editing? @"doneButtonHighlight": @"buttonHighlight";
  842. if (SYSTEM_RUNS_IOS7_OR_LATER)
  843. editButton.tintColor = [UIColor whiteColor];
  844. else {
  845. [editButton setBackgroundImage:[UIImage imageNamed:editImage] forState:UIControlStateNormal
  846. barMetrics:UIBarMetricsDefault];
  847. [editButton setBackgroundImage:[UIImage imageNamed:editImageHighlight]
  848. forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
  849. [editButton setTitleTextAttributes: editing ? @{UITextAttributeTextShadowColor : [UIColor whiteColor], UITextAttributeTextColor : [UIColor blackColor]} : @{UITextAttributeTextShadowColor : [UIColor VLCDarkTextShadowColor], UITextAttributeTextColor : [UIColor whiteColor]} forState:UIControlStateNormal];
  850. }
  851. if (!_usingTableViewToShowData) {
  852. NSArray *visibleCells = self.collectionView.visibleCells;
  853. [visibleCells enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  854. VLCPlaylistCollectionViewCell *aCell = (VLCPlaylistCollectionViewCell*)obj;
  855. [aCell setEditing:editing animated:animated];
  856. }];
  857. self.collectionView.allowsMultipleSelection = editing;
  858. /* UIKit doesn't clear the selection automagically if we leave the editing mode
  859. * so we need to do so manually */
  860. if (!editing) {
  861. [self.collectionView addGestureRecognizer:_longPressGestureRecognizer];
  862. NSArray *selectedItems = [self.collectionView indexPathsForSelectedItems];
  863. NSUInteger count = selectedItems.count;
  864. for (NSUInteger x = 0; x < count; x++)
  865. [self.collectionView deselectItemAtIndexPath:selectedItems[x] animated:NO];
  866. } else
  867. [self.collectionView removeGestureRecognizer:_longPressGestureRecognizer];
  868. } else {
  869. self.tableView.allowsMultipleSelectionDuringEditing = editing;
  870. [self.tableView setEditing:editing animated:YES];
  871. [self.editButtonItem setTitle:editing ? NSLocalizedString(@"BUTTON_CANCEL",@"") : NSLocalizedString(@"BUTTON_EDIT", @"")];
  872. }
  873. if (_libraryMode == VLCLibraryModeCreateFolder) {
  874. _libraryMode = _previousLibraryMode;
  875. _indexPaths = nil;
  876. [self updateViewContents];
  877. }
  878. self.navigationController.toolbarHidden = !editing;
  879. }
  880. - (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
  881. {
  882. return UITableViewCellEditingStyleDelete;
  883. }
  884. - (IBAction)leftButtonAction:(id)sender
  885. {
  886. [[(VLCAppDelegate*)[UIApplication sharedApplication].delegate revealController] toggleSidebar:![(VLCAppDelegate*)[UIApplication sharedApplication].delegate revealController].sidebarShowing duration:kGHRevealSidebarDefaultAnimationDuration];
  887. if (self.isEditing)
  888. [self setEditing:NO animated:YES];
  889. }
  890. - (IBAction)backToAllItems:(id)sender
  891. {
  892. if (!_usingTableViewToShowData) {
  893. if (![self.collectionView.collectionViewLayout isEqual:_folderLayout]) {
  894. //for some reason the Gesturerecognizer block themselves if not removed manually
  895. for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers) {
  896. if (recognizer == _reorderLayout.panGestureRecognizer || recognizer == _reorderLayout.longPressGestureRecognizer)
  897. [self.collectionView removeGestureRecognizer:recognizer];
  898. }
  899. _folderLayout = [[VLCFolderCollectionViewFlowLayout alloc] init];
  900. [self.collectionView setCollectionViewLayout:_folderLayout animated:NO];
  901. [_collectionView addGestureRecognizer:_longPressGestureRecognizer];
  902. }
  903. }
  904. inFolder = NO;
  905. UIBarButtonItem *createFolderItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemOrganize target:self action:@selector(createFolder)];
  906. NSMutableArray *toolbarItems = [self.toolbarItems mutableCopy];
  907. toolbarItems[0] = createFolderItem;
  908. self.toolbarItems = toolbarItems;
  909. [self setLibraryMode:_libraryMode];
  910. [self updateViewContents];
  911. }
  912. - (void)_endEditingWithHardReset:(BOOL)hardReset
  913. {
  914. [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
  915. if (hardReset)
  916. [self updateViewContents];
  917. else
  918. [self reloadViews];
  919. [self setEditing:NO animated:YES];
  920. }
  921. - (void)deleteSelection
  922. {
  923. NSArray *indexPaths;
  924. if (!_usingTableViewToShowData)
  925. indexPaths = [self.collectionView indexPathsForSelectedItems];
  926. else
  927. indexPaths = [self.tableView indexPathsForSelectedRows];
  928. NSUInteger count = indexPaths.count;
  929. NSMutableArray *objects = [[NSMutableArray alloc] initWithCapacity:count];
  930. for (NSUInteger x = 0; x < count; x++)
  931. [objects addObject:_foundMedia[[indexPaths[x] row]]];
  932. for (NSUInteger x = 0; x < count; x++)
  933. [self removeMediaObject:objects[x] updateDatabase:NO];
  934. [self _endEditingWithHardReset:YES];
  935. }
  936. - (void)renameSelection
  937. {
  938. NSArray *indexPaths;
  939. if (!_usingTableViewToShowData)
  940. indexPaths = [self.collectionView indexPathsForSelectedItems];
  941. else
  942. indexPaths = [self.tableView indexPathsForSelectedRows];
  943. if (indexPaths.count < 1) {
  944. [self _endEditingWithHardReset:NO];
  945. return;
  946. }
  947. NSString *itemName;
  948. if (!_usingTableViewToShowData)
  949. itemName = [(VLCPlaylistCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPaths[0]] titleLabel].text;
  950. else
  951. itemName = [(VLCPlaylistTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPaths[0]] titleLabel].text;
  952. VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:[NSString stringWithFormat:NSLocalizedString(@"RENAME_MEDIA_TO", @""), itemName] message:nil cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", @"") otherButtonTitles:@[NSLocalizedString(@"BUTTON_RENAME", @"")]];
  953. [alert setAlertViewStyle:UIAlertViewStylePlainTextInput];
  954. [[alert textFieldAtIndex:0] setText:itemName];
  955. [[alert textFieldAtIndex:0] setClearButtonMode:UITextFieldViewModeAlways];
  956. __weak VLCAlertView *weakAlert = alert;
  957. alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
  958. if (cancelled)
  959. [self _endEditingWithHardReset:NO];
  960. else
  961. [self renameMediaObjectTo:[weakAlert textFieldAtIndex:0].text];
  962. };
  963. [alert show];
  964. }
  965. - (void)renameMediaObjectTo:(NSString*)newName
  966. {
  967. NSArray *indexPaths;
  968. if (!_usingTableViewToShowData)
  969. indexPaths = [self.collectionView indexPathsForSelectedItems];
  970. else
  971. indexPaths = [self.tableView indexPathsForSelectedRows];
  972. if (indexPaths.count < 1)
  973. return;
  974. id mediaObject = _foundMedia[[indexPaths[0] row]];
  975. if ([mediaObject isKindOfClass:[MLAlbum class]] || [mediaObject isKindOfClass:[MLShowEpisode class]] || [mediaObject isKindOfClass:[MLShow class]] || [mediaObject isKindOfClass:[MLLabel class]] )
  976. [mediaObject setName:newName];
  977. else
  978. [mediaObject setTitle:newName];
  979. if (!_usingTableViewToShowData)
  980. [self.collectionView deselectItemAtIndexPath:indexPaths[0] animated:YES];
  981. else
  982. [self.tableView deselectRowAtIndexPath:indexPaths[0] animated:YES];
  983. if (indexPaths.count > 1)
  984. [self renameSelection];
  985. else
  986. [self _endEditingWithHardReset:NO];
  987. }
  988. #pragma mark - coin coin
  989. - (void)setLibraryMode:(VLCLibraryMode)mode
  990. {
  991. _libraryMode = mode;
  992. [self updateViewContents];
  993. }
  994. #pragma mark - autorotation
  995. // RootController is responsible for supporting interface orientation(iOS6.0+), i.e. navigation controller
  996. // so this will not work as intended without "voodoo magic"(UINavigationController category, subclassing, etc)
  997. /* introduced in iOS 6 */
  998. - (NSUInteger)supportedInterfaceOrientations
  999. {
  1000. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
  1001. return UIInterfaceOrientationMaskAll;
  1002. return (_foundMedia.count > 0)? UIInterfaceOrientationMaskAllButUpsideDown:
  1003. UIInterfaceOrientationMaskPortrait;
  1004. }
  1005. /* introduced in iOS 6 */
  1006. - (BOOL)shouldAutorotate
  1007. {
  1008. return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (_foundMedia.count > 0);
  1009. }
  1010. - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
  1011. {
  1012. [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
  1013. if (!_usingTableViewToShowData)
  1014. [self.collectionView.collectionViewLayout invalidateLayout];
  1015. }
  1016. #pragma mark - Search Display Controller Delegate
  1017. - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
  1018. {
  1019. NSInteger listCount = _foundMedia.count;
  1020. [_searchData removeAllObjects];
  1021. NSManagedObject *item;
  1022. NSRange nameRange;
  1023. for (int i = 0; i < listCount; i++) {
  1024. item = _foundMedia[i];
  1025. if ([item isKindOfClass:[MLAlbum class]]) {
  1026. nameRange = [[(MLAlbum*)item name] rangeOfString:searchString options:NSCaseInsensitiveSearch];
  1027. if (nameRange.location == NSNotFound) {
  1028. NSString *releaseYear = [(MLAlbum *)item releaseYear];
  1029. if (releaseYear)
  1030. nameRange = [releaseYear rangeOfString:searchString options:NSCaseInsensitiveSearch];
  1031. /* user didn't search for our album name or year, let's do a deeper search */
  1032. if (nameRange.location == NSNotFound) {
  1033. NSArray *tracks = [(MLAlbum*)item sortedTracks];
  1034. NSUInteger trackCount = tracks.count;
  1035. for (NSUInteger x = 0; x < trackCount; x++) {
  1036. nameRange = [self _processItem:tracks[x] withString:searchString];
  1037. if (nameRange.location != NSNotFound)
  1038. break;
  1039. }
  1040. }
  1041. }
  1042. } else if ([item isKindOfClass:[MLShow class]]) {
  1043. nameRange = [[(MLShow*)item name] rangeOfString:searchString options:NSCaseInsensitiveSearch];
  1044. /* user didn't search for our show name, let's do a deeper search */
  1045. if (nameRange.location == NSNotFound) {
  1046. NSArray *episodes = [(MLShow*)item sortedEpisodes];
  1047. NSUInteger episodeCount = episodes.count;
  1048. NSString *name;
  1049. for (NSUInteger x = 0; x < episodeCount; x++) {
  1050. name = [(MLShowEpisode*)episodes[x] name];
  1051. if (name)
  1052. nameRange = [name rangeOfString:searchString options:NSCaseInsensitiveSearch];
  1053. if (nameRange.location != NSNotFound) {
  1054. break;
  1055. }
  1056. }
  1057. }
  1058. } else if ([item isKindOfClass:[MLLabel class]]) {
  1059. nameRange = [[(MLLabel*)item name] rangeOfString:searchString options:NSCaseInsensitiveSearch];
  1060. /* user didn't search for our label name, let's do a deeper search */
  1061. if (nameRange.location == NSNotFound) {
  1062. NSArray *files = [(MLLabel*)item sortedFolderItems];
  1063. NSUInteger fileCount = files.count;
  1064. for (NSUInteger x = 0; x < fileCount; x++) {
  1065. nameRange = [self _processItem:files[x] withString:searchString];
  1066. if (nameRange.location != NSNotFound)
  1067. break;
  1068. }
  1069. }
  1070. } else // simple file
  1071. nameRange = [self _processItem:(MLFile*)item withString:searchString];
  1072. if (nameRange.location != NSNotFound)
  1073. [_searchData addObject:item];
  1074. }
  1075. return YES;
  1076. }
  1077. - (NSRange)_processItem:(NSManagedObject *)file withString:(NSString *)searchString
  1078. {
  1079. NSRange nameRange;
  1080. nameRange = [[(MLFile *)file title] rangeOfString:searchString options:NSCaseInsensitiveSearch];
  1081. if (nameRange.location == NSNotFound) {
  1082. NSString *artist = [(MLFile *)file artist];
  1083. if (artist)
  1084. nameRange = [artist rangeOfString:searchString options:NSCaseInsensitiveSearch];
  1085. if (nameRange.location == NSNotFound && ![file isKindOfClass:[MLAlbumTrack class]]) {
  1086. NSString *releaseYear = [(MLFile *)file releaseYear];
  1087. if (releaseYear)
  1088. nameRange = [releaseYear rangeOfString:searchString options:NSCaseInsensitiveSearch];
  1089. }
  1090. }
  1091. return nameRange;
  1092. }
  1093. - (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
  1094. {
  1095. tableView.rowHeight = 90.;
  1096. tableView.backgroundColor = [UIColor blackColor];
  1097. }
  1098. @end