[Sandbox] Support bookmarking individual files
Individually added files, directly opened by the user, may now store bookmarks in settings. Signed-off-by: Christopher Snowhill <kode54@gmail.com>swiftingly
parent
35400e1320
commit
7d26150c26
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21223.12" systemVersion="22A5286j" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="20086" systemVersion="21F79" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||||
<entity name="AlbumArtwork" representedClassName="AlbumArtwork" syncable="YES" codeGenerationType="class">
|
<entity name="AlbumArtwork" representedClassName="AlbumArtwork" syncable="YES" codeGenerationType="class">
|
||||||
<attribute name="artData" optional="YES" attributeType="Binary"/>
|
<attribute name="artData" optional="YES" attributeType="Binary"/>
|
||||||
<attribute name="artHash" optional="YES" attributeType="String"/>
|
<attribute name="artHash" optional="YES" attributeType="String"/>
|
||||||
|
@ -64,12 +64,13 @@
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="SandboxToken" representedClassName="SandboxToken" syncable="YES" codeGenerationType="class">
|
<entity name="SandboxToken" representedClassName="SandboxToken" syncable="YES" codeGenerationType="class">
|
||||||
<attribute name="bookmark" optional="YES" attributeType="Binary"/>
|
<attribute name="bookmark" optional="YES" attributeType="Binary"/>
|
||||||
|
<attribute name="folder" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||||
<attribute name="path" optional="YES" attributeType="String"/>
|
<attribute name="path" optional="YES" attributeType="String"/>
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="AlbumArtwork" positionX="0" positionY="207" width="128" height="59"/>
|
<element name="AlbumArtwork" positionX="0" positionY="207" width="128" height="59"/>
|
||||||
<element name="PlayCount" positionX="-18" positionY="171" width="128" height="149"/>
|
<element name="PlayCount" positionX="-18" positionY="171" width="128" height="149"/>
|
||||||
<element name="PlaylistEntry" positionX="-36" positionY="9" width="128" height="719"/>
|
<element name="PlaylistEntry" positionX="-36" positionY="9" width="128" height="719"/>
|
||||||
<element name="SandboxToken" positionX="-18" positionY="171" width="128" height="59"/>
|
<element name="SandboxToken" positionX="-18" positionY="171" width="128" height="74"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
|
@ -399,6 +399,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
||||||
[[SandboxBroker sharedSandboxBroker] addFolderIfMissing:url];
|
[[SandboxBroker sharedSandboxBroker] addFolderIfMissing:url];
|
||||||
[expandedURLs addObjectsFromArray:[self fileURLsAtPath:[url path]]];
|
[expandedURLs addObjectsFromArray:[self fileURLsAtPath:[url path]]];
|
||||||
} else {
|
} else {
|
||||||
|
[[SandboxBroker sharedSandboxBroker] addFileIfMissing:url];
|
||||||
[expandedURLs addObject:[NSURL fileURLWithPath:[url path]]];
|
[expandedURLs addObject:[NSURL fileURLWithPath:[url path]]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
@interface SandboxToken : NSManagedObject
|
@interface SandboxToken : NSManagedObject
|
||||||
@property(nonatomic, strong) NSString *path;
|
@property(nonatomic, strong) NSString *path;
|
||||||
@property(nonatomic, strong) NSData *bookmark;
|
@property(nonatomic, strong) NSData *bookmark;
|
||||||
|
@property(nonatomic) BOOL folder;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation SandboxPathBehaviorController
|
@implementation SandboxPathBehaviorController
|
||||||
|
|
|
@ -22,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
- (void)shutdown;
|
- (void)shutdown;
|
||||||
|
|
||||||
- (void)addFolderIfMissing:(NSURL *)folderUrl;
|
- (void)addFolderIfMissing:(NSURL *)folderUrl;
|
||||||
|
- (void)addFileIfMissing:(NSURL *)fileUrl;
|
||||||
|
|
||||||
- (const void *)beginFolderAccess:(NSURL *)fileUrl;
|
- (const void *)beginFolderAccess:(NSURL *)fileUrl;
|
||||||
- (void)endFolderAccess:(const void *)handle;
|
- (void)endFolderAccess:(const void *)handle;
|
||||||
|
|
|
@ -24,6 +24,7 @@ static SandboxBroker *kSharedSandboxBroker = nil;
|
||||||
NSInteger _refCount;
|
NSInteger _refCount;
|
||||||
NSURL *_secureUrl;
|
NSURL *_secureUrl;
|
||||||
NSString *_path;
|
NSString *_path;
|
||||||
|
BOOL _isFolder;
|
||||||
};
|
};
|
||||||
|
|
||||||
@property(readonly) SandboxToken *token;
|
@property(readonly) SandboxToken *token;
|
||||||
|
@ -34,6 +35,8 @@ static SandboxBroker *kSharedSandboxBroker = nil;
|
||||||
|
|
||||||
@property NSInteger refCount;
|
@property NSInteger refCount;
|
||||||
|
|
||||||
|
@property(readonly) BOOL isFolder;
|
||||||
|
|
||||||
- (id)initWithToken:(SandboxToken *)token;
|
- (id)initWithToken:(SandboxToken *)token;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -45,6 +48,7 @@ static SandboxBroker *kSharedSandboxBroker = nil;
|
||||||
obj->_secureUrl = nil;
|
obj->_secureUrl = nil;
|
||||||
obj->_token = token;
|
obj->_token = token;
|
||||||
obj->_path = token.path;
|
obj->_path = token.path;
|
||||||
|
obj->_isFolder = token.folder;
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
@ -72,6 +76,10 @@ static SandboxBroker *kSharedSandboxBroker = nil;
|
||||||
- (NSString *)path {
|
- (NSString *)path {
|
||||||
return _path;
|
return _path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)isFolder {
|
||||||
|
return _isFolder;
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation SandboxBroker
|
@implementation SandboxBroker
|
||||||
|
@ -99,17 +107,22 @@ static SandboxBroker *kSharedSandboxBroker = nil;
|
||||||
|
|
||||||
NSString *s = [url path];
|
NSString *s = [url path];
|
||||||
|
|
||||||
NSString *lastComponent = [url lastPathComponent];
|
NSString *lastComponent = [url fragment];
|
||||||
|
|
||||||
// Find that last component in the string from the end to make sure
|
if(lastComponent) {
|
||||||
// to get the last one
|
lastComponent = @"#";
|
||||||
NSRange fragmentRange = [s rangeOfString:lastComponent
|
// Find that last component in the string from the end to make sure
|
||||||
options:NSBackwardsSearch];
|
// to get the last one
|
||||||
|
NSRange fragmentRange = [s rangeOfString:lastComponent
|
||||||
|
options:NSBackwardsSearch];
|
||||||
|
|
||||||
// Chop the fragment.
|
// Chop the fragment.
|
||||||
NSString *newURLString = [s substringToIndex:fragmentRange.location + fragmentRange.length];
|
NSString *newURLString = [s substringToIndex:fragmentRange.location + fragmentRange.length];
|
||||||
|
|
||||||
return [NSURL fileURLWithPath:newURLString];
|
return [NSURL fileURLWithPath:newURLString];
|
||||||
|
} else {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init {
|
- (id)init {
|
||||||
|
@ -149,22 +162,40 @@ static SandboxBroker *kSharedSandboxBroker = nil;
|
||||||
|
|
||||||
NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer];
|
NSPersistentContainer *pc = [SandboxBroker sharedPersistentContainer];
|
||||||
|
|
||||||
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path.length" ascending:NO];
|
NSPredicate *folderPredicate = [NSPredicate predicateWithFormat:@"folder == NO"];
|
||||||
|
NSPredicate *filePredicate = [NSPredicate predicateWithFormat:@"path == %@", [url path]];
|
||||||
|
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[folderPredicate, filePredicate]];
|
||||||
|
|
||||||
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"];
|
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"];
|
||||||
request.sortDescriptors = @[sortDescriptor];
|
request.predicate = predicate;
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
NSArray *results = [pc.viewContext executeFetchRequest:request error:&error];
|
NSArray *results = [pc.viewContext executeFetchRequest:request error:&error];
|
||||||
if(results) results = [results copy];
|
|
||||||
|
|
||||||
if(results && [results count] > 0) {
|
if(results && [results count] > 0) {
|
||||||
for(SandboxToken *token in results) {
|
ret = [[SandboxEntry alloc] initWithToken:results[0]];
|
||||||
if(token.path && [SandboxBroker isPath:url aSubdirectoryOf:[NSURL fileURLWithPath:token.path]]) {
|
}
|
||||||
SandboxEntry *entry = [[SandboxEntry alloc] initWithToken:token];
|
|
||||||
|
|
||||||
ret = entry;
|
if(!ret) {
|
||||||
break;
|
predicate = [NSPredicate predicateWithFormat:@"folder == YES"];
|
||||||
|
|
||||||
|
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path.length" ascending:NO];
|
||||||
|
|
||||||
|
request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"];
|
||||||
|
request.sortDescriptors = @[sortDescriptor];
|
||||||
|
request.predicate = predicate;
|
||||||
|
|
||||||
|
error = nil;
|
||||||
|
results = [pc.viewContext executeFetchRequest:request error:&error];
|
||||||
|
if(results) results = [results copy];
|
||||||
|
|
||||||
|
if(results && [results count] > 0) {
|
||||||
|
for(SandboxToken *token in results) {
|
||||||
|
if(token.path && [SandboxBroker isPath:url aSubdirectoryOf:[NSURL fileURLWithPath:token.path]]) {
|
||||||
|
SandboxEntry *entry = [[SandboxEntry alloc] initWithToken:token];
|
||||||
|
|
||||||
|
ret = entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,7 +224,7 @@ static SandboxBroker *kSharedSandboxBroker = nil;
|
||||||
SandboxEntry *_entry = nil;
|
SandboxEntry *_entry = nil;
|
||||||
|
|
||||||
for(SandboxEntry *entry in storage) {
|
for(SandboxEntry *entry in storage) {
|
||||||
if(entry.path && [SandboxBroker isPath:folderUrl aSubdirectoryOf:[NSURL fileURLWithPath:entry.path]]) {
|
if(entry.path && entry.isFolder && [SandboxBroker isPath:folderUrl aSubdirectoryOf:[NSURL fileURLWithPath:entry.path]]) {
|
||||||
_entry = entry;
|
_entry = entry;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -227,18 +258,70 @@ static SandboxBroker *kSharedSandboxBroker = nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)addFileIfMissing:(NSURL *)fileUrl {
|
||||||
|
if(![fileUrl isFileURL]) return;
|
||||||
|
|
||||||
|
NSURL *url = [SandboxBroker urlWithoutFragment:fileUrl];
|
||||||
|
|
||||||
|
@synchronized (self) {
|
||||||
|
SandboxEntry *_entry = nil;
|
||||||
|
|
||||||
|
for(SandboxEntry *entry in storage) {
|
||||||
|
if(entry.path) {
|
||||||
|
if((entry.isFolder && [SandboxBroker isPath:url aSubdirectoryOf:[NSURL fileURLWithPath:entry.path]]) ||
|
||||||
|
(!entry.isFolder && [url isEqualTo:[NSURL fileURLWithPath:entry.path]])) {
|
||||||
|
_entry = entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_entry) {
|
||||||
|
_entry = [self recursivePathTest:url];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_entry) {
|
||||||
|
NSError *err = nil;
|
||||||
|
NSData *bookmark = [fileUrl bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&err];
|
||||||
|
if(!bookmark && err) {
|
||||||
|
ALog(@"Failed to add bookmark for URL: %@, with error: %@", url, [err localizedDescription]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer];
|
||||||
|
|
||||||
|
SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext];
|
||||||
|
|
||||||
|
if(token) {
|
||||||
|
token.path = [url path];
|
||||||
|
token.bookmark = bookmark;
|
||||||
|
token.folder = NO;
|
||||||
|
[pc.viewContext save:&err];
|
||||||
|
if(err) {
|
||||||
|
ALog(@"Error saving bookmark: %@", [err localizedDescription]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (const void *)beginFolderAccess:(NSURL *)fileUrl {
|
- (const void *)beginFolderAccess:(NSURL *)fileUrl {
|
||||||
NSURL *folderUrl = [[SandboxBroker urlWithoutFragment:fileUrl] URLByDeletingLastPathComponent];
|
NSURL *folderUrl = [SandboxBroker urlWithoutFragment:fileUrl];
|
||||||
if(![folderUrl isFileURL]) return NULL;
|
if(![folderUrl isFileURL]) return NULL;
|
||||||
|
|
||||||
SandboxEntry *_entry = nil;
|
SandboxEntry *_entry = nil;
|
||||||
|
|
||||||
|
NSString *sandboxPath = [folderUrl path];
|
||||||
|
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
for(SandboxEntry *entry in storage) {
|
for(SandboxEntry *entry in storage) {
|
||||||
if(entry.path && [SandboxBroker isPath:folderUrl aSubdirectoryOf:[NSURL fileURLWithPath:entry.path]]) {
|
if(entry.path) {
|
||||||
entry.refCount += 1;
|
if((entry.isFolder && [SandboxBroker isPath:folderUrl aSubdirectoryOf:[NSURL fileURLWithPath:entry.path]]) ||
|
||||||
_entry = entry;
|
(!entry.isFolder && [entry.path isEqualToString:sandboxPath])) {
|
||||||
break;
|
entry.refCount += 1;
|
||||||
|
_entry = entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue