123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512 |
- /*****************************************************************************
- * VLCDragAndDropManager.swift
- * VLC for iOS
- *****************************************************************************
- * Copyright (c) 2017 VideoLAN. All rights reserved.
- * $Id$
- *
- * Authors: Carola Nitz <caro # videolan.org>
- *
- * Refer to the COPYING file of the official project for license.
- *****************************************************************************/
- import MobileCoreServices
- import UIKit
- @available(iOS 11.0, *)
- struct DropError: Error {
- enum ErrorKind {
- case moveFileToDocuments
- case loadFileRepresentationFailed
- }
- let kind: ErrorKind
- }
- @available(iOS 11.0, *)
- protocol VLCDragAndDropManagerDelegate: NSObjectProtocol {
- func dragAndDropManagerRequestsFile(manager: NSObject, atIndexPath indexPath: IndexPath) -> Any?
- func dragAndDropManagerInsertItem(manager: NSObject, item: NSManagedObject, atIndexPath indexPath: IndexPath)
- func dragAndDropManagerDeleteItem(manager: NSObject, atIndexPath indexPath: IndexPath)
- func dragAndDropManagerRemoveFileFromFolder(manager: NSObject, file: NSManagedObject)
- func dragAndDropManagerCurrentSelection(manager: NSObject) -> AnyObject?
- }
- @available(iOS 11.0, *)
- 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
- // }
- }
|