|
@@ -5,205 +5,105 @@
|
|
|
* Copyright (c) 2018 VideoLAN. All rights reserved.
|
|
|
* $Id$
|
|
|
*
|
|
|
- * Authors: Carola Nitz <nitz.carola # gmail.com>
|
|
|
- * Mike JS. Choi <mkchoi212 # icloud.com>
|
|
|
+ * Authors: Carola Nitz <caro # videolan.org>
|
|
|
*
|
|
|
* Refer to the COPYING file of the official project for license.
|
|
|
*****************************************************************************/
|
|
|
|
|
|
-import Foundation
|
|
|
+import UIKit
|
|
|
|
|
|
-protocol VLCMediaViewControllerDelegate: class {
|
|
|
- func mediaViewControllerDidSelectMediaObject(_ mediaViewController: UIViewController, mediaObject: NSManagedObject)
|
|
|
-}
|
|
|
-
|
|
|
-class VLCMediaViewController<T>: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchResultsUpdating, UISearchControllerDelegate, IndicatorInfoProvider {
|
|
|
- let cellPadding: CGFloat = 5.0
|
|
|
- private var services: Services
|
|
|
- private var searchController: UISearchController?
|
|
|
- private let searchDataSource = VLCLibrarySearchDisplayDataSource()
|
|
|
- var subcategory: VLCMediaSubcategory<T>
|
|
|
- weak var delegate: VLCMediaViewControllerDelegate?
|
|
|
-
|
|
|
- @available(iOS 11.0, *)
|
|
|
- lazy var dragAndDropManager: VLCDragAndDropManager = { () -> VLCDragAndDropManager<T> in
|
|
|
- VLCDragAndDropManager<T>(subcategory: subcategory)
|
|
|
-
|
|
|
- }()
|
|
|
-
|
|
|
- lazy var emptyView: VLCEmptyLibraryView = {
|
|
|
- let name = String(describing: VLCEmptyLibraryView.self)
|
|
|
- let nib = Bundle.main.loadNibNamed(name, owner: self, options: nil)
|
|
|
- guard let emptyView = nib?.first as? VLCEmptyLibraryView else { fatalError("Can't find nib for \(name)") }
|
|
|
- return emptyView
|
|
|
- }()
|
|
|
-
|
|
|
- @available(*, unavailable)
|
|
|
- init() {
|
|
|
- fatalError()
|
|
|
+class VLCVideoViewController: VLCMediaViewController {
|
|
|
+ override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
|
|
|
+ let movies = VLCMediaCategoryViewController<MLFile>(services: services, subcategory: VLCMediaSubcategories.movies)
|
|
|
+ movies.delegate = mediaDelegate
|
|
|
+ let episodes = VLCMediaCategoryViewController<MLShowEpisode>(services: services, subcategory: VLCMediaSubcategories.episodes)
|
|
|
+ episodes.delegate = mediaDelegate
|
|
|
+ let playlists = VLCMediaCategoryViewController<MLLabel>(services: services, subcategory: VLCMediaSubcategories.videoPlaylists)
|
|
|
+ playlists.delegate = mediaDelegate
|
|
|
+ return [movies, episodes, playlists]
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- init(services: Services, subcategory: VLCMediaSubcategory<T>) {
|
|
|
- self.services = services
|
|
|
- self.subcategory = subcategory
|
|
|
-
|
|
|
- super.init(collectionViewLayout: UICollectionViewFlowLayout())
|
|
|
- NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .VLCThemeDidChangeNotification, object: nil)
|
|
|
- NotificationCenter.default.addObserver(self, selector: #selector(reloadData), name: subcategory.notificationName, object: nil)
|
|
|
+class VLCAudioViewController: VLCMediaViewController {
|
|
|
+ override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
|
|
|
+ let tracks = VLCMediaCategoryViewController<MLFile>(services: services, subcategory: VLCMediaSubcategories.tracks)
|
|
|
+ tracks.delegate = mediaDelegate
|
|
|
+ let genres = VLCMediaCategoryViewController<String>(services: services, subcategory: VLCMediaSubcategories.genres)
|
|
|
+ genres.delegate = mediaDelegate
|
|
|
+ let artists = VLCMediaCategoryViewController<String>(services: services, subcategory: VLCMediaSubcategories.artists)
|
|
|
+ artists.delegate = mediaDelegate
|
|
|
+ let albums = VLCMediaCategoryViewController<MLAlbum>(services: services, subcategory: VLCMediaSubcategories.albums)
|
|
|
+ albums.delegate = mediaDelegate
|
|
|
+ let playlists = VLCMediaCategoryViewController<MLLabel>(services: services, subcategory: VLCMediaSubcategories.audioPlaylists)
|
|
|
+ playlists.delegate = mediaDelegate
|
|
|
+ return [tracks, genres, artists, albums, playlists]
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- override var preferredStatusBarStyle: UIStatusBarStyle {
|
|
|
- return PresentationTheme.current.colors.statusBarStyle
|
|
|
- }
|
|
|
+class VLCMediaViewController: VLCPagingViewController<VLCLabelCell> {
|
|
|
|
|
|
- @objc func reloadData() {
|
|
|
- collectionView?.reloadData()
|
|
|
- updateUIForContent()
|
|
|
- }
|
|
|
+ var services: Services
|
|
|
+ weak var mediaDelegate: VLCMediaCategoryViewControllerDelegate?
|
|
|
+ private var rendererButton: UIButton
|
|
|
|
|
|
- @available(*, unavailable)
|
|
|
- required init?(coder aDecoder: NSCoder) {
|
|
|
- fatalError("init(coder: ) has not been implemented")
|
|
|
+ init(services: Services) {
|
|
|
+ self.services = services
|
|
|
+ rendererButton = services.rendererDiscovererManager.setupRendererButton()
|
|
|
+ super.init(nibName: nil, bundle: nil)
|
|
|
}
|
|
|
|
|
|
override func viewDidLoad() {
|
|
|
- super.viewDidLoad()
|
|
|
- setupCollectionView()
|
|
|
- setupSearchController()
|
|
|
- _ = (MLMediaLibrary.sharedMediaLibrary() as! MLMediaLibrary).libraryDidAppear()
|
|
|
- }
|
|
|
|
|
|
- override func viewWillAppear(_ animated: Bool) {
|
|
|
- super.viewWillAppear(animated)
|
|
|
- let manager = services.rendererDiscovererManager
|
|
|
- if manager.discoverers.isEmpty {
|
|
|
- // Either didn't start or stopped before
|
|
|
- manager.start()
|
|
|
+ changeCurrentIndexProgressive = { (oldCell: VLCLabelCell?, newCell: VLCLabelCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) in
|
|
|
+ guard changeCurrentIndex == true else { return }
|
|
|
+ oldCell?.iconLabel.textColor = PresentationTheme.current.colors.cellDetailTextColor
|
|
|
+ newCell?.iconLabel.textColor = PresentationTheme.current.colors.orangeUI
|
|
|
}
|
|
|
- manager.presentingViewController = self
|
|
|
- }
|
|
|
-
|
|
|
- @objc func themeDidChange() {
|
|
|
- collectionView?.backgroundColor = PresentationTheme.current.colors.background
|
|
|
- setNeedsStatusBarAppearanceUpdate()
|
|
|
+ setupNavigationBar()
|
|
|
+ super.viewDidLoad()
|
|
|
}
|
|
|
|
|
|
- func setupCollectionView() {
|
|
|
- let playlistnib = UINib(nibName: "VLCPlaylistCollectionViewCell", bundle: nil)
|
|
|
- collectionView?.register(playlistnib, forCellWithReuseIdentifier: VLCPlaylistCollectionViewCell.cellIdentifier())
|
|
|
- collectionView?.backgroundColor = PresentationTheme.current.colors.background
|
|
|
- collectionView?.alwaysBounceVertical = true
|
|
|
+ private func setupNavigationBar() {
|
|
|
if #available(iOS 11.0, *) {
|
|
|
- collectionView?.dragDelegate = dragAndDropManager
|
|
|
- collectionView?.dropDelegate = dragAndDropManager
|
|
|
+ navigationController?.navigationBar.prefersLargeTitles = false
|
|
|
}
|
|
|
+ navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("SORT", comment: ""), style: .plain, target: self, action: #selector(sort))
|
|
|
+ navigationItem.rightBarButtonItem = UIBarButtonItem(customView: rendererButton)
|
|
|
}
|
|
|
|
|
|
- override func viewDidAppear(_ animated: Bool) {
|
|
|
- super.viewDidAppear(animated)
|
|
|
- reloadData()
|
|
|
- }
|
|
|
-
|
|
|
- func setupSearchController() {
|
|
|
- searchController = UISearchController(searchResultsController: nil)
|
|
|
- searchController?.searchResultsUpdater = self
|
|
|
- searchController?.dimsBackgroundDuringPresentation = false
|
|
|
- searchController?.delegate = self
|
|
|
- if let textfield = searchController?.searchBar.value(forKey: "searchField") as? UITextField {
|
|
|
- if let backgroundview = textfield.subviews.first {
|
|
|
- backgroundview.backgroundColor = UIColor.white
|
|
|
- backgroundview.layer.cornerRadius = 10
|
|
|
- backgroundview.clipsToBounds = true
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- func updateUIForContent() {
|
|
|
- let isEmpty = collectionView?.numberOfItems(inSection: 0) == 0
|
|
|
-
|
|
|
- if isEmpty {
|
|
|
- collectionView?.setContentOffset(.zero, animated: false)
|
|
|
+ @objc func sort() {
|
|
|
+ // This should be in a subclass
|
|
|
+ let sortOptionsAlertController = UIAlertController(title: NSLocalizedString("SORT_BY", comment: ""), message: nil, preferredStyle: .actionSheet)
|
|
|
+ let sortByNameAction = UIAlertAction(title: SortOption.alphabetically.localizedDescription, style: .default) { action in
|
|
|
}
|
|
|
- collectionView?.backgroundView = isEmpty ? emptyView : nil
|
|
|
-
|
|
|
- if #available(iOS 11.0, *) {
|
|
|
- navigationItem.searchController = isEmpty ? nil : searchController
|
|
|
- } else {
|
|
|
- navigationItem.titleView = isEmpty ? nil : searchController?.searchBar
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // MARK: Renderer
|
|
|
-
|
|
|
- override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
|
|
- collectionView?.collectionViewLayout.invalidateLayout()
|
|
|
- }
|
|
|
-
|
|
|
- // MARK: - Search
|
|
|
-
|
|
|
- func updateSearchResults(for searchController: UISearchController) {
|
|
|
- searchDataSource.shouldReloadTable(forSearch: searchController.searchBar.text, searchableFiles: subcategory.files)
|
|
|
- collectionView?.reloadData()
|
|
|
- }
|
|
|
-
|
|
|
- func didPresentSearchController(_ searchController: UISearchController) {
|
|
|
- collectionView?.dataSource = searchDataSource
|
|
|
- }
|
|
|
-
|
|
|
- func didDismissSearchController(_ searchController: UISearchController) {
|
|
|
- collectionView?.dataSource = self
|
|
|
- }
|
|
|
-
|
|
|
- func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo {
|
|
|
- return IndicatorInfo(title:subcategory.indicatorInfoName)
|
|
|
- }
|
|
|
-
|
|
|
- // MARK: - UICollectionViewDataSource
|
|
|
- override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
|
|
- return subcategory.files.count
|
|
|
- }
|
|
|
-
|
|
|
- override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
|
- if let playlistCell = collectionView.dequeueReusableCell(withReuseIdentifier: VLCPlaylistCollectionViewCell.cellIdentifier(), for: indexPath) as? VLCPlaylistCollectionViewCell {
|
|
|
- if let mediaObject = subcategory.files[indexPath.row] as? NSManagedObject {
|
|
|
- playlistCell.mediaObject = mediaObject
|
|
|
- }
|
|
|
- return playlistCell
|
|
|
+ let sortBySizeAction = UIAlertAction(title: SortOption.size.localizedDescription, style: .default) { action in
|
|
|
}
|
|
|
- return UICollectionViewCell()
|
|
|
- }
|
|
|
-
|
|
|
- // MARK: - UICollectionViewDelegate
|
|
|
- override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
|
|
- if let mediaObject = subcategory.files[indexPath.row] as? NSManagedObject {
|
|
|
- delegate?.mediaViewControllerDidSelectMediaObject(self, mediaObject: mediaObject)
|
|
|
+ let sortbyDateAction = UIAlertAction(title: SortOption.insertonDate.localizedDescription, style: .default) { action in
|
|
|
}
|
|
|
+ let cancelAction = UIAlertAction(title: NSLocalizedString("CANCEL", comment: ""), style: .cancel, handler: nil)
|
|
|
+ sortOptionsAlertController.addAction(sortByNameAction)
|
|
|
+ sortOptionsAlertController.addAction(sortbyDateAction)
|
|
|
+ sortOptionsAlertController.addAction(sortBySizeAction)
|
|
|
+ sortOptionsAlertController.addAction(cancelAction)
|
|
|
+ sortOptionsAlertController.view.tintColor = UIColor.vlcOrangeTint()
|
|
|
+ present(sortOptionsAlertController, animated: true)
|
|
|
}
|
|
|
|
|
|
- // MARK: - UICollectionViewDelegateFlowLayout
|
|
|
- func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
|
|
+ // MARK: - PagerTabStripDataSource
|
|
|
|
|
|
- let numberOfCells: CGFloat = collectionView.traitCollection.horizontalSizeClass == .regular ? 3.0 : 2.0
|
|
|
- let aspectRatio: CGFloat = 10.0 / 16.0
|
|
|
-
|
|
|
- // We have the number of cells and we always have numberofCells + 1 padding spaces. -pad-[Cell]-pad-[Cell]-pad-
|
|
|
- // we then have the entire padding, we divide the entire padding by the number of Cells to know how much needs to be substracted from ech cell
|
|
|
- // since this might be an uneven number we ceil
|
|
|
- var cellWidth = collectionView.bounds.size.width / numberOfCells
|
|
|
- cellWidth = cellWidth - ceil(((numberOfCells + 1) * cellPadding) / numberOfCells)
|
|
|
-
|
|
|
- return CGSize(width: cellWidth, height: cellWidth * aspectRatio)
|
|
|
+ override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
|
|
|
+ fatalError("this should only be used as subclass")
|
|
|
}
|
|
|
|
|
|
- func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
|
|
|
- return UIEdgeInsets(top: cellPadding, left: cellPadding, bottom: cellPadding, right: cellPadding)
|
|
|
+ override func configure(cell: VLCLabelCell, for indicatorInfo: IndicatorInfo) {
|
|
|
+ cell.iconLabel.text = indicatorInfo.title
|
|
|
}
|
|
|
|
|
|
- func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
|
|
|
- return cellPadding
|
|
|
+ override func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) {
|
|
|
+ super.updateIndicator(for: viewController, fromIndex: fromIndex, toIndex: toIndex, withProgressPercentage: progressPercentage, indexWasChanged: indexWasChanged)
|
|
|
}
|
|
|
-
|
|
|
- func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
|
|
|
- return cellPadding
|
|
|
+
|
|
|
+ override var preferredStatusBarStyle: UIStatusBarStyle {
|
|
|
+ return PresentationTheme.current.colors.statusBarStyle
|
|
|
}
|
|
|
}
|