openEventsPlatformApp/plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.m
plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.mRaw
1/*
2 Licensed to the Apache Software Foundation (ASF) under one
3 or more contributor license agreements. See the NOTICE file
4 distributed with this work for additional information
5 regarding copyright ownership. The ASF licenses this file
6 to you under the Apache License, Version 2.0 (the
7 "License"); you may not use this file except in compliance
8 with the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing,
13 software distributed under the License is distributed on an
14 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 KIND, either express or implied. See the License for the
16 specific language governing permissions and limitations
17 under the License.
18 */
19
20#import "CDVFile.h"
21#import "CDVAssetLibraryFilesystem.h"
22#import <Cordova/CDV.h>
23#import <AssetsLibrary/ALAsset.h>
24#import <AssetsLibrary/ALAssetRepresentation.h>
25#import <AssetsLibrary/ALAssetsLibrary.h>
26#import <MobileCoreServices/MobileCoreServices.h>
27
28NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
29NSString* const kCDVAssetsLibraryScheme = @"assets-library";
30
31@implementation CDVAssetLibraryFilesystem
32@synthesize name=_name, urlTransformer;
33
34
35/*
36 The CDVAssetLibraryFilesystem works with resources which are identified
37 by iOS as
38 asset-library://<path>
39 and represents them internally as URLs of the form
40 cdvfile://localhost/assets-library/<path>
41 */
42
43- (NSURL *)assetLibraryURLForLocalURL:(CDVFilesystemURL *)url
44{
45 if ([url.url.scheme isEqualToString:kCDVFilesystemURLPrefix]) {
46 NSString *path = [[url.url absoluteString] substringFromIndex:[@"cdvfile://localhost/assets-library" length]];
47 return [NSURL URLWithString:[NSString stringWithFormat:@"assets-library:/%@", path]];
48 }
49 return url.url;
50}
51
52- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url
53{
54 NSDictionary* entry = [self makeEntryForLocalURL:url];
55 return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:entry];
56}
57
58- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)url {
59 return [self makeEntryForPath:url.fullPath isDirectory:NO];
60}
61
62- (NSDictionary*)makeEntryForPath:(NSString*)fullPath isDirectory:(BOOL)isDir
63{
64 NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5];
65 NSString* lastPart = [fullPath lastPathComponent];
66 if (isDir && ![fullPath hasSuffix:@"/"]) {
67 fullPath = [fullPath stringByAppendingString:@"/"];
68 }
69 [dirEntry setObject:[NSNumber numberWithBool:!isDir] forKey:@"isFile"];
70 [dirEntry setObject:[NSNumber numberWithBool:isDir] forKey:@"isDirectory"];
71 [dirEntry setObject:fullPath forKey:@"fullPath"];
72 [dirEntry setObject:lastPart forKey:@"name"];
73 [dirEntry setObject:self.name forKey: @"filesystemName"];
74
75 NSURL* nativeURL = [NSURL URLWithString:[NSString stringWithFormat:@"assets-library:/%@",fullPath]];
76 if (self.urlTransformer) {
77 nativeURL = self.urlTransformer(nativeURL);
78 }
79 dirEntry[@"nativeURL"] = [nativeURL absoluteString];
80
81 return dirEntry;
82}
83
84/* helper function to get the mimeType from the file extension
85 * IN:
86 * NSString* fullPath - filename (may include path)
87 * OUT:
88 * NSString* the mime type as type/subtype. nil if not able to determine
89 */
90+ (NSString*)getMimeTypeFromPath:(NSString*)fullPath
91{
92 NSString* mimeType = nil;
93
94 if (fullPath) {
95 CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL);
96 if (typeId) {
97 mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType);
98 if (!mimeType) {
99 // special case for m4a
100 if ([(__bridge NSString*)typeId rangeOfString : @"m4a-audio"].location != NSNotFound) {
101 mimeType = @"audio/mp4";
102 } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) {
103 mimeType = @"audio/wav";
104 } else if ([[fullPath pathExtension] rangeOfString:@"css"].location != NSNotFound) {
105 mimeType = @"text/css";
106 }
107 }
108 CFRelease(typeId);
109 }
110 }
111 return mimeType;
112}
113
114- (id)initWithName:(NSString *)name
115{
116 if (self) {
117 self.name = name;
118 }
119 return self;
120}
121
122- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options
123{
124 // return unsupported result for assets-library URLs
125 return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getFile not supported for assets-library URLs."];
126}
127
128- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI
129{
130 // we don't (yet?) support getting the parent of an asset
131 return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_READABLE_ERR];
132}
133
134- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options
135{
136 // setMetadata doesn't make sense for asset library files
137 return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
138}
139
140- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI
141{
142 // return error for assets-library URLs
143 return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
144}
145
146- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI
147{
148 // return error for assets-library URLs
149 return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"removeRecursively not supported for assets-library URLs."];
150}
151
152- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI
153{
154 // return unsupported result for assets-library URLs
155 return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readEntries not supported for assets-library URLs."];
156}
157
158- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos
159{
160 // assets-library files can't be truncated
161 return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
162}
163
164- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend
165{
166 // text can't be written into assets-library files
167 return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
168}
169
170- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback
171{
172 // Copying to an assets library file is not doable, since we can't write it.
173 CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
174 callback(result);
175}
176
177- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)url
178{
179 NSString *path = nil;
180 if ([[url.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
181 path = [url.url path];
182 } else {
183 path = url.fullPath;
184 }
185 if ([path hasSuffix:@"/"]) {
186 path = [path substringToIndex:([path length]-1)];
187 }
188 return path;
189}
190
191- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback
192{
193 ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
194 if (asset) {
195 // We have the asset! Get the data and send it off.
196 ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
197 NSUInteger size = (end > start) ? (end - start) : [assetRepresentation size];
198 Byte* buffer = (Byte*)malloc(size);
199 NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:start length:size error:nil];
200 NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
201 NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType);
202
203 callback(data, MIMEType, NO_ERROR);
204 } else {
205 callback(nil, nil, NOT_FOUND_ERR);
206 }
207 };
208
209 ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
210 // Retrieving the asset failed for some reason. Send the appropriate error.
211 NSLog(@"Error: %@", error);
212 callback(nil, nil, SECURITY_ERR);
213 };
214
215 ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
216 [assetsLibrary assetForURL:[self assetLibraryURLForLocalURL:localURL] resultBlock:resultBlock failureBlock:failureBlock];
217}
218
219- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback
220{
221 // In this case, we need to use an asynchronous method to retrieve the file.
222 // Because of this, we can't just assign to `result` and send it at the end of the method.
223 // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
224 ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
225 if (asset) {
226 // We have the asset! Populate the dictionary and send it off.
227 NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
228 ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
229 [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[assetRepresentation size]] forKey:@"size"];
230 [fileInfo setObject:localURL.fullPath forKey:@"fullPath"];
231 NSString* filename = [assetRepresentation filename];
232 [fileInfo setObject:filename forKey:@"name"];
233 [fileInfo setObject:[CDVAssetLibraryFilesystem getMimeTypeFromPath:filename] forKey:@"type"];
234 NSDate* creationDate = [asset valueForProperty:ALAssetPropertyDate];
235 NSNumber* msDate = [NSNumber numberWithDouble:[creationDate timeIntervalSince1970] * 1000];
236 [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
237
238 callback([CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]);
239 } else {
240 // We couldn't find the asset. Send the appropriate error.
241 callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]);
242 }
243 };
244 ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
245 // Retrieving the asset failed for some reason. Send the appropriate error.
246 callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]]);
247 };
248
249 ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
250 [assetsLibrary assetForURL:[self assetLibraryURLForLocalURL:localURL] resultBlock:resultBlock failureBlock:failureBlock];
251 return;
252}
253@end