Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
VLC-iOS
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Sergey
VLC-iOS
Commits
ea54eeb7
Commit
ea54eeb7
authored
Nov 26, 2017
by
Carola Nitz
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial Drag and Drop support
parent
e53f95d4
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
665 additions
and
16 deletions
+665
-16
Resources/VLCStringsForLocalization.m
Resources/VLCStringsForLocalization.m
+4
-0
Resources/en.lproj/Localizable.strings
Resources/en.lproj/Localizable.strings
+6
-0
SharedSources/VLCDragAndDropManager.swift
SharedSources/VLCDragAndDropManager.swift
+500
-0
Sources/VLCLibraryViewController.m
Sources/VLCLibraryViewController.m
+43
-12
Sources/VLCMediaDataSource.h
Sources/VLCMediaDataSource.h
+2
-1
Sources/VLCMediaDataSource.m
Sources/VLCMediaDataSource.m
+9
-2
Sources/VLCPlaylistCollectionViewCell.m
Sources/VLCPlaylistCollectionViewCell.m
+16
-1
Sources/VLCPlaylistTableViewCell.h
Sources/VLCPlaylistTableViewCell.h
+1
-0
VLC-iOS-Bridging-Header.h
VLC-iOS-Bridging-Header.h
+8
-0
VLC.xcodeproj/project.pbxproj
VLC.xcodeproj/project.pbxproj
+31
-0
VLCMediaData+VLCDragAndDrop.swift
VLCMediaData+VLCDragAndDrop.swift
+45
-0
No files found.
Resources/VLCStringsForLocalization.m
View file @
ea54eeb7
...
...
@@ -17,4 +17,8 @@ NSLocalizedString(@"STORE_DESCRIPTION", nil);
NSLocalizedString
(
@"STORE_DESCRIPTION_TV"
,
nil
);
NSLocalizedString
(
@"ABOUT_APP"
,
nil
);
NSLocalizedString
(
@"SECTION_HEADER_NETWORK"
,
nil
);
NSLocalizedString
(
@"THIS_FILE"
,
nil
);
NSLocalizedString
(
@"NOT_SUPPORTED_FILETYPE"
,
nil
);
NSLocalizedString
(
@"FILE_EXISTS"
,
nil
);
}
Resources/en.lproj/Localizable.strings
View file @
ea54eeb7
...
...
@@ -324,6 +324,12 @@
"DELETE_MESSAGE" = "Confirm the deletion of the selected files";
"DELETE_TITLE" = "Delete Selected Files";
//Drag and Drop
"THIS_FILE" = "This file";
"NOT_SUPPORTED_FILETYPE" = "%@ is not a supported filetype by VLC";
"FILE_EXISTS" = "%@ already exists";
/* New strings */
"PROTOCOL_NOT_SELECTED" = "PROTOCOL_NOT_SELECTED";
"Settings" = "Settings";
SharedSources/VLCDragAndDropManager.swift
0 → 100644
View file @
ea54eeb7
/*****************************************************************************
* 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
UIKit
import
MobileCoreServices
@available
(
iOS
11.0
,
*
)
struct
DropError
:
Error
{
enum
ErrorKind
{
case
moveFileToDocuments
case
loadFileRepresentationFailed
}
let
kind
:
ErrorKind
}
@available
(
iOS
11.0
,
*
)
@objc
protocol
VLCDragAndDropManagerDelegate
:
NSObjectProtocol
{
func
dragAndDropManagerRequestsFile
(
manager
:
VLCDragAndDropManager
,
atIndexPath
indexPath
:
IndexPath
)
->
AnyObject
?
func
dragAndDropManagerInsertItem
(
manager
:
VLCDragAndDropManager
,
item
:
NSManagedObject
,
atIndexPath
indexPath
:
IndexPath
)
func
dragAndDropManagerDeleteItem
(
manager
:
VLCDragAndDropManager
,
atIndexPath
indexPath
:
IndexPath
)
func
dragAndDropManagerRemoveFileFromFolder
(
manager
:
VLCDragAndDropManager
,
file
:
NSManagedObject
)
func
dragAndDropManagerCurrentSelection
(
manager
:
VLCDragAndDropManager
)
->
AnyObject
?
}
@available
(
iOS
11.0
,
*
)
class
VLCDragAndDropManager
:
NSObject
,
UICollectionViewDragDelegate
,
UITableViewDragDelegate
,
UICollectionViewDropDelegate
,
UITableViewDropDelegate
,
UIDropInteractionDelegate
{
@objc
weak
var
delegate
:
VLCDragAndDropManagerDelegate
?
lazy
var
utiTypeIdentifiers
:[
String
]
=
{
self
.
supportedTypeIdentifiers
()
}()
/// 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
func
supportedTypeIdentifiers
()
->
[
String
]
{
var
typeIdentifiers
:[
String
]
=
[]
let
documentTypes
=
Bundle
.
main
.
object
(
forInfoDictionaryKey
:
"CFBundleDocumentTypes"
)
if
let
documents
=
documentTypes
as?
Array
<
AnyObject
>
{
for
item
in
documents
{
if
let
object
=
item
as?
[
NSString
:
Any
]
{
for
(
key
,
value
)
in
object
where
key
==
"LSItemContentTypes"
{
typeIdentifiers
.
append
(
contentsOf
:
value
as!
Array
)
}
}
}
}
return
typeIdentifiers
}
//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
,
firtSessionItem
:
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
:
"PlaylistCell"
,
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
.
delegate
?
.
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
delegate
?
.
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
.
count
>
0
&&
!
inFolder
()
{
tableView
.
performBatchUpdates
({
tableView
.
insertRows
(
at
:
[
destinationPath
],
with
:
.
automatic
)
delegate
?
.
dragAndDropManagerInsertItem
(
manager
:
self
,
item
:
mlFile
,
atIndexPath
:
destinationPath
)
delegate
?
.
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
({
tableView
.
deleteRows
(
at
:
[
sourcepath
],
with
:
.
automatic
)
if
let
file
=
delegate
?
.
dragAndDropManagerRequestsFile
(
manager
:
self
,
atIndexPath
:
sourcepath
)
as?
MLFile
{
addFile
(
file
:
file
,
toFolderAt
:
index
)
delegate
?
.
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
,
firtSessionItem
:
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
:
"PlaylistCell"
)
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
.
delegate
?
.
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
.
count
>
0
&&
!
inFolder
()
{
collectionView
.
performBatchUpdates
({
collectionView
.
insertItems
(
at
:
[
destinationPath
])
delegate
?
.
dragAndDropManagerInsertItem
(
manager
:
self
,
item
:
mlFile
,
atIndexPath
:
destinationPath
)
delegate
?
.
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
({
collectionView
.
deleteItems
(
at
:[
sourcepath
])
if
let
file
=
delegate
?
.
dragAndDropManagerRequestsFile
(
manager
:
self
,
atIndexPath
:
sourcepath
)
as?
MLFile
{
addFile
(
file
:
file
,
toFolderAt
:
index
)
delegate
?
.
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
,
firtSessionItem
item
:
AnyObject
?,
withDestinationIndexPath
destinationIndexPath
:
IndexPath
?)
->
UIDropOperation
{
let
inAlbum
=
delegate
?
.
dragAndDropManagerCurrentSelection
(
manager
:
self
)
as?
MLAlbum
!=
nil
let
inShow
=
delegate
?
.
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
)
{
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
.
count
>
0
{
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
=
delegate
?
.
dragAndDropManagerRequestsFile
(
manager
:
self
,
atIndexPath
:
indexPath
)
return
file
as?
MLLabel
!=
nil
}
return
false
}
private
func
fileIsCollection
(
file
:
AnyObject
?)
->
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
{
let
file
=
delegate
?
.
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
=
delegate
?
.
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
:
AnyObject
)
->
[
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
.
first
{
_
=
set
.
insert
(
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
:
AnyObject
)
->
[
UIDragItem
]
{
guard
let
file
=
mlFile
(
from
:
file
),
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
=
delegate
?
.
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
.
delegate
?
.
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
}
}
Sources/VLCLibraryViewController.m
View file @
ea54eeb7
...
...
@@ -30,12 +30,14 @@
#import "VLCPlaylistTableViewCell.h"
#import "VLCPlaybackController+MediaLibrary.h"
#import "VLC_iOS-Swift.h"
#import <CoreSpotlight/CoreSpotlight.h>
/* prefs keys */
static
NSString
*
kDisplayedFirstSteps
=
@"Did we display the first steps tutorial?"
;
static
NSString
*
kUsingTableViewToShowData
=
@"UsingTableViewToShowData"
;
@implementation
EmptyLibraryView
-
(
IBAction
)
learnMore
:(
id
)
sender
...
...
@@ -71,6 +73,8 @@ static NSString *kUsingTableViewToShowData = @"UsingTableViewToShowData";
UIBarButtonItem
*
_shareBarButtonItem
;
UIBarButtonItem
*
_removeFromFolderBarButtonItem
;
UIBarButtonItem
*
_deleteSelectedBarButtonItem
;
NSObject
*
dragAndDropManager
;
}
@property
(
nonatomic
,
strong
)
UIBarButtonItem
*
displayModeBarButtonItem
;
...
...
@@ -83,9 +87,6 @@ static NSString *kUsingTableViewToShowData = @"UsingTableViewToShowData";
@implementation
VLCLibraryViewController
-
(
void
)
dealloc
{
}
+
(
void
)
initialize
{
...
...
@@ -96,16 +97,21 @@ static NSString *kUsingTableViewToShowData = @"UsingTableViewToShowData";
-
(
void
)
loadView
{
[
self
setupContentView
];
[
self
setViewFromDeviceOrientation
];
[
self
updateViewsForCurrentDisplayMode
];
_libraryMode
=
VLCLibraryModeAllFiles
;
_searchDataSource
=
[
VLCLibrarySearchDisplayDataSource
new
];
_mediaDataSource
=
[
VLCMediaDataSource
new
];
_searchDataSource
=
[
VLCLibrarySearchDisplayDataSource
new
];
self
.
emptyLibraryView
=
[[[
NSBundle
mainBundle
]
loadNibNamed
:
@"VLCEmptyLibraryView"
owner
:
self
options
:
nil
]
lastObject
];
_emptyLibraryView
.
emptyLibraryLongDescriptionLabel
.
lineBreakMode
=
NSLineBreakByWordWrapping
;
_emptyLibraryView
.
emptyLibraryLongDescriptionLabel
.
numberOfLines
=
0
;
if
(
@available
(
iOS
11
.
0
,
*
))
{
dragAndDropManager
=
[
VLCDragAndDropManager
new
];
((
VLCDragAndDropManager
*
)
dragAndDropManager
).
delegate
=
_mediaDataSource
;
UIDropInteraction
*
dropInteraction
=
[[
UIDropInteraction
alloc
]
initWithDelegate
:(
VLCDragAndDropManager
*
)
dragAndDropManager
];
[
_emptyLibraryView
addInteraction
:
dropInteraction
];
}
[
self
setupContentView
];
[
self
setViewFromDeviceOrientation
];
[
self
updateViewsForCurrentDisplayMode
];
_libraryMode
=
VLCLibraryModeAllFiles
;
}
-
(
void
)
setupContentView
...
...
@@ -114,7 +120,6 @@ static NSString *kUsingTableViewToShowData = @"UsingTableViewToShowData";
UIView
*
contentView
=
[[
UIView
alloc
]
initWithFrame
:
viewDimensions
];
contentView
.
autoresizingMask
=
UIViewAutoresizingFlexibleHeight
|
UIViewAutoresizingFlexibleWidth
;
contentView
.
backgroundColor
=
[
UIColor
VLCDarkBackgroundColor
];
if
(
self
.
usingTableViewToShowData
)
{
if
(
!
_tableView
)
{
_tableView
=
[[
UITableView
alloc
]
initWithFrame
:
viewDimensions
style
:
UITableViewStylePlain
];
...
...
@@ -128,6 +133,10 @@ static NSString *kUsingTableViewToShowData = @"UsingTableViewToShowData";
_tableView
.
separatorColor
=
[
UIColor
VLCDarkBackgroundColor
];
_tableView
.
delegate
=
self
;
_tableView
.
dataSource
=
self
;
if
(
@available
(
iOS
11
.
0
,
*
))
{
_tableView
.
dragDelegate
=
((
VLCDragAndDropManager
*
)
dragAndDropManager
);
_tableView
.
dropDelegate
=
((
VLCDragAndDropManager
*
)
dragAndDropManager
);
}
_tableView
.
indicatorStyle
=
UIScrollViewIndicatorStyleWhite
;
_tableView
.
autoresizingMask
=
UIViewAutoresizingFlexibleHeight
|
UIViewAutoresizingFlexibleWidth
;
_tableView
.
tableHeaderView
=
_searchController
.
searchBar
;
...
...
@@ -142,6 +151,10 @@ static NSString *kUsingTableViewToShowData = @"UsingTableViewToShowData";
_folderLayout
=
[[
VLCFolderCollectionViewFlowLayout
alloc
]
init
];
_collectionView
=
[[
UICollectionView
alloc
]
initWithFrame
:
viewDimensions
collectionViewLayout
:
_folderLayout
];
_collectionView
.
alwaysBounceVertical
=
YES
;
if
(
@available
(
iOS
11
.
0
,
*
))
{
_collectionView
.
dragDelegate
=
((
VLCDragAndDropManager
*
)
dragAndDropManager
);
_collectionView
.
dropDelegate
=
((
VLCDragAndDropManager
*
)
dragAndDropManager
);
}
_collectionView
.
indicatorStyle
=
UIScrollViewIndicatorStyleWhite
;
_collectionView
.
delegate
=
self
;