[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>
lastfm
Christopher Snowhill 2022-06-29 11:56:50 -07:00
parent 02ec735687
commit 82179a5f10
5 changed files with 114 additions and 27 deletions

View File

@ -1,5 +1,5 @@
<?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">
<attribute name="artData" optional="YES" attributeType="Binary"/>
<attribute name="artHash" optional="YES" attributeType="String"/>
@ -64,12 +64,13 @@
</entity>
<entity name="SandboxToken" representedClassName="SandboxToken" syncable="YES" codeGenerationType="class">
<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"/>
</entity>
<elements>
<element name="AlbumArtwork" positionX="0" positionY="207" width="128" height="59"/>
<element name="PlayCount" positionX="-18" positionY="171" width="128" height="149"/>
<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>
</model>

View File

@ -399,6 +399,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
[[SandboxBroker sharedSandboxBroker] addFolderIfMissing:url];
[expandedURLs addObjectsFromArray:[self fileURLsAtPath:[url path]]];
} else {
[[SandboxBroker sharedSandboxBroker] addFileIfMissing:url];
[expandedURLs addObject:[NSURL fileURLWithPath:[url path]]];
}
}

View File

@ -22,6 +22,7 @@
@interface SandboxToken : NSManagedObject
@property(nonatomic, strong) NSString *path;
@property(nonatomic, strong) NSData *bookmark;
@property(nonatomic) BOOL folder;
@end
@implementation SandboxPathBehaviorController

View File

@ -22,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)shutdown;
- (void)addFolderIfMissing:(NSURL *)folderUrl;
- (void)addFileIfMissing:(NSURL *)fileUrl;
- (const void *)beginFolderAccess:(NSURL *)fileUrl;
- (void)endFolderAccess:(const void *)handle;

View File

@ -24,6 +24,7 @@ static SandboxBroker *kSharedSandboxBroker = nil;
NSInteger _refCount;
NSURL *_secureUrl;
NSString *_path;
BOOL _isFolder;
};
@property(readonly) SandboxToken *token;
@ -34,6 +35,8 @@ static SandboxBroker *kSharedSandboxBroker = nil;
@property NSInteger refCount;
@property(readonly) BOOL isFolder;
- (id)initWithToken:(SandboxToken *)token;
@end
@ -45,6 +48,7 @@ static SandboxBroker *kSharedSandboxBroker = nil;
obj->_secureUrl = nil;
obj->_token = token;
obj->_path = token.path;
obj->_isFolder = token.folder;
}
return obj;
}
@ -72,6 +76,10 @@ static SandboxBroker *kSharedSandboxBroker = nil;
- (NSString *)path {
return _path;
}
- (BOOL)isFolder {
return _isFolder;
}
@end
@implementation SandboxBroker
@ -99,8 +107,10 @@ static SandboxBroker *kSharedSandboxBroker = nil;
NSString *s = [url path];
NSString *lastComponent = [url lastPathComponent];
NSString *lastComponent = [url fragment];
if(lastComponent) {
lastComponent = @"#";
// Find that last component in the string from the end to make sure
// to get the last one
NSRange fragmentRange = [s rangeOfString:lastComponent
@ -110,6 +120,9 @@ static SandboxBroker *kSharedSandboxBroker = nil;
NSString *newURLString = [s substringToIndex:fragmentRange.location + fragmentRange.length];
return [NSURL fileURLWithPath:newURLString];
} else {
return url;
}
}
- (id)init {
@ -149,13 +162,30 @@ static SandboxBroker *kSharedSandboxBroker = nil;
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"];
request.sortDescriptors = @[sortDescriptor];
request.predicate = predicate;
NSError *error = nil;
NSArray *results = [pc.viewContext executeFetchRequest:request error:&error];
if(results && [results count] > 0) {
ret = [[SandboxEntry alloc] initWithToken:results[0]];
}
if(!ret) {
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) {
@ -168,6 +198,7 @@ static SandboxBroker *kSharedSandboxBroker = nil;
}
}
}
}
if(ret) {
BOOL isStale;
@ -193,7 +224,7 @@ static SandboxBroker *kSharedSandboxBroker = nil;
SandboxEntry *_entry = nil;
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;
break;
}
@ -227,20 +258,72 @@ 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 {
NSURL *folderUrl = [[SandboxBroker urlWithoutFragment:fileUrl] URLByDeletingLastPathComponent];
NSURL *folderUrl = [SandboxBroker urlWithoutFragment:fileUrl];
if(![folderUrl isFileURL]) return NULL;
SandboxEntry *_entry = nil;
NSString *sandboxPath = [folderUrl path];
@synchronized(self) {
for(SandboxEntry *entry in storage) {
if(entry.path && [SandboxBroker isPath:folderUrl aSubdirectoryOf:[NSURL fileURLWithPath:entry.path]]) {
if(entry.path) {
if((entry.isFolder && [SandboxBroker isPath:folderUrl aSubdirectoryOf:[NSURL fileURLWithPath:entry.path]]) ||
(!entry.isFolder && [entry.path isEqualToString:sandboxPath])) {
entry.refCount += 1;
_entry = entry;
break;
}
}
}
if(!_entry) {
_entry = [self recursivePathTest:folderUrl];