|
@@ -33,478 +33,479 @@ protocol VLCDragAndDropManagerDelegate: NSObjectProtocol {
|
|
}
|
|
}
|
|
|
|
|
|
@available(iOS 11.0, *)
|
|
@available(iOS 11.0, *)
|
|
-class VLCDragAndDropManager<T>: NSObject, UICollectionViewDragDelegate, UITableViewDragDelegate, UICollectionViewDropDelegate, UITableViewDropDelegate, UIDropInteractionDelegate {
|
|
|
|
-
|
|
|
|
- let utiTypeIdentifiers: [String] = VLCDragAndDropManager.supportedTypeIdentifiers()
|
|
|
|
- var subcategory: VLCMediaSubcategoryModel<T>
|
|
|
|
- /// Returns the supported type identifiers that VLC can process.
|
|
|
|
- /// It fetches the identifiers in LSItemContentTypes from all the CFBundleDocumentTypes in the info.plist.
|
|
|
|
- /// Video, Audio and Subtitle formats
|
|
|
|
- ///
|
|
|
|
- /// - Returns: Array of UTITypeIdentifiers
|
|
|
|
- private class func supportedTypeIdentifiers() -> [String] {
|
|
|
|
- var typeIdentifiers: [String] = []
|
|
|
|
- if let documents = Bundle.main.infoDictionary?["CFBundleDocumentTypes"] as? [[String: Any]] {
|
|
|
|
- for item in documents {
|
|
|
|
- if let value = item["LSItemContentTypes"] as? [String] {
|
|
|
|
- typeIdentifiers.append(contentsOf: value)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return typeIdentifiers
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- @available(*, unavailable, message: "use init(category:)")
|
|
|
|
- override init() {
|
|
|
|
- fatalError()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- init(subcategory: VLCMediaSubcategoryModel<T>) {
|
|
|
|
- self.subcategory = subcategory
|
|
|
|
- super.init()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // MARK: - TableView
|
|
|
|
-
|
|
|
|
- func tableView(_ tableView: UITableView, canHandle session: UIDropSession) -> Bool {
|
|
|
|
- return canHandleDropSession(session: session)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- func tableView(_ tableView: UITableView, itemsForAddingTo session: UIDragSession, at indexPath: IndexPath, point: CGPoint) -> [UIDragItem] {
|
|
|
|
- return dragItems(forIndexPath: indexPath)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
|
|
|
- return dragItems(forIndexPath: indexPath)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal {
|
|
|
|
- let operation = dropOperation(hasActiveDrag: tableView.hasActiveDrag, firstSessionItem: session.items.first, withDestinationIndexPath: destinationIndexPath)
|
|
|
|
- return UITableViewDropProposal(operation: operation, intent: .insertIntoDestinationIndexPath)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) {
|
|
|
|
- let section = tableView.numberOfSections - 1
|
|
|
|
- let row = tableView.numberOfRows(inSection: section)
|
|
|
|
- let destinationPath = coordinator.destinationIndexPath ?? IndexPath(row: row, section: section)
|
|
|
|
-
|
|
|
|
- for item in coordinator.items {
|
|
|
|
- let itemProvider = item.dragItem.itemProvider
|
|
|
|
- // we're not gonna handle moving of folders
|
|
|
|
- if let sourceItem = item.dragItem.localObject, fileIsCollection(file: sourceItem as AnyObject) {
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if fileIsFolder(atIndexPath: destinationPath) { // handle dropping onto a folder
|
|
|
|
- addDragItem(tableView: tableView, dragItem: item, toFolderAt: destinationPath)
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if item.sourceIndexPath != nil { // element within VLC
|
|
|
|
- moveItem(tableView: tableView, item: item, toIndexPath: destinationPath)
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- // Element dragging from another App
|
|
|
|
- let placeholder = UITableViewDropPlaceholder(insertionIndexPath: destinationPath, reuseIdentifier: VLCPlaylistTableViewCell.cellIdentifier(), rowHeight: VLCPlaylistTableViewCell.heightOfCell())
|
|
|
|
- let placeholderContext = coordinator.drop(item.dragItem, to: placeholder)
|
|
|
|
- createFileWith(itemProvider: itemProvider) {
|
|
|
|
- [weak self] file, error in
|
|
|
|
-
|
|
|
|
- guard let strongSelf = self else { return }
|
|
|
|
-
|
|
|
|
- if let file = file {
|
|
|
|
- placeholderContext.commitInsertion() {
|
|
|
|
- insertionIndexPath in
|
|
|
|
- strongSelf.subcategory.dragAndDropManagerInsertItem(manager: strongSelf, item: file, atIndexPath: insertionIndexPath)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if let error = error as? DropError {
|
|
|
|
- strongSelf.handleError(error: error, itemProvider: item.dragItem.itemProvider)
|
|
|
|
- placeholderContext.deletePlaceholder()
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private func inFolder() -> Bool {
|
|
|
|
- return subcategory.dragAndDropManagerCurrentSelection(manager: self) as? MLLabel != nil
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private func moveItem(tableView: UITableView, item: UITableViewDropItem, toIndexPath destinationPath: IndexPath) {
|
|
|
|
- if let mlFile = item.dragItem.localObject as? MLFile, !mlFile.labels.isEmpty && !inFolder() {
|
|
|
|
- tableView.performBatchUpdates({
|
|
|
|
- tableView.insertRows(at: [destinationPath], with: .automatic)
|
|
|
|
- subcategory.dragAndDropManagerInsertItem(manager: self, item: mlFile, atIndexPath: destinationPath)
|
|
|
|
- subcategory.dragAndDropManagerRemoveFileFromFolder(manager: self, file: mlFile)
|
|
|
|
- }, completion: nil)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private func addDragItem(tableView: UITableView, dragItem item: UITableViewDropItem, toFolderAt index: IndexPath) {
|
|
|
|
- if let sourcepath = item.sourceIndexPath { // local file that just needs to be moved
|
|
|
|
- tableView.performBatchUpdates({
|
|
|
|
- if let file = subcategory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: sourcepath) as? MLFile {
|
|
|
|
- tableView.deleteRows(at: [sourcepath], with: .automatic)
|
|
|
|
- addFile(file: file, toFolderAt: index)
|
|
|
|
- subcategory.dragAndDropManagerDeleteItem(manager: self, atIndexPath: sourcepath)
|
|
|
|
- }
|
|
|
|
- }, completion: nil)
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- // file from other app
|
|
|
|
- createFileWith(itemProvider: item.dragItem.itemProvider) {
|
|
|
|
- [weak self] file, error in
|
|
|
|
-
|
|
|
|
- if let strongSelf = self, let file = file {
|
|
|
|
- strongSelf.addFile(file: file, toFolderAt: index)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // MARK: - Collectionview
|
|
|
|
-
|
|
|
|
- func collectionView(_ collectionView: UICollectionView, canHandle session: UIDropSession) -> Bool {
|
|
|
|
- return canHandleDropSession(session: session)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
|
|
|
- return dragItems(forIndexPath: indexPath)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- func collectionView(_ collectionView: UICollectionView, itemsForAddingTo session: UIDragSession, at indexPath: IndexPath, point: CGPoint) -> [UIDragItem] {
|
|
|
|
- return dragItems(forIndexPath: indexPath)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
|
|
|
|
- let operation = dropOperation(hasActiveDrag: collectionView.hasActiveDrag, firstSessionItem: session.items.first, withDestinationIndexPath: destinationIndexPath)
|
|
|
|
- return UICollectionViewDropProposal(operation: operation, intent: .insertIntoDestinationIndexPath)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
|
|
|
|
- let section = collectionView.numberOfSections - 1
|
|
|
|
- let row = collectionView.numberOfItems(inSection: section)
|
|
|
|
- let destinationPath = coordinator.destinationIndexPath ?? IndexPath(row: row, section: section)
|
|
|
|
-
|
|
|
|
- for item in coordinator.items {
|
|
|
|
- if let sourceItem = item.dragItem.localObject, fileIsCollection(file: sourceItem as AnyObject) { // We're not handling moving of Collection
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- if fileIsFolder(atIndexPath: destinationPath) { // handle dropping onto a folder
|
|
|
|
- addDragItem(collectionView: collectionView, dragItem: item, toFolderAt: destinationPath)
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- if item.sourceIndexPath != nil { // element within VLC
|
|
|
|
- moveItem(collectionView: collectionView, item: item, toIndexPath: destinationPath)
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- // Element from another App
|
|
|
|
- let placeholder = UICollectionViewDropPlaceholder(insertionIndexPath: destinationPath, reuseIdentifier: VLCPlaylistCollectionViewCell.cellIdentifier())
|
|
|
|
- let placeholderContext = coordinator.drop(item.dragItem, to: placeholder)
|
|
|
|
- createFileWith(itemProvider: item.dragItem.itemProvider) {
|
|
|
|
- [weak self] file, error in
|
|
|
|
-
|
|
|
|
- guard let strongSelf = self else { return }
|
|
|
|
-
|
|
|
|
- if let file = file {
|
|
|
|
- placeholderContext.commitInsertion() {
|
|
|
|
- insertionIndexPath in
|
|
|
|
- strongSelf.subcategory.dragAndDropManagerInsertItem(manager: strongSelf, item: file, atIndexPath: insertionIndexPath)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if let error = error as? DropError {
|
|
|
|
- strongSelf.handleError(error: error, itemProvider: item.dragItem.itemProvider)
|
|
|
|
- placeholderContext.deletePlaceholder()
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private func moveItem(collectionView: UICollectionView, item: UICollectionViewDropItem, toIndexPath destinationPath: IndexPath) {
|
|
|
|
- if let mlFile = item.dragItem.localObject as? MLFile, !mlFile.labels.isEmpty && !inFolder() {
|
|
|
|
- collectionView.performBatchUpdates({
|
|
|
|
- collectionView.insertItems(at: [destinationPath])
|
|
|
|
- subcategory.dragAndDropManagerInsertItem(manager: self, item: mlFile, atIndexPath: destinationPath)
|
|
|
|
- subcategory.dragAndDropManagerRemoveFileFromFolder(manager: self, file: mlFile)
|
|
|
|
- }, completion: nil)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private func addDragItem(collectionView: UICollectionView, dragItem item: UICollectionViewDropItem, toFolderAt index: IndexPath) {
|
|
|
|
- if let sourcepath = item.sourceIndexPath {
|
|
|
|
- // local file that just needs to be moved
|
|
|
|
- collectionView.performBatchUpdates({
|
|
|
|
- if let file = subcategory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: sourcepath) as? MLFile {
|
|
|
|
- collectionView.deleteItems(at: [sourcepath])
|
|
|
|
- addFile(file: file, toFolderAt: index)
|
|
|
|
- subcategory.dragAndDropManagerDeleteItem(manager: self, atIndexPath: sourcepath)
|
|
|
|
- }
|
|
|
|
- }, completion: nil)
|
|
|
|
- } else {
|
|
|
|
- // file from other app
|
|
|
|
- createFileWith(itemProvider: item.dragItem.itemProvider) {
|
|
|
|
- [weak self] file, error in
|
|
|
|
- if let strongSelf = self, let file = file {
|
|
|
|
- strongSelf.addFile(file: file, toFolderAt: index)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // MARK: - DropInteractionDelegate for EmptyView
|
|
|
|
-
|
|
|
|
- func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool {
|
|
|
|
- return canHandleDropSession(session: session)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
|
|
|
|
- return UIDropProposal(operation: .copy)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
|
|
|
|
- for item in session.items {
|
|
|
|
- createFileWith(itemProvider: item.itemProvider) {
|
|
|
|
- [weak self] _, error in
|
|
|
|
- if let error = error as? DropError {
|
|
|
|
- self?.handleError(error: error, itemProvider: item.itemProvider)
|
|
|
|
- }
|
|
|
|
- // no need to handle the file case since the libraryVC updates itself after getting a file
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // MARK: - Shared Methods
|
|
|
|
-
|
|
|
|
- // Checks if the session has items conforming to typeidentifiers
|
|
|
|
- private func canHandleDropSession(session: UIDropSession) -> Bool {
|
|
|
|
- if session.localDragSession != nil {
|
|
|
|
- return true
|
|
|
|
- }
|
|
|
|
- return session.hasItemsConforming(toTypeIdentifiers: utiTypeIdentifiers)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// Returns a drop operation type
|
|
|
|
- ///
|
|
|
|
- /// - Parameters:
|
|
|
|
- /// - hasActiveDrag: State if the drag started within the app
|
|
|
|
- /// - item: UIDragItem from session
|
|
|
|
- /// - Returns: UIDropOperation
|
|
|
|
- private func dropOperation(hasActiveDrag: Bool, firstSessionItem item: AnyObject?, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UIDropOperation {
|
|
|
|
- let inAlbum = subcategory.dragAndDropManagerCurrentSelection(manager: self) as? MLAlbum != nil
|
|
|
|
- let inShow = subcategory.dragAndDropManagerCurrentSelection(manager: self) as? MLShow != nil
|
|
|
|
- // you can move files into a folder or copy from anothr app into a folder
|
|
|
|
- if fileIsFolder(atIndexPath: destinationIndexPath) {
|
|
|
|
- // no dragging entire shows and albums into folders
|
|
|
|
- if let dragItem = item, let mlFile = dragItem.localObject as? MLFile, mlFile.isAlbumTrack() || mlFile.isShowEpisode() {
|
|
|
|
- return .forbidden
|
|
|
|
- }
|
|
|
|
- return hasActiveDrag ? .move : .copy
|
|
|
|
- }
|
|
|
|
- // you can't reorder
|
|
|
|
- if inFolder() {
|
|
|
|
- return hasActiveDrag ? .forbidden : .copy
|
|
|
|
- }
|
|
|
|
- // you can't reorder in or drag into an Album or Show
|
|
|
|
- if inAlbum || inShow {
|
|
|
|
- return .cancel
|
|
|
|
- }
|
|
|
|
- // we're dragging a file out of a folder
|
|
|
|
- if let dragItem = item, let mlFile = dragItem.localObject as? MLFile, !mlFile.labels.isEmpty {
|
|
|
|
- return .copy
|
|
|
|
- }
|
|
|
|
- // no reorder from another app into the top layer
|
|
|
|
- return hasActiveDrag ? .forbidden : .copy
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// show an Alert when dropping failed
|
|
|
|
- ///
|
|
|
|
- /// - Parameters:
|
|
|
|
- /// - error: the type of error that happend
|
|
|
|
- /// - itemProvider: the itemProvider to retrieve the suggestedName
|
|
|
|
- private func handleError(error: DropError, itemProvider: NSItemProvider) {
|
|
|
|
- let message: String
|
|
|
|
- let filename = itemProvider.suggestedName ?? NSLocalizedString("THIS_FILE", comment: "")
|
|
|
|
- switch error.kind {
|
|
|
|
- case .loadFileRepresentationFailed:
|
|
|
|
- message = String(format: NSLocalizedString("NOT_SUPPORTED_FILETYPE", comment: ""), filename)
|
|
|
|
- case .moveFileToDocuments:
|
|
|
|
- message = String(format: NSLocalizedString("FILE_EXISTS", comment: ""), filename)
|
|
|
|
- }
|
|
|
|
- let alert = UIAlertController(title: NSLocalizedString("ERROR", comment: ""), message: message, preferredStyle: .alert)
|
|
|
|
- alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
|
|
|
|
- UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private func fileIsFolder(atIndexPath indexPath: IndexPath?) -> Bool {
|
|
|
|
- if let indexPath = indexPath {
|
|
|
|
- let file = subcategory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: indexPath)
|
|
|
|
- return file as? MLLabel != nil
|
|
|
|
- }
|
|
|
|
- return false
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private func fileIsCollection(file: Any?) -> Bool {
|
|
|
|
- let isFolder = file as? MLLabel != nil
|
|
|
|
- let isAlbum = file as? MLAlbum != nil
|
|
|
|
- let isShow = file as? MLShow != nil
|
|
|
|
- return isFolder || isAlbum || isShow
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private func fileIsCollection(atIndexPath indexPath: IndexPath?) -> Bool {
|
|
|
|
- if let indexPath = indexPath {
|
|
|
|
- if let file = subcategory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: indexPath) {
|
|
|
|
- return fileIsCollection(file:file)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return false
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // creating dragItems for the file at indexpath
|
|
|
|
- private func dragItems(forIndexPath indexPath: IndexPath) -> [UIDragItem] {
|
|
|
|
- if let file = subcategory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: indexPath) {
|
|
|
|
- if fileIsCollection(atIndexPath: indexPath) {
|
|
|
|
- return dragItemsforCollection(file: file)
|
|
|
|
- }
|
|
|
|
- return dragItem(fromFile: file)
|
|
|
|
- }
|
|
|
|
- assert(false, "we can't generate a dragfile if the delegate can't return a file ")
|
|
|
|
- return []
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// Iterates over the items of a collection to create dragitems.
|
|
|
|
- /// Since we're not storing collections as folders we have to provide single files
|
|
|
|
- ///
|
|
|
|
- /// - Parameter file: Can be of type MLAlbum, MLLabel or MLShow
|
|
|
|
- /// - Returns: An array of UIDragItems
|
|
|
|
- private func dragItemsforCollection(file: Any) -> [UIDragItem] {
|
|
|
|
- var dragItems = [UIDragItem]()
|
|
|
|
- var set = Set<AnyHashable>()
|
|
|
|
- if let folder = file as? MLLabel {
|
|
|
|
- set = folder.files
|
|
|
|
- } else if let album = file as? MLAlbum {
|
|
|
|
- for track in album.tracks {
|
|
|
|
- if let mlfile = (track as? MLAlbumTrack)?.files.first {
|
|
|
|
- _ = set.insert(mlfile)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else if let show = file as? MLShow {
|
|
|
|
- for episode in show.episodes {
|
|
|
|
- if let mlfile = (episode as? MLShowEpisode)?.files {
|
|
|
|
- set = set.union(mlfile)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- assert(false, "can't get dragitems from a file that is not a collection")
|
|
|
|
- }
|
|
|
|
- for convertibleFile in set {
|
|
|
|
- if let mlfile = convertibleFile as? MLFile, let item = dragItem(fromFile: mlfile).first {
|
|
|
|
- dragItems.append(item)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return dragItems
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- //Provides an item for other applications
|
|
|
|
- private func dragItem(fromFile file: Any) -> [UIDragItem] {
|
|
|
|
- guard let file = mlFile(from: file as AnyObject), let path = file.url else {
|
|
|
|
- assert(false, "can't create a dragitem if there is no file or the file has no url")
|
|
|
|
- return []
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- let data = try? Data(contentsOf: path, options: .mappedIfSafe)
|
|
|
|
- let itemProvider = NSItemProvider()
|
|
|
|
- itemProvider.suggestedName = path.lastPathComponent
|
|
|
|
- // maybe use UTTypeForFileURL
|
|
|
|
- if let identifiers = try? path.resourceValues(forKeys: [.typeIdentifierKey]), let identifier = identifiers.typeIdentifier {
|
|
|
|
- // here we can show progress
|
|
|
|
- itemProvider.registerDataRepresentation(forTypeIdentifier: identifier, visibility: .all) { completion -> Progress? in
|
|
|
|
- completion(data, nil)
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- let dragitem = UIDragItem(itemProvider: itemProvider)
|
|
|
|
- dragitem.localObject = file
|
|
|
|
- return [dragitem]
|
|
|
|
- }
|
|
|
|
- assert(false, "we can't provide a typeidentifier")
|
|
|
|
- return []
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private func mlFile(from file: AnyObject) -> MLFile? {
|
|
|
|
- if let episode = file as? MLShowEpisode, let convertedfile = episode.files.first as? MLFile {
|
|
|
|
- return convertedfile
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if let track = file as? MLAlbumTrack, let convertedfile = track.files.first as? MLFile {
|
|
|
|
- return convertedfile
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if let convertedfile = file as? MLFile {
|
|
|
|
- return convertedfile
|
|
|
|
- }
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private func addFile(file: MLFile, toFolderAt folderIndex: IndexPath) {
|
|
|
|
- let label = subcategory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: folderIndex) as! MLLabel
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
- _ = label.files.insert(file)
|
|
|
|
- file.labels = [label]
|
|
|
|
- file.folderTrackNumber = NSNumber(integerLiteral: label.files.count - 1)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// try to create a file from the dropped item
|
|
|
|
- ///
|
|
|
|
- /// - Parameters:
|
|
|
|
- /// - itemProvider: itemprovider which is used to load the files from
|
|
|
|
- /// - completion: callback with the successfully created file or error if it failed
|
|
|
|
- private func createFileWith(itemProvider: NSItemProvider, completion: @escaping ((MLFile?, Error?) -> Void)) {
|
|
|
|
- itemProvider.loadFileRepresentation(forTypeIdentifier: kUTTypeData as String) {
|
|
|
|
- [weak self] (url, error) in
|
|
|
|
- guard let strongSelf = self else { return }
|
|
|
|
-
|
|
|
|
- guard let url = url else {
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
- completion(nil, DropError(kind: .loadFileRepresentationFailed))
|
|
|
|
- }
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- // returns nil for local session but this should also not be called for a local session
|
|
|
|
- guard let destinationURL = strongSelf.moveFileToDocuments(fromURL: url) else {
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
- completion(nil, DropError(kind: .moveFileToDocuments))
|
|
|
|
- }
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- DispatchQueue.global(qos: .background).async {
|
|
|
|
- let sharedlib = MLMediaLibrary.sharedMediaLibrary() as? MLMediaLibrary
|
|
|
|
- sharedlib?.addFilePaths([destinationURL.path])
|
|
|
|
-
|
|
|
|
- if let file = MLFile.file(for: destinationURL).first as? MLFile {
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
- // we dragged into a folder
|
|
|
|
- if let selection = strongSelf.subcategory.dragAndDropManagerCurrentSelection(manager: strongSelf) as? MLLabel {
|
|
|
|
- file.labels = [selection]
|
|
|
|
- }
|
|
|
|
- completion(file, nil)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private func moveFileToDocuments(fromURL filepath: URL?) -> URL? {
|
|
|
|
- let searchPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
|
|
|
|
- let newDirectoryPath = searchPaths.first
|
|
|
|
- guard let directoryPath = newDirectoryPath, let url = filepath else {
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- let destinationURL = URL(fileURLWithPath: "\(directoryPath)" + "/" + "\(url.lastPathComponent)")
|
|
|
|
- do {
|
|
|
|
- try FileManager.default.moveItem(at: url, to: destinationURL)
|
|
|
|
- } catch let error {
|
|
|
|
- print(error.localizedDescription)
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- return destinationURL
|
|
|
|
- }
|
|
|
|
|
|
+class VLCDragAndDropManager<ModelType>: NSObject {
|
|
|
|
+//, UICollectionViewDragDelegate, UITableViewDragDelegate, UICollectionViewDropDelegate, UITableViewDropDelegate, UIDropInteractionDelegate {
|
|
|
|
+
|
|
|
|
+// let utiTypeIdentifiers: [String] = VLCDragAndDropManager.supportedTypeIdentifiers()
|
|
|
|
+// var cateory: ModelType
|
|
|
|
+// /// Returns the supported type identifiers that VLC can process.
|
|
|
|
+// /// It fetches the identifiers in LSItemContentTypes from all the CFBundleDocumentTypes in the info.plist.
|
|
|
|
+// /// Video, Audio and Subtitle formats
|
|
|
|
+// ///
|
|
|
|
+// /// - Returns: Array of UTITypeIdentifiers
|
|
|
|
+// private class func supportedTypeIdentifiers() -> [String] {
|
|
|
|
+// var typeIdentifiers: [String] = []
|
|
|
|
+// if let documents = Bundle.main.infoDictionary?["CFBundleDocumentTypes"] as? [[String: Any]] {
|
|
|
|
+// for item in documents {
|
|
|
|
+// if let value = item["LSItemContentTypes"] as? [String] {
|
|
|
|
+// typeIdentifiers.append(contentsOf: value)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// return typeIdentifiers
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// @available(*, unavailable, message: "use init(category:)")
|
|
|
|
+// override init() {
|
|
|
|
+// fatalError()
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// init(cateory: ModelType) {
|
|
|
|
+// self.cateory = cateory
|
|
|
|
+// super.init()
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// // MARK: - TableView
|
|
|
|
+//
|
|
|
|
+// func tableView(_ tableView: UITableView, canHandle session: UIDropSession) -> Bool {
|
|
|
|
+// return canHandleDropSession(session: session)
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// func tableView(_ tableView: UITableView, itemsForAddingTo session: UIDragSession, at indexPath: IndexPath, point: CGPoint) -> [UIDragItem] {
|
|
|
|
+// return dragItems(forIndexPath: indexPath)
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
|
|
|
+// return dragItems(forIndexPath: indexPath)
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal {
|
|
|
|
+// let operation = dropOperation(hasActiveDrag: tableView.hasActiveDrag, firstSessionItem: session.items.first, withDestinationIndexPath: destinationIndexPath)
|
|
|
|
+// return UITableViewDropProposal(operation: operation, intent: .insertIntoDestinationIndexPath)
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) {
|
|
|
|
+// let section = tableView.numberOfSections - 1
|
|
|
|
+// let row = tableView.numberOfRows(inSection: section)
|
|
|
|
+// let destinationPath = coordinator.destinationIndexPath ?? IndexPath(row: row, section: section)
|
|
|
|
+//
|
|
|
|
+// for item in coordinator.items {
|
|
|
|
+// let itemProvider = item.dragItem.itemProvider
|
|
|
|
+// // we're not gonna handle moving of folders
|
|
|
|
+// if let sourceItem = item.dragItem.localObject, fileIsCollection(file: sourceItem as AnyObject) {
|
|
|
|
+// continue
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// if fileIsFolder(atIndexPath: destinationPath) { // handle dropping onto a folder
|
|
|
|
+// addDragItem(tableView: tableView, dragItem: item, toFolderAt: destinationPath)
|
|
|
|
+// continue
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// if item.sourceIndexPath != nil { // element within VLC
|
|
|
|
+// moveItem(tableView: tableView, item: item, toIndexPath: destinationPath)
|
|
|
|
+// continue
|
|
|
|
+// }
|
|
|
|
+// // Element dragging from another App
|
|
|
|
+// let placeholder = UITableViewDropPlaceholder(insertionIndexPath: destinationPath, reuseIdentifier: VLCPlaylistTableViewCell.cellIdentifier(), rowHeight: VLCPlaylistTableViewCell.heightOfCell())
|
|
|
|
+// let placeholderContext = coordinator.drop(item.dragItem, to: placeholder)
|
|
|
|
+// createFileWith(itemProvider: itemProvider) {
|
|
|
|
+// [weak self] file, error in
|
|
|
|
+//
|
|
|
|
+// guard let strongSelf = self else { return }
|
|
|
|
+//
|
|
|
|
+// if let file = file {
|
|
|
|
+// placeholderContext.commitInsertion() {
|
|
|
|
+// insertionIndexPath in
|
|
|
|
+// strongSelf.cateory.dragAndDropManagerInsertItem(manager: strongSelf, item: file, atIndexPath: insertionIndexPath)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// if let error = error as? DropError {
|
|
|
|
+// strongSelf.handleError(error: error, itemProvider: item.dragItem.itemProvider)
|
|
|
|
+// placeholderContext.deletePlaceholder()
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// private func inFolder() -> Bool {
|
|
|
|
+// return cateory.dragAndDropManagerCurrentSelection(manager: self) as? MLLabel != nil
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// private func moveItem(tableView: UITableView, item: UITableViewDropItem, toIndexPath destinationPath: IndexPath) {
|
|
|
|
+// if let mlFile = item.dragItem.localObject as? MLFile, !mlFile.labels.isEmpty && !inFolder() {
|
|
|
|
+// tableView.performBatchUpdates({
|
|
|
|
+// tableView.insertRows(at: [destinationPath], with: .automatic)
|
|
|
|
+// cateory.dragAndDropManagerInsertItem(manager: self, item: mlFile, atIndexPath: destinationPath)
|
|
|
|
+// cateory.dragAndDropManagerRemoveFileFromFolder(manager: self, file: mlFile)
|
|
|
|
+// }, completion: nil)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// private func addDragItem(tableView: UITableView, dragItem item: UITableViewDropItem, toFolderAt index: IndexPath) {
|
|
|
|
+// if let sourcepath = item.sourceIndexPath { // local file that just needs to be moved
|
|
|
|
+// tableView.performBatchUpdates({
|
|
|
|
+// if let file = cateory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: sourcepath) as? MLFile {
|
|
|
|
+// tableView.deleteRows(at: [sourcepath], with: .automatic)
|
|
|
|
+// addFile(file: file, toFolderAt: index)
|
|
|
|
+// cateory.dragAndDropManagerDeleteItem(manager: self, atIndexPath: sourcepath)
|
|
|
|
+// }
|
|
|
|
+// }, completion: nil)
|
|
|
|
+// return
|
|
|
|
+// }
|
|
|
|
+// // file from other app
|
|
|
|
+// createFileWith(itemProvider: item.dragItem.itemProvider) {
|
|
|
|
+// [weak self] file, error in
|
|
|
|
+//
|
|
|
|
+// if let strongSelf = self, let file = file {
|
|
|
|
+// strongSelf.addFile(file: file, toFolderAt: index)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// // MARK: - Collectionview
|
|
|
|
+//
|
|
|
|
+// func collectionView(_ collectionView: UICollectionView, canHandle session: UIDropSession) -> Bool {
|
|
|
|
+// return canHandleDropSession(session: session)
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
|
|
|
+// return dragItems(forIndexPath: indexPath)
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// func collectionView(_ collectionView: UICollectionView, itemsForAddingTo session: UIDragSession, at indexPath: IndexPath, point: CGPoint) -> [UIDragItem] {
|
|
|
|
+// return dragItems(forIndexPath: indexPath)
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
|
|
|
|
+// let operation = dropOperation(hasActiveDrag: collectionView.hasActiveDrag, firstSessionItem: session.items.first, withDestinationIndexPath: destinationIndexPath)
|
|
|
|
+// return UICollectionViewDropProposal(operation: operation, intent: .insertIntoDestinationIndexPath)
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
|
|
|
|
+// let section = collectionView.numberOfSections - 1
|
|
|
|
+// let row = collectionView.numberOfItems(inSection: section)
|
|
|
|
+// let destinationPath = coordinator.destinationIndexPath ?? IndexPath(row: row, section: section)
|
|
|
|
+//
|
|
|
|
+// for item in coordinator.items {
|
|
|
|
+// if let sourceItem = item.dragItem.localObject, fileIsCollection(file: sourceItem as AnyObject) { // We're not handling moving of Collection
|
|
|
|
+// continue
|
|
|
|
+// }
|
|
|
|
+// if fileIsFolder(atIndexPath: destinationPath) { // handle dropping onto a folder
|
|
|
|
+// addDragItem(collectionView: collectionView, dragItem: item, toFolderAt: destinationPath)
|
|
|
|
+// continue
|
|
|
|
+// }
|
|
|
|
+// if item.sourceIndexPath != nil { // element within VLC
|
|
|
|
+// moveItem(collectionView: collectionView, item: item, toIndexPath: destinationPath)
|
|
|
|
+// continue
|
|
|
|
+// }
|
|
|
|
+// // Element from another App
|
|
|
|
+// let placeholder = UICollectionViewDropPlaceholder(insertionIndexPath: destinationPath, reuseIdentifier: VLCPlaylistCollectionViewCell.cellIdentifier())
|
|
|
|
+// let placeholderContext = coordinator.drop(item.dragItem, to: placeholder)
|
|
|
|
+// createFileWith(itemProvider: item.dragItem.itemProvider) {
|
|
|
|
+// [weak self] file, error in
|
|
|
|
+//
|
|
|
|
+// guard let strongSelf = self else { return }
|
|
|
|
+//
|
|
|
|
+// if let file = file {
|
|
|
|
+// placeholderContext.commitInsertion() {
|
|
|
|
+// insertionIndexPath in
|
|
|
|
+// strongSelf.cateory.dragAndDropManagerInsertItem(manager: strongSelf, item: file, atIndexPath: insertionIndexPath)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// if let error = error as? DropError {
|
|
|
|
+// strongSelf.handleError(error: error, itemProvider: item.dragItem.itemProvider)
|
|
|
|
+// placeholderContext.deletePlaceholder()
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// private func moveItem(collectionView: UICollectionView, item: UICollectionViewDropItem, toIndexPath destinationPath: IndexPath) {
|
|
|
|
+// if let mlFile = item.dragItem.localObject as? MLFile, !mlFile.labels.isEmpty && !inFolder() {
|
|
|
|
+// collectionView.performBatchUpdates({
|
|
|
|
+// collectionView.insertItems(at: [destinationPath])
|
|
|
|
+// cateory.dragAndDropManagerInsertItem(manager: self, item: mlFile, atIndexPath: destinationPath)
|
|
|
|
+// cateory.dragAndDropManagerRemoveFileFromFolder(manager: self, file: mlFile)
|
|
|
|
+// }, completion: nil)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// private func addDragItem(collectionView: UICollectionView, dragItem item: UICollectionViewDropItem, toFolderAt index: IndexPath) {
|
|
|
|
+// if let sourcepath = item.sourceIndexPath {
|
|
|
|
+// // local file that just needs to be moved
|
|
|
|
+// collectionView.performBatchUpdates({
|
|
|
|
+// if let file = cateory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: sourcepath) as? MLFile {
|
|
|
|
+// collectionView.deleteItems(at: [sourcepath])
|
|
|
|
+// addFile(file: file, toFolderAt: index)
|
|
|
|
+// cateory.dragAndDropManagerDeleteItem(manager: self, atIndexPath: sourcepath)
|
|
|
|
+// }
|
|
|
|
+// }, completion: nil)
|
|
|
|
+// } else {
|
|
|
|
+// // file from other app
|
|
|
|
+// createFileWith(itemProvider: item.dragItem.itemProvider) {
|
|
|
|
+// [weak self] file, error in
|
|
|
|
+// if let strongSelf = self, let file = file {
|
|
|
|
+// strongSelf.addFile(file: file, toFolderAt: index)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// // MARK: - DropInteractionDelegate for EmptyView
|
|
|
|
+//
|
|
|
|
+// func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool {
|
|
|
|
+// return canHandleDropSession(session: session)
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
|
|
|
|
+// return UIDropProposal(operation: .copy)
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
|
|
|
|
+// for item in session.items {
|
|
|
|
+// createFileWith(itemProvider: item.itemProvider) {
|
|
|
|
+// [weak self] _, error in
|
|
|
|
+// if let error = error as? DropError {
|
|
|
|
+// self?.handleError(error: error, itemProvider: item.itemProvider)
|
|
|
|
+// }
|
|
|
|
+// // no need to handle the file case since the libraryVC updates itself after getting a file
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// // MARK: - Shared Methods
|
|
|
|
+//
|
|
|
|
+// // Checks if the session has items conforming to typeidentifiers
|
|
|
|
+// private func canHandleDropSession(session: UIDropSession) -> Bool {
|
|
|
|
+// if session.localDragSession != nil {
|
|
|
|
+// return true
|
|
|
|
+// }
|
|
|
|
+// return session.hasItemsConforming(toTypeIdentifiers: utiTypeIdentifiers)
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// /// Returns a drop operation type
|
|
|
|
+// ///
|
|
|
|
+// /// - Parameters:
|
|
|
|
+// /// - hasActiveDrag: State if the drag started within the app
|
|
|
|
+// /// - item: UIDragItem from session
|
|
|
|
+// /// - Returns: UIDropOperation
|
|
|
|
+// private func dropOperation(hasActiveDrag: Bool, firstSessionItem item: AnyObject?, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UIDropOperation {
|
|
|
|
+// let inAlbum = cateory.dragAndDropManagerCurrentSelection(manager: self) as? MLAlbum != nil
|
|
|
|
+// let inShow = cateory.dragAndDropManagerCurrentSelection(manager: self) as? MLShow != nil
|
|
|
|
+// // you can move files into a folder or copy from anothr app into a folder
|
|
|
|
+// if fileIsFolder(atIndexPath: destinationIndexPath) {
|
|
|
|
+// // no dragging entire shows and albums into folders
|
|
|
|
+// if let dragItem = item, let mlFile = dragItem.localObject as? MLFile, mlFile.isAlbumTrack() || mlFile.isShowEpisode() {
|
|
|
|
+// return .forbidden
|
|
|
|
+// }
|
|
|
|
+// return hasActiveDrag ? .move : .copy
|
|
|
|
+// }
|
|
|
|
+// // you can't reorder
|
|
|
|
+// if inFolder() {
|
|
|
|
+// return hasActiveDrag ? .forbidden : .copy
|
|
|
|
+// }
|
|
|
|
+// // you can't reorder in or drag into an Album or Show
|
|
|
|
+// if inAlbum || inShow {
|
|
|
|
+// return .cancel
|
|
|
|
+// }
|
|
|
|
+// // we're dragging a file out of a folder
|
|
|
|
+// if let dragItem = item, let mlFile = dragItem.localObject as? MLFile, !mlFile.labels.isEmpty {
|
|
|
|
+// return .copy
|
|
|
|
+// }
|
|
|
|
+// // no reorder from another app into the top layer
|
|
|
|
+// return hasActiveDrag ? .forbidden : .copy
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// /// show an Alert when dropping failed
|
|
|
|
+// ///
|
|
|
|
+// /// - Parameters:
|
|
|
|
+// /// - error: the type of error that happend
|
|
|
|
+// /// - itemProvider: the itemProvider to retrieve the suggestedName
|
|
|
|
+// private func handleError(error: DropError, itemProvider: NSItemProvider) {
|
|
|
|
+// let message: String
|
|
|
|
+// let filename = itemProvider.suggestedName ?? NSLocalizedString("THIS_FILE", comment: "")
|
|
|
|
+// switch error.kind {
|
|
|
|
+// case .loadFileRepresentationFailed:
|
|
|
|
+// message = String(format: NSLocalizedString("NOT_SUPPORTED_FILETYPE", comment: ""), filename)
|
|
|
|
+// case .moveFileToDocuments:
|
|
|
|
+// message = String(format: NSLocalizedString("FILE_EXISTS", comment: ""), filename)
|
|
|
|
+// }
|
|
|
|
+// let alert = UIAlertController(title: NSLocalizedString("ERROR", comment: ""), message: message, preferredStyle: .alert)
|
|
|
|
+// alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
|
|
|
|
+// UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// private func fileIsFolder(atIndexPath indexPath: IndexPath?) -> Bool {
|
|
|
|
+// if let indexPath = indexPath {
|
|
|
|
+// let file = cateory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: indexPath)
|
|
|
|
+// return file as? MLLabel != nil
|
|
|
|
+// }
|
|
|
|
+// return false
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// private func fileIsCollection(file: Any?) -> Bool {
|
|
|
|
+// let isFolder = file as? MLLabel != nil
|
|
|
|
+// let isAlbum = file as? MLAlbum != nil
|
|
|
|
+// let isShow = file as? MLShow != nil
|
|
|
|
+// return isFolder || isAlbum || isShow
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// private func fileIsCollection(atIndexPath indexPath: IndexPath?) -> Bool {
|
|
|
|
+// if let indexPath = indexPath {
|
|
|
|
+// if let file = cateory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: indexPath) {
|
|
|
|
+// return fileIsCollection(file:file)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// return false
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// // creating dragItems for the file at indexpath
|
|
|
|
+// private func dragItems(forIndexPath indexPath: IndexPath) -> [UIDragItem] {
|
|
|
|
+// if let file = cateory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: indexPath) {
|
|
|
|
+// if fileIsCollection(atIndexPath: indexPath) {
|
|
|
|
+// return dragItemsforCollection(file: file)
|
|
|
|
+// }
|
|
|
|
+// return dragItem(fromFile: file)
|
|
|
|
+// }
|
|
|
|
+// assert(false, "we can't generate a dragfile if the delegate can't return a file ")
|
|
|
|
+// return []
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// /// Iterates over the items of a collection to create dragitems.
|
|
|
|
+// /// Since we're not storing collections as folders we have to provide single files
|
|
|
|
+// ///
|
|
|
|
+// /// - Parameter file: Can be of type MLAlbum, MLLabel or MLShow
|
|
|
|
+// /// - Returns: An array of UIDragItems
|
|
|
|
+// private func dragItemsforCollection(file: Any) -> [UIDragItem] {
|
|
|
|
+// var dragItems = [UIDragItem]()
|
|
|
|
+// var set = Set<AnyHashable>()
|
|
|
|
+// if let folder = file as? MLLabel {
|
|
|
|
+// set = folder.files
|
|
|
|
+// } else if let album = file as? MLAlbum {
|
|
|
|
+// for track in album.tracks {
|
|
|
|
+// if let mlfile = (track as? MLAlbumTrack)?.files.first {
|
|
|
|
+// _ = set.insert(mlfile)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// } else if let show = file as? MLShow {
|
|
|
|
+// for episode in show.episodes {
|
|
|
|
+// if let mlfile = (episode as? MLShowEpisode)?.files {
|
|
|
|
+// set = set.union(mlfile)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// } else {
|
|
|
|
+// assert(false, "can't get dragitems from a file that is not a collection")
|
|
|
|
+// }
|
|
|
|
+// for convertibleFile in set {
|
|
|
|
+// if let mlfile = convertibleFile as? MLFile, let item = dragItem(fromFile: mlfile).first {
|
|
|
|
+// dragItems.append(item)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// return dragItems
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// //Provides an item for other applications
|
|
|
|
+// private func dragItem(fromFile file: Any) -> [UIDragItem] {
|
|
|
|
+// guard let file = mlFile(from: file as AnyObject), let path = file.url else {
|
|
|
|
+// assert(false, "can't create a dragitem if there is no file or the file has no url")
|
|
|
|
+// return []
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// let data = try? Data(contentsOf: path, options: .mappedIfSafe)
|
|
|
|
+// let itemProvider = NSItemProvider()
|
|
|
|
+// itemProvider.suggestedName = path.lastPathComponent
|
|
|
|
+// // maybe use UTTypeForFileURL
|
|
|
|
+// if let identifiers = try? path.resourceValues(forKeys: [.typeIdentifierKey]), let identifier = identifiers.typeIdentifier {
|
|
|
|
+// // here we can show progress
|
|
|
|
+// itemProvider.registerDataRepresentation(forTypeIdentifier: identifier, visibility: .all) { completion -> Progress? in
|
|
|
|
+// completion(data, nil)
|
|
|
|
+// return nil
|
|
|
|
+// }
|
|
|
|
+// let dragitem = UIDragItem(itemProvider: itemProvider)
|
|
|
|
+// dragitem.localObject = file
|
|
|
|
+// return [dragitem]
|
|
|
|
+// }
|
|
|
|
+// assert(false, "we can't provide a typeidentifier")
|
|
|
|
+// return []
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// private func mlFile(from file: AnyObject) -> MLFile? {
|
|
|
|
+// if let episode = file as? MLShowEpisode, let convertedfile = episode.files.first as? MLFile {
|
|
|
|
+// return convertedfile
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// if let track = file as? MLAlbumTrack, let convertedfile = track.files.first as? MLFile {
|
|
|
|
+// return convertedfile
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// if let convertedfile = file as? MLFile {
|
|
|
|
+// return convertedfile
|
|
|
|
+// }
|
|
|
|
+// return nil
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// private func addFile(file: MLFile, toFolderAt folderIndex: IndexPath) {
|
|
|
|
+// let label = cateory.dragAndDropManagerRequestsFile(manager: self, atIndexPath: folderIndex) as! MLLabel
|
|
|
|
+// DispatchQueue.main.async {
|
|
|
|
+// _ = label.files.insert(file)
|
|
|
|
+// file.labels = [label]
|
|
|
|
+// file.folderTrackNumber = NSNumber(integerLiteral: label.files.count - 1)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// /// try to create a file from the dropped item
|
|
|
|
+// ///
|
|
|
|
+// /// - Parameters:
|
|
|
|
+// /// - itemProvider: itemprovider which is used to load the files from
|
|
|
|
+// /// - completion: callback with the successfully created file or error if it failed
|
|
|
|
+// private func createFileWith(itemProvider: NSItemProvider, completion: @escaping ((MLFile?, Error?) -> Void)) {
|
|
|
|
+// itemProvider.loadFileRepresentation(forTypeIdentifier: kUTTypeData as String) {
|
|
|
|
+// [weak self] (url, error) in
|
|
|
|
+// guard let strongSelf = self else { return }
|
|
|
|
+//
|
|
|
|
+// guard let url = url else {
|
|
|
|
+// DispatchQueue.main.async {
|
|
|
|
+// completion(nil, DropError(kind: .loadFileRepresentationFailed))
|
|
|
|
+// }
|
|
|
|
+// return
|
|
|
|
+// }
|
|
|
|
+// // returns nil for local session but this should also not be called for a local session
|
|
|
|
+// guard let destinationURL = strongSelf.moveFileToDocuments(fromURL: url) else {
|
|
|
|
+// DispatchQueue.main.async {
|
|
|
|
+// completion(nil, DropError(kind: .moveFileToDocuments))
|
|
|
|
+// }
|
|
|
|
+// return
|
|
|
|
+// }
|
|
|
|
+// DispatchQueue.global(qos: .background).async {
|
|
|
|
+// let sharedlib = MLMediaLibrary.sharedMediaLibrary() as? MLMediaLibrary
|
|
|
|
+// sharedlib?.addFilePaths([destinationURL.path])
|
|
|
|
+//
|
|
|
|
+// if let file = MLFile.file(for: destinationURL).first as? MLFile {
|
|
|
|
+// DispatchQueue.main.async {
|
|
|
|
+// // we dragged into a folder
|
|
|
|
+// if let selection = strongSelf.cateory.dragAndDropManagerCurrentSelection(manager: strongSelf) as? MLLabel {
|
|
|
|
+// file.labels = [selection]
|
|
|
|
+// }
|
|
|
|
+// completion(file, nil)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// private func moveFileToDocuments(fromURL filepath: URL?) -> URL? {
|
|
|
|
+// let searchPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
|
|
|
|
+// let newDirectoryPath = searchPaths.first
|
|
|
|
+// guard let directoryPath = newDirectoryPath, let url = filepath else {
|
|
|
|
+// return nil
|
|
|
|
+// }
|
|
|
|
+// let destinationURL = URL(fileURLWithPath: "\(directoryPath)" + "/" + "\(url.lastPathComponent)")
|
|
|
|
+// do {
|
|
|
|
+// try FileManager.default.moveItem(at: url, to: destinationURL)
|
|
|
|
+// } catch let error {
|
|
|
|
+// print(error.localizedDescription)
|
|
|
|
+// return nil
|
|
|
|
+// }
|
|
|
|
+// return destinationURL
|
|
|
|
+// }
|
|
}
|
|
}
|