VLCPlaylistViewController.m 53 KB

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