VLCLibraryViewController.m 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446
  1. /*****************************************************************************
  2. * VLCLibraryViewController.m
  3. * VLC for iOS
  4. *****************************************************************************
  5. * Copyright (c) 2013-2017 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. * Tobias Conradi <videolan # tobias-conradi.de>
  13. *
  14. * Refer to the COPYING file of the official project for license.
  15. *****************************************************************************/
  16. #import "VLCLibraryViewController.h"
  17. #import "LXReorderableCollectionViewFlowLayout.h"
  18. #import "VLCActivityViewControllerVendor.h"
  19. #import "VLCAppDelegate.h"
  20. #import "VLCBugreporter.h"
  21. #import "VLCFirstStepsViewController.h"
  22. #import "VLCFolderCollectionViewFlowLayout.h"
  23. #import "VLCKeychainCoordinator.h"
  24. #import "VLCMediaDataSource.h"
  25. #import "VLCLibrarySearchDisplayDataSource.h"
  26. #import "VLCMovieViewController.h"
  27. #import "VLCPlaylistCollectionViewCell.h"
  28. #import "VLCPlaylistTableViewCell.h"
  29. #import "VLCPlaybackController+MediaLibrary.h"
  30. #import "VLC_iOS-Swift.h"
  31. #import <CoreSpotlight/CoreSpotlight.h>
  32. /* prefs keys */
  33. static NSString *kDisplayedFirstSteps = @"Did we display the first steps tutorial?";
  34. static NSString *kUsingTableViewToShowData = @"UsingTableViewToShowData";
  35. @implementation EmptyLibraryView
  36. - (IBAction)learnMore:(id)sender
  37. {
  38. UIViewController *firstStepsVC = [[VLCFirstStepsViewController alloc] initWithNibName:nil bundle:nil];
  39. UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:firstStepsVC];
  40. navCon.modalPresentationStyle = UIModalPresentationFormSheet;
  41. [self.window.rootViewController presentViewController:navCon animated:YES completion:nil];
  42. }
  43. @end
  44. @interface VLCLibraryViewController () <VLCFolderCollectionViewDelegateFlowLayout, LXReorderableCollectionViewDataSource, LXReorderableCollectionViewDelegateFlowLayout, UITableViewDataSource, UITableViewDelegate, MLMediaLibrary, VLCMediaListDelegate, UISearchResultsUpdating, UISearchControllerDelegate> {
  45. VLCLibraryMode _libraryMode;
  46. VLCLibraryMode _previousLibraryMode;
  47. UIBarButtonItem *_menuButton;
  48. NSMutableArray *_indexPaths;
  49. id _folderObject;
  50. VLCFolderCollectionViewFlowLayout *_folderLayout;
  51. LXReorderableCollectionViewFlowLayout *_reorderLayout;
  52. BOOL _inFolder;
  53. BOOL _isSelected;
  54. BOOL _deleteFromTableView;
  55. UILongPressGestureRecognizer *_longPressGestureRecognizer;
  56. VLCLibrarySearchDisplayDataSource *_searchDataSource;
  57. VLCMediaDataSource *_mediaDataSource;
  58. UISearchController *_searchController;
  59. UIBarButtonItem *_selectAllBarButtonItem;
  60. UIBarButtonItem *_createFolderBarButtonItem;
  61. UIBarButtonItem *_shareBarButtonItem;
  62. UIBarButtonItem *_removeFromFolderBarButtonItem;
  63. UIBarButtonItem *_deleteSelectedBarButtonItem;
  64. NSObject *dragAndDropManager;
  65. }
  66. @property (nonatomic, strong) UIBarButtonItem *displayModeBarButtonItem;
  67. @property (nonatomic, strong) UITableView *tableView;
  68. @property (nonatomic, strong) UICollectionView *collectionView;
  69. @property (nonatomic, strong) EmptyLibraryView *emptyLibraryView;
  70. @property (nonatomic) BOOL usingTableViewToShowData;
  71. @end
  72. @implementation VLCLibraryViewController
  73. + (void)initialize
  74. {
  75. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  76. [defaults registerDefaults:@{kDisplayedFirstSteps : [NSNumber numberWithBool:NO]}];
  77. [defaults registerDefaults:@{kUsingTableViewToShowData : [NSNumber numberWithBool:UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone]}];
  78. }
  79. - (void)loadView
  80. {
  81. _mediaDataSource = [VLCMediaDataSource new];
  82. _searchDataSource = [VLCLibrarySearchDisplayDataSource new];
  83. self.emptyLibraryView = [[[NSBundle mainBundle] loadNibNamed:@"VLCEmptyLibraryView" owner:self options:nil] lastObject];
  84. _emptyLibraryView.emptyLibraryLongDescriptionLabel.lineBreakMode = NSLineBreakByWordWrapping;
  85. _emptyLibraryView.emptyLibraryLongDescriptionLabel.numberOfLines = 0;
  86. if (@available(iOS 11.0, *)) {
  87. dragAndDropManager = [VLCDragAndDropManager new];
  88. ((VLCDragAndDropManager *)dragAndDropManager).delegate = _mediaDataSource;
  89. UIDropInteraction *dropInteraction = [[UIDropInteraction alloc] initWithDelegate:(VLCDragAndDropManager *)dragAndDropManager];
  90. [_emptyLibraryView addInteraction:dropInteraction];
  91. }
  92. [self setupContentView];
  93. [self setViewFromDeviceOrientation];
  94. [self updateViewsForCurrentDisplayMode];
  95. _libraryMode = VLCLibraryModeAllFiles;
  96. }
  97. - (void)setupContentView
  98. {
  99. CGRect viewDimensions = [UIApplication sharedApplication].keyWindow.bounds;
  100. UIView *contentView = [[UIView alloc] initWithFrame:viewDimensions];
  101. contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  102. contentView.backgroundColor = [UIColor VLCDarkBackgroundColor];
  103. if (self.usingTableViewToShowData) {
  104. if (!_tableView) {
  105. _tableView = [[UITableView alloc] initWithFrame:viewDimensions style:UITableViewStylePlain];
  106. _tableView.backgroundColor = [UIColor VLCDarkBackgroundColor];
  107. CGRect frame = _tableView.bounds;
  108. frame.origin.y = -frame.size.height;
  109. UIView *topView = [[UIView alloc] initWithFrame:frame];
  110. topView.backgroundColor = [UIColor VLCDarkBackgroundColor];
  111. [_tableView addSubview:topView];
  112. _tableView.rowHeight = [VLCPlaylistTableViewCell heightOfCell];
  113. _tableView.separatorColor = [UIColor VLCDarkBackgroundColor];
  114. _tableView.delegate = self;
  115. _tableView.dataSource = self;
  116. if (@available(iOS 11.0, *)) {
  117. _tableView.dragDelegate = ((VLCDragAndDropManager *)dragAndDropManager);
  118. _tableView.dropDelegate = ((VLCDragAndDropManager *)dragAndDropManager);
  119. }
  120. _tableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
  121. _tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  122. _tableView.tableHeaderView = _searchController.searchBar;
  123. UINib *nib = [UINib nibWithNibName:@"VLCPlaylistTableViewCell" bundle:nil];
  124. [_tableView registerNib:nib forCellReuseIdentifier:kPlaylistCellIdentifier];
  125. }
  126. _tableView.frame = contentView.bounds;
  127. [contentView addSubview:_tableView];
  128. [_tableView reloadData];
  129. } else {
  130. if (!_collectionView) {
  131. _folderLayout = [[VLCFolderCollectionViewFlowLayout alloc] init];
  132. _collectionView = [[UICollectionView alloc] initWithFrame:viewDimensions collectionViewLayout:_folderLayout];
  133. _collectionView.alwaysBounceVertical = YES;
  134. if (@available(iOS 11.0, *)) {
  135. _collectionView.dragDelegate = ((VLCDragAndDropManager *)dragAndDropManager);
  136. _collectionView.dropDelegate = ((VLCDragAndDropManager *)dragAndDropManager);
  137. }
  138. _collectionView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
  139. _collectionView.delegate = self;
  140. _collectionView.dataSource = self;
  141. _collectionView.backgroundColor = [UIColor VLCDarkBackgroundColor];
  142. _collectionView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  143. _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_collectionViewHandleLongPressGesture:)];
  144. [_collectionView addGestureRecognizer:_longPressGestureRecognizer];
  145. [_collectionView registerNib:[UINib nibWithNibName:@"VLCPlaylistCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"PlaylistCell"];
  146. }
  147. _collectionView.frame = contentView.bounds;
  148. [contentView addSubview:_collectionView];
  149. [_collectionView reloadData];
  150. [_searchController setActive:NO];
  151. }
  152. self.view = contentView;
  153. }
  154. #pragma mark -
  155. - (void)viewWillLayoutSubviews {
  156. UIScrollView *dataView = self.usingTableViewToShowData ? _tableView : _collectionView;
  157. [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
  158. [dataView setContentInset:UIEdgeInsetsZero];
  159. }
  160. - (void)viewDidLoad
  161. {
  162. [super viewDidLoad];
  163. self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", nil);
  164. _menuButton = [UIBarButtonItem themedRevealMenuButtonWithTarget:self andSelector:@selector(leftButtonAction:)];
  165. self.navigationItem.leftBarButtonItem = _menuButton;
  166. self.editButtonItem.title = NSLocalizedString(@"BUTTON_EDIT", nil);
  167. self.editButtonItem.tintColor = [UIColor whiteColor];
  168. _selectAllBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"BUTTON_ALL", nil) style:UIBarButtonItemStylePlain target:self action:@selector(handleSelection)];
  169. _selectAllBarButtonItem.tintColor = [UIColor whiteColor];
  170. UIFont *font = [UIFont boldSystemFontOfSize:17];
  171. NSDictionary *attributes = @{NSFontAttributeName: font};
  172. [_selectAllBarButtonItem setTitleTextAttributes:attributes forState:UIControlStateNormal];
  173. _emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"EMPTY_LIBRARY", nil);
  174. _emptyLibraryView.emptyLibraryLongDescriptionLabel.text = NSLocalizedString(@"EMPTY_LIBRARY_LONG", nil);
  175. [_emptyLibraryView.emptyLibraryLongDescriptionLabel sizeToFit];
  176. [_emptyLibraryView.learnMoreButton setTitle:NSLocalizedString(@"BUTTON_LEARN_MORE", nil) forState:UIControlStateNormal];
  177. _createFolderBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemOrganize target:self action:@selector(createFolder)];
  178. // Better visual alignment with the action button
  179. _createFolderBarButtonItem.imageInsets = UIEdgeInsetsMake(3, 0, -3, 0);
  180. _createFolderBarButtonItem.landscapeImagePhoneInsets = UIEdgeInsetsMake(2, 0, -2, 0);
  181. _removeFromFolderBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemReply target:self action:@selector(removeFromFolder)];
  182. _removeFromFolderBarButtonItem.imageInsets = UIEdgeInsetsMake(2, 0, -2, 0);
  183. _removeFromFolderBarButtonItem.landscapeImagePhoneInsets = UIEdgeInsetsMake(1, 0, -1, 0);
  184. _removeFromFolderBarButtonItem.enabled = NO;
  185. _shareBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(share:)];
  186. _shareBarButtonItem.enabled = NO;
  187. _deleteSelectedBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(deleteSelection:)];
  188. _deleteFromTableView = NO;
  189. UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
  190. fixedSpace.width = 20;
  191. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
  192. fixedSpace.width *= 2;
  193. }
  194. [self setToolbarItems:@[_shareBarButtonItem,
  195. fixedSpace,
  196. _createFolderBarButtonItem,
  197. [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
  198. [UIBarButtonItem themedDarkToolbarButtonWithTitle:NSLocalizedString(@"BUTTON_RENAME", nil) target:self andSelector:@selector(renameSelection)],
  199. [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
  200. _removeFromFolderBarButtonItem,
  201. fixedSpace,
  202. _deleteSelectedBarButtonItem]];
  203. self.navigationController.toolbar.barStyle = UIBarStyleBlack;
  204. self.navigationController.toolbar.tintColor = [UIColor whiteColor];
  205. [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
  206. self.edgesForExtendedLayout = UIRectEdgeNone;
  207. [self setupSearchController];
  208. }
  209. - (void)setupSearchController
  210. {
  211. _searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
  212. _searchController.searchResultsUpdater = self;
  213. _searchController.dimsBackgroundDuringPresentation = NO;
  214. _searchController.delegate = self;
  215. [self setSearchBar:YES resetContent:YES];
  216. }
  217. - (void)viewWillAppear:(BOOL)animated
  218. {
  219. [super viewWillAppear:animated];
  220. [self.collectionView.collectionViewLayout invalidateLayout];
  221. [self setViewFromDeviceOrientation];
  222. [self updateViewsForCurrentDisplayMode];
  223. }
  224. - (void)viewDidAppear:(BOOL)animated
  225. {
  226. [super viewDidAppear:animated];
  227. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  228. if (![[defaults objectForKey:kDisplayedFirstSteps] boolValue]) {
  229. [self.emptyLibraryView performSelector:@selector(learnMore:) withObject:nil afterDelay:1.];
  230. [defaults setObject:[NSNumber numberWithBool:YES] forKey:kDisplayedFirstSteps];
  231. }
  232. if ([_mediaDataSource numberOfFiles] < 1)
  233. [self updateViewContents];
  234. [[MLMediaLibrary sharedMediaLibrary] performSelector:@selector(libraryDidAppear) withObject:nil afterDelay:1.];
  235. }
  236. - (void)viewDidDisappear:(BOOL)animated
  237. {
  238. [super viewDidDisappear:animated];
  239. [[MLMediaLibrary sharedMediaLibrary] libraryDidDisappear];
  240. }
  241. - (BOOL)canBecomeFirstResponder
  242. {
  243. return YES;
  244. }
  245. - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
  246. {
  247. if (motion == UIEventSubtypeMotionShake)
  248. [[VLCBugreporter sharedInstance] handleBugreportRequest];
  249. }
  250. - (void)openMediaObject:(NSManagedObject *)mediaObject
  251. {
  252. if ([mediaObject isKindOfClass:[MLAlbum class]] || [mediaObject isKindOfClass:[MLShow class]]) {
  253. [_mediaDataSource updateContentsForSelection:mediaObject];
  254. BOOL isAlbum = [mediaObject isKindOfClass:[MLAlbum class]];
  255. self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
  256. if (_libraryMode == VLCLibraryModeAllFiles)
  257. self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", nil);
  258. else
  259. [self.navigationItem.leftBarButtonItem setTitle: isAlbum ? NSLocalizedString(@"LIBRARY_MUSIC", nil) : NSLocalizedString(@"LIBRARY_SERIES", nil)];
  260. self.title = [(MLAlbum*)mediaObject name];
  261. [self reloadViews];
  262. } else if ([mediaObject isKindOfClass:[MLLabel class]]) {
  263. [_mediaDataSource updateContentsForSelection:mediaObject];
  264. _inFolder = YES;
  265. if (!self.usingTableViewToShowData) {
  266. if (@available(iOS 11.0, *)) {
  267. } else {
  268. if (![self.collectionView.collectionViewLayout isEqual:_reorderLayout]) {
  269. for (UIGestureRecognizer *recognizer in _collectionView.gestureRecognizers) {
  270. if (recognizer == _folderLayout.panGestureRecognizer || recognizer == _folderLayout.longPressGestureRecognizer || recognizer == _longPressGestureRecognizer)
  271. [self.collectionView removeGestureRecognizer:recognizer];
  272. }
  273. _reorderLayout = [[LXReorderableCollectionViewFlowLayout alloc] init];
  274. [self.collectionView setCollectionViewLayout:_reorderLayout animated:NO];
  275. }
  276. }
  277. }
  278. _libraryMode = VLCLibraryModeFolder;
  279. self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
  280. self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", nil);
  281. self.title = [(MLLabel*)mediaObject name];
  282. _removeFromFolderBarButtonItem.enabled = YES;
  283. _createFolderBarButtonItem.enabled = NO;
  284. [self reloadViews];
  285. return;
  286. } else {
  287. VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
  288. [vpc playMediaLibraryObject:mediaObject];
  289. [self createSpotlightItem:mediaObject];
  290. }
  291. }
  292. - (void)createSpotlightItem:(nonnull NSManagedObject *)mediaObject
  293. {
  294. if ([CSSearchableItemAttributeSet class] != nil && ![[VLCKeychainCoordinator defaultCoordinator] passcodeLockEnabled]) {
  295. self.userActivity = [[NSUserActivity alloc] initWithActivityType:kVLCUserActivityPlaying];
  296. MLFile *file = nil;
  297. if ([mediaObject isKindOfClass:[MLAlbumTrack class]]) {
  298. file = [(MLAlbumTrack *)mediaObject anyFileFromTrack];
  299. } else if ([mediaObject isKindOfClass:[MLShowEpisode class]]) {
  300. file = [(MLShowEpisode *)mediaObject anyFileFromEpisode];
  301. } else if ([mediaObject isKindOfClass:[MLFile class]]){
  302. file = (MLFile *)mediaObject;
  303. }
  304. self.userActivity.title = file.title;
  305. self.userActivity.contentAttributeSet = file.coreSpotlightAttributeSet;
  306. self.userActivity.userInfo = @{@"playingmedia":mediaObject.objectID.URIRepresentation};
  307. self.userActivity.eligibleForSearch = YES;
  308. self.userActivity.eligibleForHandoff = YES;
  309. //self.userActivity.contentUserAction = NSUserActivityContentUserActionPlay;
  310. [self.userActivity becomeCurrent];
  311. }
  312. }
  313. - (void)removeMediaObject:(id)managedObject updateDatabase:(BOOL)updateDB
  314. {
  315. [_mediaDataSource removeMediaObject:managedObject];
  316. if (updateDB) {
  317. [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
  318. [self updateViewContents];
  319. }
  320. }
  321. - (void)_displayEmptyLibraryViewIfNeeded
  322. {
  323. if (self.emptyLibraryView.superview)
  324. [self.emptyLibraryView removeFromSuperview];
  325. if ([_mediaDataSource numberOfFiles] == 0) {
  326. _inFolder = (_libraryMode == VLCLibraryModeFolder || _libraryMode == VLCLibraryModeCreateFolder);
  327. self.emptyLibraryView.emptyLibraryLabel.text = _inFolder ? NSLocalizedString(@"FOLDER_EMPTY", nil) : NSLocalizedString(@"EMPTY_LIBRARY", nil);
  328. self.emptyLibraryView.emptyLibraryLongDescriptionLabel.text = _inFolder ? NSLocalizedString(@"FOLDER_EMPTY_LONG", nil) : NSLocalizedString(@"EMPTY_LIBRARY_LONG", nil);
  329. self.emptyLibraryView.learnMoreButton.hidden = _inFolder;
  330. self.emptyLibraryView.frame = self.view.bounds;
  331. [self.view addSubview:self.emptyLibraryView];
  332. self.navigationItem.rightBarButtonItems = nil;
  333. } else {
  334. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
  335. UIBarButtonItem *toggleDisplayedView = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"tableViewIcon"] style:UIBarButtonItemStylePlain target:self action:@selector(toggleDisplayedView:)];
  336. self.navigationItem.rightBarButtonItems = @[toggleDisplayedView, self.editButtonItem];
  337. self.displayModeBarButtonItem = toggleDisplayedView;
  338. } else {
  339. self.navigationItem.rightBarButtonItem = self.editButtonItem;
  340. }
  341. }
  342. if (self.usingTableViewToShowData) {
  343. _tableView.separatorStyle = [_mediaDataSource numberOfFiles] > 0 ? UITableViewCellSeparatorStyleSingleLine:
  344. UITableViewCellSeparatorStyleNone;
  345. } else {
  346. [self.collectionView.collectionViewLayout invalidateLayout];
  347. }
  348. [self updateViewsForCurrentDisplayMode];
  349. }
  350. - (void)setViewFromDeviceOrientation
  351. {
  352. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
  353. BOOL isPortrait = YES;
  354. if (SYSTEM_RUNS_IOS8_OR_LATER) {
  355. if (self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact)
  356. isPortrait = NO;
  357. } else {
  358. if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]))
  359. isPortrait = NO;
  360. }
  361. [self setUsingTableViewToShowData:isPortrait];
  362. [self _displayEmptyLibraryViewIfNeeded];
  363. }
  364. }
  365. - (void)setSearchBar:(BOOL)enable resetContent:(BOOL)resetContent
  366. {
  367. _tableView.tableHeaderView = enable ? _searchController.searchBar : nil;
  368. if (resetContent) {
  369. CGPoint contentOffset = _tableView.contentOffset;
  370. contentOffset.y += CGRectGetHeight(_tableView.tableHeaderView.frame);
  371. _tableView.contentOffset = contentOffset;
  372. }
  373. }
  374. - (void)libraryUpgradeComplete
  375. {
  376. self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", nil);
  377. self.navigationItem.leftBarButtonItem = _menuButton;
  378. self.emptyLibraryView.emptyLibraryLongDescriptionLabel.hidden = NO;
  379. self.emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"EMPTY_LIBRARY", nil);
  380. [self.emptyLibraryView.activityIndicator stopAnimating];
  381. [self.emptyLibraryView removeFromSuperview];
  382. [self updateViewContents];
  383. }
  384. - (void)updateViewContents
  385. {
  386. self.navigationItem.leftBarButtonItem = _menuButton;
  387. [_mediaDataSource updateContentsForSelection:nil];
  388. _removeFromFolderBarButtonItem.enabled = NO;
  389. switch (_libraryMode) {
  390. case VLCLibraryModeAllAlbums: {
  391. self.title = NSLocalizedString(@"LIBRARY_MUSIC", nil);
  392. _createFolderBarButtonItem.enabled = NO;
  393. [_mediaDataSource addAlbumsInAllAlbumMode:YES];
  394. } break;
  395. case VLCLibraryModeAllSeries: {
  396. self.title = NSLocalizedString(@"LIBRARY_SERIES", nil);
  397. _createFolderBarButtonItem.enabled = NO;
  398. [_mediaDataSource addAllShows];
  399. } break;
  400. //Todo: I'm not sure if updateViewContents should be called in VLCLibraryModeFolder
  401. //Here should maybe be an NSAssert to prevent this but for now due to refactoring these calls would've been made in that case
  402. case VLCLibraryModeAllFiles:
  403. case VLCLibraryModeFolder:
  404. case VLCLibraryModeCreateFolder: {
  405. self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", nil);
  406. _createFolderBarButtonItem.enabled = YES;
  407. [_mediaDataSource addAlbumsInAllAlbumMode:NO];
  408. [_mediaDataSource addAllShows];
  409. [_mediaDataSource addAllFolders];
  410. [_mediaDataSource addRemainingFiles];
  411. }
  412. }
  413. [self reloadViews];
  414. }
  415. - (void)reloadViews
  416. {
  417. // Since this gets can get called at any point and wipe away the selections, we update the actionBarButtonItem here because this can happen if you tap "Save Video" in the UIActivityController and a media access alert view takes away focus (the corresponding 'became active' notification of UIApplication will call this). Or simply try bringing down the notification center to trigger this. Any existing UIActivityViewController session should be safe as it would have copies of the selected file references.
  418. if (self.usingTableViewToShowData) {
  419. [self.tableView reloadData];
  420. [self updateActionBarButtonItemStateWithSelectedIndexPaths:[self.tableView indexPathsForSelectedRows]];
  421. _isSelected = NO;
  422. } else {
  423. [self.collectionView reloadData];
  424. [self updateActionBarButtonItemStateWithSelectedIndexPaths:[self.collectionView indexPathsForSelectedItems]];
  425. if (_libraryMode == VLCLibraryModeAllFiles) {
  426. if (self.collectionView.collectionViewLayout != _folderLayout) {
  427. for (UIGestureRecognizer *recognizer in _collectionView.gestureRecognizers) {
  428. if (recognizer == _reorderLayout.panGestureRecognizer ||
  429. recognizer == _reorderLayout.longPressGestureRecognizer) {
  430. [self.collectionView removeGestureRecognizer:recognizer];
  431. }
  432. }
  433. [self.collectionView setCollectionViewLayout:_folderLayout animated:NO];
  434. [self.collectionView addGestureRecognizer:_longPressGestureRecognizer];
  435. }
  436. }
  437. }
  438. [self _displayEmptyLibraryViewIfNeeded];
  439. }
  440. #pragma mark - Table View
  441. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  442. {
  443. return [_mediaDataSource numberOfFiles];
  444. }
  445. // Customize the appearance of table view cells.
  446. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  447. {
  448. static NSString *CellIdentifier = @"PlaylistCell";
  449. VLCPlaylistTableViewCell *cell = (VLCPlaylistTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  450. UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRightOnTableViewCellGestureAction:)];
  451. [swipeRight setDirection:(UISwipeGestureRecognizerDirectionRight)];
  452. [cell addGestureRecognizer:swipeRight];
  453. cell.mediaObject = [_mediaDataSource objectAtIndex:indexPath.row];
  454. return cell;
  455. }
  456. - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
  457. {
  458. [_mediaDataSource moveObjectFromIndex:fromIndexPath.item toIndex:toIndexPath.item];
  459. }
  460. - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
  461. {
  462. if (@available(iOS 11.0, *)) {
  463. return true;
  464. }
  465. return _inFolder;
  466. }
  467. - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath
  468. {
  469. if (@available(iOS 11.0, *)) {
  470. return true;
  471. }
  472. return _inFolder;
  473. }
  474. - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
  475. {
  476. cell.backgroundColor = (indexPath.row % 2 == 0)? [UIColor blackColor]: [UIColor VLCDarkBackgroundColor];
  477. cell.multipleSelectionBackgroundView.backgroundColor = cell.backgroundColor;
  478. }
  479. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
  480. {
  481. if (editingStyle == UITableViewCellEditingStyleDelete) {
  482. NSInteger row = indexPath.row;
  483. _deleteFromTableView = YES;
  484. if (row < [_mediaDataSource numberOfFiles])
  485. [self deleteSelection:indexPath];
  486. }
  487. }
  488. - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
  489. {
  490. if (tableView.isEditing) {
  491. [self updateActionBarButtonItemStateWithSelectedIndexPaths:[tableView indexPathsForSelectedRows]];
  492. }
  493. }
  494. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  495. {
  496. if ([(VLCPlaylistTableViewCell *)[tableView cellForRowAtIndexPath:indexPath] isExpanded]) {
  497. [(VLCPlaylistTableViewCell *)[tableView cellForRowAtIndexPath:indexPath] collapsWithAnimation:YES];
  498. return;
  499. }
  500. for (VLCPlaylistTableViewCell *cell in [tableView visibleCells]) {
  501. if ([cell isExpanded])
  502. [cell collapsWithAnimation:NO];
  503. }
  504. if (tableView.isEditing) {
  505. if (_libraryMode == VLCLibraryModeCreateFolder) {
  506. _folderObject = [_mediaDataSource objectAtIndex:indexPath.row];
  507. _libraryMode = _previousLibraryMode;
  508. [self updateViewContents];
  509. [self createFolderWithName:nil];
  510. } else {
  511. [self updateActionBarButtonItemStateWithSelectedIndexPaths:[tableView indexPathsForSelectedRows]];
  512. }
  513. return;
  514. }
  515. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  516. NSManagedObject *selectedObject = [_mediaDataSource objectAtIndex:indexPath.row];
  517. if (selectedObject != nil)
  518. [self openMediaObject:selectedObject];
  519. }
  520. #pragma mark - Gesture Action
  521. - (void)swipeRightOnTableViewCellGestureAction:(UIGestureRecognizer *)recognizer
  522. {
  523. [self setEditing:!self.tableView.isEditing animated:YES];
  524. if (!self.tableView.isEditing) {
  525. NSIndexPath *path = [self.tableView indexPathForRowAtPoint:[recognizer locationInView:self.tableView]];
  526. [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:path.row inSection:path.section]
  527. animated:YES
  528. scrollPosition:UITableViewScrollPositionNone];
  529. [self updateActionBarButtonItemStateWithSelectedIndexPaths:[self.tableView indexPathsForSelectedRows]];
  530. }
  531. }
  532. - (void)swipeRightOnCollectionViewCellGestureAction:(UIGestureRecognizer *)recognizer
  533. {
  534. NSIndexPath *path = [self.collectionView indexPathForItemAtPoint:[recognizer locationInView:self.collectionView]];
  535. VLCPlaylistCollectionViewCell *cell = (VLCPlaylistCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:path];
  536. [cell showMetadata:!cell.showsMetaData];
  537. }
  538. #pragma mark - Collection View
  539. - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
  540. {
  541. return [_mediaDataSource numberOfFiles];
  542. }
  543. - (UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
  544. {
  545. VLCPlaylistCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"PlaylistCell" forIndexPath:indexPath];
  546. cell.mediaObject = [_mediaDataSource objectAtIndex:indexPath.row];
  547. cell.collectionView = _collectionView;
  548. [cell setEditing:self.editing animated:NO];
  549. UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRightOnCollectionViewCellGestureAction:)];
  550. [swipeRight setDirection:(UISwipeGestureRecognizerDirectionRight)];
  551. [cell addGestureRecognizer:swipeRight];
  552. return cell;
  553. }
  554. - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
  555. {
  556. const CGFloat maxCellWidth = [UIScreen mainScreen].bounds.size.width / 3;
  557. const CGFloat aspectRatio = 9.0/16.0;
  558. CGRect windowFrame = [UIApplication sharedApplication].keyWindow.frame;
  559. CGFloat windowWidth = windowFrame.size.width;
  560. int numberOfCellsPerRow = ceil(windowWidth/maxCellWidth);
  561. CGFloat cellWidth = windowWidth/numberOfCellsPerRow;
  562. cellWidth -= 5;
  563. return CGSizeMake(cellWidth, cellWidth * aspectRatio);
  564. }
  565. - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
  566. {
  567. return UIEdgeInsetsMake(5, 5, 5, 5);
  568. }
  569. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
  570. {
  571. return 2.5;
  572. }
  573. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
  574. {
  575. return 0.;
  576. }
  577. - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
  578. {
  579. if (self.editing) {
  580. if (_libraryMode == VLCLibraryModeCreateFolder) {
  581. _folderObject = [_mediaDataSource objectAtIndex:indexPath.item];
  582. [self updateViewContents];
  583. [self createFolderWithName:nil];
  584. _libraryMode = _previousLibraryMode;
  585. } else {
  586. [self updateActionBarButtonItemStateWithSelectedIndexPaths:[collectionView indexPathsForSelectedItems]];
  587. }
  588. [(VLCPlaylistCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath] selectionUpdate];
  589. return;
  590. }
  591. [collectionView deselectItemAtIndexPath:indexPath animated:NO];
  592. NSArray *visibleCells = [[collectionView visibleCells] copy];
  593. NSUInteger cellCount = visibleCells.count;
  594. for (NSUInteger x = 0; x < cellCount; x++) {
  595. VLCPlaylistCollectionViewCell *cell = visibleCells[x];
  596. if ([cell showsMetaData])
  597. [cell showMetadata:NO];
  598. }
  599. NSManagedObject *selectedObject = [_mediaDataSource objectAtIndex:indexPath.row];
  600. if (selectedObject != nil)
  601. [self openMediaObject:selectedObject];
  602. }
  603. - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
  604. {
  605. if (self.editing) {
  606. [self updateActionBarButtonItemStateWithSelectedIndexPaths:[collectionView indexPathsForSelectedItems]];
  607. }
  608. [(VLCPlaylistCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath] selectionUpdate];
  609. }
  610. - (void)collectionView:(UICollectionView *)collectionView removeItemFromFolderAtIndexPathIfNeeded:(NSIndexPath *)indexPath
  611. {
  612. id mediaObject = [_mediaDataSource objectAtIndex:indexPath.item];
  613. [_mediaDataSource removeMediaObjectFromFolder:mediaObject];
  614. [self backToAllItems:nil];
  615. }
  616. - (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath willMoveToIndexPath:(NSIndexPath *)toIndexPath
  617. {
  618. [_mediaDataSource moveObjectFromIndex:fromIndexPath.item toIndex:toIndexPath.item];
  619. }
  620. - (BOOL)collectionView:(UICollectionView *)collectionView shouldSpringLoadItemAtIndexPath:(NSIndexPath *)indexPath withContext:(id<UISpringLoadedInteractionContext>)context NS_AVAILABLE_IOS(11.0)
  621. {
  622. id mediaObject = [_mediaDataSource objectAtIndex:indexPath.item];
  623. return [mediaObject isKindOfClass:[MLLabel class]];
  624. }
  625. - (void)collectionView:(UICollectionView *)collectionView requestToMoveItemAtIndexPath:(NSIndexPath *)itemPath intoFolderAtIndexPath:(NSIndexPath *)folderPath
  626. {
  627. id folderPathItem = [_mediaDataSource objectAtIndex:folderPath.item];
  628. id itemPathItem = [_mediaDataSource objectAtIndex:itemPath.item];
  629. BOOL validFileTypeAtFolderPath = ([folderPathItem isKindOfClass:[MLFile class]] || [folderPathItem isKindOfClass:[MLLabel class]]) && [itemPathItem isKindOfClass:[MLFile class]];
  630. if (!validFileTypeAtFolderPath) {
  631. VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FOLDER_INVALID_TYPE_TITLE", nil) message:NSLocalizedString(@"FOLDER_INVALID_TYPE_MESSAGE", nil) cancelButtonTitle:nil otherButtonTitles:@[NSLocalizedString(@"BUTTON_OK", nil)]];
  632. alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
  633. [self updateViewContents];
  634. };
  635. [alert show];
  636. return;
  637. }
  638. BOOL isFolder = [folderPathItem isKindOfClass:[MLLabel class]];
  639. if (isFolder){
  640. MLLabel *folder = folderPathItem;
  641. MLFile *file = itemPathItem;
  642. [file setLabels:[[NSSet alloc] initWithObjects:folder, nil]];
  643. file.folderTrackNumber = @([folder.files count] - 1);
  644. [_mediaDataSource removeObjectAtIndex:itemPath.item];
  645. [self updateViewContents];
  646. } else {
  647. _folderObject = folderPathItem;
  648. _indexPaths = [NSMutableArray arrayWithArray:@[itemPath]];
  649. [self showCreateFolderAlert];
  650. }
  651. }
  652. #pragma mark - collection/tableView helper
  653. - (NSArray *)selectedIndexPaths
  654. {
  655. NSArray *indexPaths;
  656. if (self.usingTableViewToShowData)
  657. indexPaths = [self.tableView indexPathsForSelectedRows];
  658. else
  659. indexPaths = [self.collectionView indexPathsForSelectedItems];
  660. return indexPaths ?: [NSArray array];
  661. }
  662. #pragma mark - Folder implementation
  663. - (void)showCreateFolderAlert
  664. {
  665. VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FOLDER_CHOOSE_NAME_TITLE", nil) message:NSLocalizedString(@"FOLDER_CHOOSE_NAME_MESSAGE", nil) cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", nil) otherButtonTitles:@[NSLocalizedString(@"BUTTON_SAVE", nil)]];
  666. [alert setAlertViewStyle:UIAlertViewStylePlainTextInput];
  667. UITextField *zeroTextField = [alert textFieldAtIndex:0];
  668. [zeroTextField setText:NSLocalizedString(@"FOLDER_NAME_PLACEHOLDER", nil)];
  669. [zeroTextField setClearButtonMode:UITextFieldViewModeAlways];
  670. __weak VLCAlertView *weakAlert = alert;
  671. alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
  672. if (cancelled)
  673. [self updateViewContents];
  674. else
  675. [self createFolderWithName:[weakAlert textFieldAtIndex:0].text];
  676. };
  677. [alert show];
  678. }
  679. - (void)createFolder
  680. {
  681. if (_libraryMode == VLCLibraryModeCreateFolder) {
  682. _libraryMode = _previousLibraryMode;
  683. [self updateViewContents];
  684. [self showCreateFolderAlert];
  685. return;
  686. }
  687. _indexPaths = [NSMutableArray arrayWithArray:[self selectedIndexPaths]];
  688. for (NSInteger i = _indexPaths.count - 1; i >=0; i--) {
  689. NSIndexPath *path = _indexPaths[i];
  690. id mediaObject = [_mediaDataSource objectAtIndex:path.row];
  691. if ([mediaObject isKindOfClass:[MLLabel class]])
  692. [_indexPaths removeObject:path];
  693. }
  694. if ([_indexPaths count] != 0) {
  695. NSArray *folder = [MLLabel allLabels];
  696. //if we already have folders display them
  697. if ([folder count] > 0) {
  698. [_mediaDataSource updateContentsForSelection:nil];
  699. [_mediaDataSource addAllFolders];
  700. self.title = NSLocalizedString(@"SELECT_FOLDER", nil);
  701. _previousLibraryMode = _libraryMode;
  702. _libraryMode = VLCLibraryModeCreateFolder;
  703. [self reloadViews];
  704. return;
  705. }
  706. }
  707. //no selected items or no existing folder ask for foldername
  708. [self showCreateFolderAlert];
  709. }
  710. - (void)removeFromFolder
  711. {
  712. _indexPaths = [NSMutableArray arrayWithArray:[self selectedIndexPaths]];
  713. [_indexPaths sortUsingSelector:@selector(compare:)];
  714. for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
  715. NSIndexPath *path = _indexPaths[i];
  716. id item = [_mediaDataSource objectAtIndex:path.row];
  717. [_mediaDataSource removeMediaObjectFromFolder:item];
  718. [_mediaDataSource removeObjectAtIndex:path.row];
  719. }
  720. [self reloadViews];
  721. }
  722. - (void)createFolderWithName:(NSString *)folderName
  723. {
  724. NSArray *labels = [MLLabel allLabels];
  725. for (MLLabel *label in labels){
  726. if ([label.name isEqualToString:folderName]) {
  727. _folderObject = nil;
  728. _indexPaths = nil;
  729. VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FOLDER_NAME_DUPLICATE_TITLE", nil) message:NSLocalizedString(@"FOLDER_NAME_DUPLICATE_MESSAGE", nil) cancelButtonTitle:nil otherButtonTitles:@[NSLocalizedString(@"BUTTON_OK", nil)]];
  730. alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
  731. [self updateViewContents];
  732. };
  733. [alert show];
  734. return;
  735. }
  736. }
  737. if (_folderObject != nil) {
  738. id mediaObject;
  739. NSUInteger folderIndex = [_mediaDataSource indexOfObject:_folderObject];
  740. if (folderIndex != NSNotFound) {
  741. mediaObject = [_mediaDataSource objectAtIndex:folderIndex];
  742. }
  743. //item got dragged onto item
  744. if ([mediaObject isKindOfClass:[MLFile class]]) {
  745. MLFile *file = (MLFile *)mediaObject;
  746. MLLabel *label = [[MLMediaLibrary sharedMediaLibrary] createObjectForEntity:@"Label"];
  747. label.name = folderName;
  748. file.labels = [NSSet setWithObjects:label,nil];
  749. NSNumber *folderTrackNumber = [NSNumber numberWithInt:(int)[label files].count - 1];
  750. file.folderTrackNumber = folderTrackNumber;
  751. [_mediaDataSource removeObjectAtIndex:folderIndex];
  752. [_mediaDataSource insertObject:label atIndex:folderIndex];
  753. id item = [_mediaDataSource objectAtIndex:((NSIndexPath *)_indexPaths[0]).item];
  754. if (![item isKindOfClass:[MLFile class]])
  755. return;
  756. MLFile *itemFile = (MLFile *)item;
  757. itemFile.labels = file.labels;
  758. [_mediaDataSource removeObjectAtIndex:((NSIndexPath *)_indexPaths[0]).item];
  759. itemFile.folderTrackNumber = @([label files].count - 1);
  760. } else {
  761. //item got dragged onto folder or items should be added to folder
  762. id label = [_mediaDataSource objectAtIndex:folderIndex];
  763. if (![label isKindOfClass:[MLLabel class]])
  764. return;
  765. [_indexPaths sortUsingSelector:@selector(compare:)];
  766. NSUInteger count = [_mediaDataSource numberOfFiles];
  767. for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
  768. NSIndexPath *path = _indexPaths[i];
  769. if (path.row >= count)
  770. continue;
  771. id object = [_mediaDataSource objectAtIndex:path.row];
  772. if (_libraryMode != VLCLibraryModeCreateFolder && ![object isKindOfClass:[MLFile class]])
  773. continue;
  774. if (_libraryMode == VLCLibraryModeCreateFolder)
  775. [self updateViewContents];
  776. id item = [_mediaDataSource objectAtIndex:path.row];
  777. if (![item isKindOfClass:[MLFile class]])
  778. continue;
  779. MLFile *file = (MLFile *)item;
  780. file.labels = [NSSet setWithObjects:label, nil];
  781. [_mediaDataSource removeObjectAtIndex:path.row];
  782. file.folderTrackNumber = @([label files].count - 1);
  783. }
  784. }
  785. _folderObject = nil;
  786. } else {
  787. //create new folder
  788. MLLabel *label = [[MLMediaLibrary sharedMediaLibrary] createObjectForEntity:@"Label"];
  789. label.name = folderName;
  790. //if items were selected
  791. if ([_indexPaths count] != 0) {
  792. [_indexPaths sortUsingSelector:@selector(compare:)];
  793. for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
  794. NSIndexPath *path = _indexPaths[i];
  795. NSUInteger index = self.usingTableViewToShowData ? path.row : path.item;
  796. if (index < [_mediaDataSource numberOfFiles]) {
  797. id item = [_mediaDataSource objectAtIndex:index];
  798. if (![item isKindOfClass:[MLFile class]])
  799. continue;
  800. MLFile *file = (MLFile *)item;
  801. file.labels = [NSSet setWithObjects:label, nil];
  802. file.folderTrackNumber = @([label files].count - 1);
  803. [_mediaDataSource removeObjectAtIndex:index];
  804. }
  805. }
  806. }
  807. }
  808. _indexPaths = nil;
  809. [self setEditing:NO];
  810. [self updateViewContents];
  811. }
  812. - (void)_collectionViewHandleLongPressGesture:(UIGestureRecognizer *) sender
  813. {
  814. if (sender.state == UIGestureRecognizerStateBegan && !self.isEditing)
  815. [self setEditing:YES animated:YES];
  816. }
  817. #pragma mark - UI implementation
  818. - (void)handleSelection
  819. {
  820. if (self.usingTableViewToShowData) {
  821. NSInteger numberOfSections = [self.tableView numberOfSections];
  822. for (NSInteger section = 0; section < numberOfSections; section++) {
  823. NSInteger numberOfRowInSection = [self.tableView numberOfRowsInSection:section];
  824. for (NSInteger row = 0; row < numberOfRowInSection; row++) {
  825. if (!_isSelected)
  826. [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:section] animated:NO scrollPosition:UITableViewScrollPositionNone];
  827. else
  828. [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:section] animated:NO];
  829. }
  830. }
  831. } else {
  832. NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:0];
  833. for (NSInteger item = 0; item < numberOfItems; item++) {
  834. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:item inSection:0];
  835. if (!_isSelected)
  836. [self.collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
  837. else
  838. [self.collectionView deselectItemAtIndexPath:indexPath animated:NO];
  839. [(VLCPlaylistCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath] selectionUpdate];
  840. }
  841. }
  842. _isSelected = !_isSelected;
  843. }
  844. - (void)setEditing:(BOOL)editing animated:(BOOL)animated
  845. {
  846. [super setEditing:editing animated:animated];
  847. _isSelected = NO;
  848. UIBarButtonItem *editButton = self.editButtonItem;
  849. editButton.tintColor = [UIColor whiteColor];
  850. if (!editing && self.navigationItem.rightBarButtonItems.lastObject == _selectAllBarButtonItem)
  851. [self.navigationItem setRightBarButtonItems: [self.navigationItem.rightBarButtonItems subarrayWithRange:NSMakeRange(0, self.navigationItem.rightBarButtonItems.count - 1)]];
  852. else
  853. [self.navigationItem setRightBarButtonItems:editing ? [self.navigationItem.rightBarButtonItems arrayByAddingObject:_selectAllBarButtonItem] : [self.navigationItem rightBarButtonItems] animated:YES];
  854. if (self.usingTableViewToShowData) {
  855. [self setSearchBar:!editing resetContent:!editing];
  856. self.tableView.allowsMultipleSelectionDuringEditing = editing;
  857. [self.tableView setEditing:editing animated:YES];
  858. } else {
  859. NSArray *visibleCells = self.collectionView.visibleCells;
  860. [visibleCells enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  861. VLCPlaylistCollectionViewCell *aCell = (VLCPlaylistCollectionViewCell*)obj;
  862. // always clear selection when enter edit mode
  863. aCell.selected = NO;
  864. [aCell setEditing:editing animated:animated];
  865. }];
  866. self.collectionView.allowsMultipleSelection = editing;
  867. /* UIKit doesn't clear the selection automagically if we leave the editing mode
  868. * so we need to do so manually */
  869. if (!editing) {
  870. [self.collectionView addGestureRecognizer:_longPressGestureRecognizer];
  871. NSArray *selectedItems = [self.collectionView indexPathsForSelectedItems];
  872. NSUInteger count = selectedItems.count;
  873. for (NSUInteger x = 0; x < count; x++)
  874. [self.collectionView deselectItemAtIndexPath:selectedItems[x] animated:NO];
  875. } else
  876. [self.collectionView removeGestureRecognizer:_longPressGestureRecognizer];
  877. }
  878. if (_libraryMode == VLCLibraryModeCreateFolder) {
  879. _libraryMode = _previousLibraryMode;
  880. _indexPaths = nil;
  881. [self updateViewContents];
  882. }
  883. [self.navigationController setToolbarHidden:!editing animated:YES];
  884. [UIView performWithoutAnimation:^{
  885. [editButton setTitle:editing ? NSLocalizedString(@"BUTTON_CANCEL", nil) : NSLocalizedString(@"BUTTON_EDIT", nil)];
  886. }];
  887. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
  888. NSMutableArray *rightBarButtonItems = [self.navigationItem.rightBarButtonItems mutableCopy];
  889. UIBarButtonItem *toggleDisplayedView = rightBarButtonItems[0];
  890. toggleDisplayedView.enabled = !editing;
  891. rightBarButtonItems[0] = toggleDisplayedView;
  892. self.navigationItem.rightBarButtonItems = rightBarButtonItems;
  893. }
  894. }
  895. - (void)toggleDisplayedView:(UIBarButtonItem *)button
  896. {
  897. self.usingTableViewToShowData = !self.usingTableViewToShowData;
  898. UIImage *newButtonImage = [UIImage imageNamed: self.usingTableViewToShowData ? @"collectionViewIcon" : @"tableViewIcon"];
  899. [button setImage:newButtonImage];
  900. [self updateViewsForCurrentDisplayMode];
  901. }
  902. - (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
  903. {
  904. return UITableViewCellEditingStyleDelete;
  905. }
  906. - (IBAction)leftButtonAction:(id)sender
  907. {
  908. [[VLCSidebarController sharedInstance] toggleSidebar];
  909. if (self.isEditing)
  910. [self setEditing:NO animated:YES];
  911. }
  912. - (IBAction)backToAllItems:(id)sender
  913. {
  914. _inFolder = NO;
  915. NSMutableArray *toolbarItems = [self.toolbarItems mutableCopy];
  916. toolbarItems[2] = _createFolderBarButtonItem;
  917. self.toolbarItems = toolbarItems;
  918. [self setLibraryMode:_previousLibraryMode];
  919. if (!self.isEditing) {
  920. [self setSearchBar:YES resetContent:NO];
  921. }
  922. [self updateViewContents];
  923. }
  924. - (void)_endEditingWithHardReset:(BOOL)hardReset
  925. {
  926. [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
  927. if (hardReset)
  928. [self updateViewContents];
  929. else
  930. [self reloadViews];
  931. [self setEditing:NO animated:YES];
  932. }
  933. - (void)deleteSelection:(id)sender
  934. {
  935. NSArray *indexPaths = [self usingTableViewToShowData] ? [self.tableView indexPathsForSelectedRows] : [self.collectionView indexPathsForSelectedItems];
  936. if ((!indexPaths || [indexPaths count] == 0) && !_deleteFromTableView) {
  937. if ([UIAlertController class]) {
  938. UIAlertController *invalidSelection = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"DELETE_INVALID_TITLE", nil) message:NSLocalizedString(@"DELETE_INVALID_MESSAGE", nil) preferredStyle:UIAlertControllerStyleAlert];
  939. UIAlertAction *doneAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"BUTTON_OK", nil) style:UIAlertActionStyleDefault handler:nil];
  940. [invalidSelection addAction:doneAction];
  941. [self presentViewController:invalidSelection animated:YES completion:nil];
  942. } else {
  943. //TODO remove when we drop iOS7
  944. VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"DELETE_INVALID_TITLE", nil)
  945. message:NSLocalizedString(@"DELETE_INVALID_MESSAGE", nil)
  946. cancelButtonTitle:NSLocalizedString(@"BUTTON_OK", nil)
  947. otherButtonTitles:nil];
  948. alert.completion = nil;
  949. [alert show];
  950. }
  951. } else {
  952. UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"DELETE_TITLE", nil) message:nil preferredStyle:UIAlertControllerStyleActionSheet];
  953. UIAlertAction *deleteAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"BUTTON_DELETE", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
  954. if (_deleteFromTableView) {
  955. NSIndexPath *indexPath = (NSIndexPath *)sender;
  956. if (indexPath && indexPath.row < [_mediaDataSource numberOfFiles])
  957. [self removeMediaObject:[_mediaDataSource objectAtIndex:indexPath.row] updateDatabase:YES];
  958. } else
  959. [self deletionAfterConfirmation];
  960. _deleteFromTableView = NO;
  961. }];
  962. UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"BUTTON_CANCEL", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
  963. _deleteFromTableView ? [self setEditing:NO animated:YES] : [self reloadViews];
  964. _deleteFromTableView = NO;
  965. }];
  966. [alert addAction:deleteAction];
  967. [alert addAction:cancelAction];
  968. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
  969. CGRect tmpBounds = self.view.bounds;
  970. [alert setTitle:NSLocalizedString(@"DELETE_TITLE", nil)];
  971. [alert setMessage:NSLocalizedString(@"DELETE_MESSAGE", nil)];
  972. [alert.popoverPresentationController setPermittedArrowDirections:0];
  973. [alert.popoverPresentationController setSourceView:self.view];
  974. [alert.popoverPresentationController setSourceRect:CGRectMake(tmpBounds.size.width / 2.0, tmpBounds.size.height / 2.0, 1.0, 1.0)];
  975. }
  976. [self presentViewController:alert animated:YES completion:nil];
  977. }
  978. }
  979. - (void)deletionAfterConfirmation
  980. {
  981. NSArray *indexPaths;
  982. if (self.usingTableViewToShowData) {
  983. indexPaths = [self.tableView indexPathsForSelectedRows];
  984. } else {
  985. indexPaths = [self.collectionView indexPathsForSelectedItems];
  986. }
  987. NSMutableArray *objects = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
  988. for (NSIndexPath *indexPath in indexPaths)
  989. [objects addObject:[_mediaDataSource objectAtIndex:indexPath.row]];
  990. for (id object in objects)
  991. [self removeMediaObject:object updateDatabase:NO];
  992. [self _endEditingWithHardReset:YES];
  993. }
  994. - (void)renameSelection
  995. {
  996. NSArray *indexPaths = [self selectedIndexPaths];
  997. if (indexPaths.count < 1) {
  998. [self _endEditingWithHardReset:NO];
  999. return;
  1000. }
  1001. NSString *itemName;
  1002. if (self.usingTableViewToShowData)
  1003. itemName = [(VLCPlaylistTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPaths[0]] titleLabel].text;
  1004. else
  1005. itemName = [(VLCPlaylistCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPaths[0]] titleLabel].text;
  1006. VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:[NSString stringWithFormat:NSLocalizedString(@"RENAME_MEDIA_TO", nil), itemName] message:nil cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", nil) otherButtonTitles:@[NSLocalizedString(@"BUTTON_RENAME", nil)]];
  1007. [alert setAlertViewStyle:UIAlertViewStylePlainTextInput];
  1008. [[alert textFieldAtIndex:0] setText:itemName];
  1009. [[alert textFieldAtIndex:0] setClearButtonMode:UITextFieldViewModeAlways];
  1010. __weak VLCAlertView *weakAlert = alert;
  1011. alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
  1012. if (cancelled)
  1013. [self _endEditingWithHardReset:NO];
  1014. else
  1015. [self renameMediaObjectTo:[weakAlert textFieldAtIndex:0].text];
  1016. };
  1017. [alert show];
  1018. }
  1019. - (void)renameMediaObjectTo:(NSString*)newName
  1020. {
  1021. NSArray *indexPaths = [self selectedIndexPaths];
  1022. if (indexPaths.count < 1)
  1023. return;
  1024. NSUInteger row = [indexPaths[0] row];
  1025. id mediaObject = [_mediaDataSource objectAtIndex:row];
  1026. if (!mediaObject) return;
  1027. if ([mediaObject isKindOfClass:[MLAlbum class]] || [mediaObject isKindOfClass:[MLShowEpisode class]] || [mediaObject isKindOfClass:[MLShow class]] || [mediaObject isKindOfClass:[MLLabel class]] )
  1028. [mediaObject setName:newName];
  1029. else
  1030. [mediaObject setTitle:newName];
  1031. if (self.usingTableViewToShowData)
  1032. [self.tableView deselectRowAtIndexPath:indexPaths[0] animated:YES];
  1033. else
  1034. [self.collectionView deselectItemAtIndexPath:indexPaths[0] animated:YES];
  1035. if (indexPaths.count > 1)
  1036. [self renameSelection];
  1037. else
  1038. [self _endEditingWithHardReset:NO];
  1039. }
  1040. - (void)updateViewsForCurrentDisplayMode
  1041. {
  1042. UIImage *newButtonImage = [UIImage imageNamed: self.usingTableViewToShowData ? @"collectionViewIcon" : @"tableViewIcon"];
  1043. [self.displayModeBarButtonItem setImage:newButtonImage];
  1044. }
  1045. #pragma mark - Sharing
  1046. // We take the array of index paths (instead of a count) to actually examine if the content is shareable. Selecting a single folder should not enable the share button.
  1047. - (void)updateActionBarButtonItemStateWithSelectedIndexPaths:(NSArray *)indexPaths
  1048. {
  1049. NSUInteger count = [indexPaths count];
  1050. if (!indexPaths || count == 0) {
  1051. _shareBarButtonItem.enabled = NO;
  1052. } else {
  1053. // Look for at least one MLFile
  1054. for (NSIndexPath *indexPath in indexPaths) {
  1055. id mediaItem = [_mediaDataSource objectAtIndex:[indexPath row]];
  1056. if ([mediaItem isKindOfClass:[MLFile class]] || [mediaItem isKindOfClass:[MLAlbumTrack class]] | [mediaItem isKindOfClass:[MLShowEpisode class]]) {
  1057. _shareBarButtonItem.enabled = YES;
  1058. return;
  1059. }
  1060. }
  1061. }
  1062. }
  1063. - (NSArray *)fileURLsFromSelection
  1064. {
  1065. NSArray *indexPaths = [self selectedIndexPaths];
  1066. if (indexPaths.count == 0) return nil;
  1067. NSMutableArray<NSURL*> *fileURLObjects = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
  1068. for (NSIndexPath *indexpath in indexPaths) {
  1069. id mediaItem = [_mediaDataSource objectAtIndex:[indexpath row]];
  1070. NSURL *fileURL;
  1071. if ([mediaItem isKindOfClass:[MLFile class]]) {
  1072. fileURL = [(MLFile *) mediaItem url];
  1073. } else if ([mediaItem isKindOfClass:[MLAlbumTrack class]]) {
  1074. fileURL = [[(MLAlbumTrack *) mediaItem anyFileFromTrack] url];
  1075. } else if ([mediaItem isKindOfClass:[MLShowEpisode class]]) {
  1076. fileURL = [[(MLShowEpisode *) mediaItem anyFileFromEpisode] url];
  1077. }
  1078. if ([fileURL isFileURL]) {
  1079. [fileURLObjects addObject:fileURL];
  1080. }
  1081. }
  1082. return [fileURLObjects copy];
  1083. }
  1084. - (void)share:(UIBarButtonItem *)barButtonItem
  1085. {
  1086. NSParameterAssert(barButtonItem);
  1087. if (!barButtonItem) {
  1088. APLog(@"Missing a UIBarButtonItem to present from");
  1089. return;
  1090. }
  1091. //disable any possible changes to selection (or exit from this screen)
  1092. [[UIApplication sharedApplication] beginIgnoringInteractionEvents];
  1093. UIActivityViewController *controller = [VLCActivityViewControllerVendor activityViewControllerForFiles:[self fileURLsFromSelection] presentingButton:barButtonItem presentingViewController:self];
  1094. if (!controller) {
  1095. [[UIApplication sharedApplication] endIgnoringInteractionEvents];
  1096. } else {
  1097. if (SYSTEM_RUNS_IOS8_OR_LATER) {
  1098. controller.popoverPresentationController.sourceView = self.navigationController.toolbar;
  1099. }
  1100. [self.navigationController presentViewController:controller animated:YES completion:^{
  1101. [[UIApplication sharedApplication] endIgnoringInteractionEvents];
  1102. }];
  1103. }
  1104. }
  1105. #pragma mark - properties
  1106. - (void)setLibraryMode:(VLCLibraryMode)mode
  1107. {
  1108. _libraryMode = mode;
  1109. [self updateViewContents];
  1110. if (mode == VLCLibraryModeAllAlbums ||
  1111. mode == VLCLibraryModeAllSeries ||
  1112. mode == VLCLibraryModeAllFiles) {
  1113. _previousLibraryMode = mode;
  1114. }
  1115. }
  1116. - (BOOL)usingTableViewToShowData
  1117. {
  1118. return [[NSUserDefaults standardUserDefaults] boolForKey:kUsingTableViewToShowData];
  1119. }
  1120. - (void)setUsingTableViewToShowData:(BOOL)usingTableViewToShowData
  1121. {
  1122. [[NSUserDefaults standardUserDefaults] setBool:usingTableViewToShowData forKey:kUsingTableViewToShowData];
  1123. [self updateViewsForCurrentDisplayMode];
  1124. [self setupContentView];
  1125. }
  1126. #pragma mark - autorotation
  1127. // RootController is responsible for supporting interface orientation(iOS6.0+), i.e. navigation controller
  1128. // so this will not work as intended without "voodoo magic"(UINavigationController category, subclassing, etc)
  1129. /* introduced in iOS 6 */
  1130. - (UIInterfaceOrientationMask)supportedInterfaceOrientations
  1131. {
  1132. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
  1133. return UIInterfaceOrientationMaskAll;
  1134. return ([_mediaDataSource numberOfFiles] > 0)? UIInterfaceOrientationMaskAllButUpsideDown:
  1135. UIInterfaceOrientationMaskPortrait;
  1136. }
  1137. /* introduced in iOS 6 */
  1138. - (BOOL)shouldAutorotate
  1139. {
  1140. return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || ([_mediaDataSource numberOfFiles] > 0);
  1141. }
  1142. - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
  1143. {
  1144. [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
  1145. [self setEditing:NO];
  1146. [self setViewFromDeviceOrientation];
  1147. if (self.usingTableViewToShowData) {
  1148. NSArray *visibleCells = [self.tableView visibleCells];
  1149. NSUInteger cellCount = visibleCells.count;
  1150. for (NSUInteger x = 0; x < cellCount; x++) {
  1151. if ([visibleCells[x] isExpanded])
  1152. [visibleCells[x] metaDataLabel].hidden = YES;
  1153. }
  1154. } else {
  1155. [self.collectionView.collectionViewLayout invalidateLayout];
  1156. }
  1157. }
  1158. // >= iOS 8
  1159. - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
  1160. {
  1161. [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
  1162. [self setEditing:NO];
  1163. [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
  1164. [self setViewFromDeviceOrientation];
  1165. if (!self.usingTableViewToShowData) {
  1166. [self.collectionView.collectionViewLayout invalidateLayout];
  1167. }
  1168. } completion:nil];
  1169. }
  1170. #pragma mark - UIScrollViewDelegate
  1171. - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
  1172. {
  1173. if ([_searchController.searchBar isFirstResponder])
  1174. [_searchController.searchBar resignFirstResponder];
  1175. }
  1176. #pragma mark - SearchController Delegate
  1177. - (void)didPresentSearchController:(UISearchController *)searchController
  1178. {
  1179. _tableView.dataSource = _searchDataSource;
  1180. }
  1181. - (void)didDismissSearchController:(UISearchController *)searchController
  1182. {
  1183. _tableView.dataSource = self;
  1184. }
  1185. #pragma mark - Search Research Updater
  1186. - (void)updateSearchResultsForSearchController:(UISearchController *)searchController
  1187. {
  1188. [_searchDataSource shouldReloadTableForSearchString:searchController.searchBar.text searchableFiles:[_mediaDataSource allObjects]];
  1189. [_tableView reloadData];
  1190. }
  1191. #pragma mark - handoff
  1192. - (void)restoreUserActivityState:(NSUserActivity *)activity
  1193. {
  1194. NSString *userActivityType = activity.activityType;
  1195. if([userActivityType isEqualToString:kVLCUserActivityLibraryMode] ||
  1196. [userActivityType isEqualToString:kVLCUserActivityLibrarySelection]) {
  1197. NSDictionary *dict = activity.userInfo;
  1198. NSString *folderPath = dict[@"folder"];
  1199. if (!folderPath) return;
  1200. NSURL *folderURL = [NSURL URLWithString:folderPath];
  1201. NSUInteger count = [_mediaDataSource numberOfFiles];
  1202. for (NSUInteger i = 0; i < count; i++) {
  1203. NSManagedObject *object = [_mediaDataSource objectAtIndex:i];
  1204. if([object.objectID.URIRepresentation isEqual:folderURL]) {
  1205. [self tableView:self.tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
  1206. }
  1207. }
  1208. }
  1209. }
  1210. @end