From e2ef1c0599a9534146b3d0411fda594131e0f821 Mon Sep 17 00:00:00 2001 From: jrouault Date: Wed, 18 May 2016 16:42:06 +0900 Subject: [PATCH] Use NSURLSession to download files on iOS and update error returned on Android --- README.md | 85 +++++-- .../plugin/WizAssets/WizAssetsPlugin.java | 210 +++++++++++++---- .../HelloCordova.xcodeproj/project.pbxproj | 2 +- .../Plugins/WizAssetsPlugin/WizAssetsPlugin.h | 15 ++ .../Plugins/WizAssetsPlugin/WizAssetsPlugin.m | 218 +++++++----------- plugin.xml | 7 +- .../plugin/wizAssets/WizAssetsError.js | 41 ++++ www/phonegap/plugin/wizAssets/wizAssets.js | 8 +- 8 files changed, 378 insertions(+), 208 deletions(-) create mode 100644 www/phonegap/plugin/wizAssets/WizAssetsError.js diff --git a/README.md b/README.md index 8b1d58d..a444e69 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # phonegap-plugin-wizAssets - PhoneGap Version : 3.0 -- last update : 15/11/2013 +- last update : 23/05/2016 # Description -PhoneGap plugin for managing application assets with javascript asset maps. Includes (iOS background threaded) downloadFile, getFileURI, getFileURIs, deleteFile, deleteFiles. +PhoneGap plugin for managing application assets with javascript asset maps. Includes downloadFile, getFileURI, getFileURIs, deleteFile, deleteFiles. ## Install (with Plugman) @@ -22,54 +22,62 @@ PhoneGap plugin for managing application assets with javascript asset maps. Incl ## APIs -### downloadFile() +### wizAssets.downloadFile(remoteURL, assetId, success, fail) -**wizAssets.downloadFile(String remoteURL, String assetId, Function success, Function fail);** - -- downloads a file to native App directory @ ./ + gameDir+ / +filePathToBeStoredWithFilename
+- downloads a file to native App directory @ ./ + gameDir+ / + assetId
- A success returns a local URL string like; file://documents/settings/img/cards/card001.jpg
-- example; +- An error returns an error object such as: +``` +{ + "code": WizAssetsError value, + "status": if code is a HTTP_REQUEST_ERROR, the status of the HTPP request, optional + "message": description of the error if any +} +``` +**Example** ``` wizAssets.downloadFile("http://google.com/logo.jpg", "img/ui/logo.jpg", successCallback, failCallback); ``` -### deleteFile() - -**wizAssets.deleteFile(string assetId, Function success, Function fail);** +### wizAssets.deleteFile(assetId, success, fail) - deletes the file specified by the asset id
- if the asset id does not exist fail will be called with error NotFoundError
-- if the asset id cannot be deleted (i.e. file resides in read-only memory) fail will be called with error NotModificationAllowedError +- if the asset id cannot be deleted (i.e. file resides in read-only memory) fail will be called with an error message + +**Example** ``` -wizAssets.deleteFile("file://documents/settings/img/cards/card001.jpg", successCallback, failCallback); +wizAssets.deleteFile("img/cards/card001.jpg", successCallback, failCallback); ``` -### deleteFiles() - -**wizAssets.deleteFiles(Array assetIds, Function success, Function fail );** +### wizAssets.deleteFiles(assetIds, success, fail) -- delete files specified by their asset id in Array like; [ "img/cards/card001.jpg", "img/cards/card002.jpg " .. ]
+- delete files specified by their asset id in Array
- if an asset id uses a path format and matches a folder, then the folder content will be deleted; img/cards
+- if an asset id cannot be deleted (i.e. file resides in read-only memory) fail will be called with an error message - the array CAN contain one asset id -### getFileURI() +**Example** +``` +wizAssets.deleteFiles(["img/cards/card001.jpg", "img/cards/card002.jpg"], successCallback, failCallback); +``` -**wizAssets.getFileURI(String assetId, Function success, Function fail);** +### wizAssets.getFileURI(assetId, success, fail) - A success returns a local URL string like file://documents/settings/img/cards/card001.jpg
-- example; +- A failure returns an error message +**Example** ``` wizAssets.getFileURI("img/ui/logo.jpg", successCallback, failCallback); ``` -### getFileURIs() - -**wizAssets.getFileURIs(Function success, Function fail);** +### wizAssets.getFileURIs(success, fail) - A success returns a hashmap of asset id matching its local URL such as +- A failure returns an error message ``` { @@ -77,5 +85,36 @@ wizAssets.getFileURI("img/ui/logo.jpg", successCallback, failCallback); "img/ui/loader.gif" : "/sdcard//img/ui/loading.gif", "img/cards/card001.jpg" : "file://documents/settings/img/cards/card001.jpg" -} +} ``` + +**Example** +``` +wizAssets.getFileURIs(successCallback, failCallback); +``` + +### Error handling + +All error codes are available via ```window.WizAssetsError```. + +**Example** +``` +function fail(error) { + if (error.code === window.WizAssetsError.HTTP_REQUEST_ERROR) { + // Check error.status + } else { + // Log error code with error.message + } +} +``` + +| Code | Constant | Description | +|-----:|:-----------------------------|:-------------------------------------------------------------------------------------------------------| +| 1 | `ARGS_MISSING_ERROR` | Arguments are missing in your call to WizAssets' method | +| 2 | `INVALID_URL_ERROR` | Specified URL to download is invald | +| 3 | `CONNECTIVITY_ERROR` | Download could not be processed because network could not be accessed | +| 4 | `HTTP_REQUEST_ERROR` | HTTP status of the request is not successful (not 2XX) | +| 5 | `HTTP_REQUEST_CONTENT_ERROR` | Content received in HTTP request could not be read | +| 6 | `DIRECTORY_CREATION_ERROR` | Creation of the directory to save the asset failed | +| 7 | `FILE_CREATION_ERROR` | Saving the asset failed | +| 8 | `JSON_CREATION_ERROR` | Your call to WizAssets' method failed and its error could not be processed internally | \ No newline at end of file diff --git a/platforms/android/src/jp/wizcorp/phonegap/plugin/WizAssets/WizAssetsPlugin.java b/platforms/android/src/jp/wizcorp/phonegap/plugin/WizAssets/WizAssetsPlugin.java index 09023ca..725804d 100755 --- a/platforms/android/src/jp/wizcorp/phonegap/plugin/WizAssets/WizAssetsPlugin.java +++ b/platforms/android/src/jp/wizcorp/phonegap/plugin/WizAssets/WizAssetsPlugin.java @@ -15,8 +15,8 @@ import java.io.*; import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; -import java.util.Date; import java.util.zip.GZIPInputStream; import android.annotation.SuppressLint; @@ -39,6 +39,8 @@ import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.entity.BufferedHttpEntity; @@ -55,7 +57,18 @@ public class WizAssetsPlugin extends CordovaPlugin { private static final String DELETE_FILE_ACTION = "deleteFile"; private static final String DELETE_FILES_ACTION = "deleteFiles"; + private static final int NO_ERROR = 0; + private static final int ARGS_MISSING_ERROR = 1; + private static final int INVALID_URL_ERROR = 2; + private static final int CONNECTIVITY_ERROR = 3; + private static final int HTTP_REQUEST_ERROR = 4; + private static final int HTTP_REQUEST_CONTENT_ERROR = 5; + private static final int DIRECTORY_CREATION_ERROR = 6; + private static final int FILE_CREATION_ERROR = 7; + private static final int JSON_CREATION_ERROR = 8; + private String pathToStorage; + private int blockSize; @Override public void initialize(CordovaInterface cordova, CordovaWebView webView) { @@ -63,9 +76,29 @@ public void initialize(CordovaInterface cordova, CordovaWebView webView) { final Context applicationContext = cordova.getActivity().getApplicationContext(); pathToStorage = applicationContext.getCacheDir().getAbsolutePath() + File.separator; + setBlockSize(); + wizAssetManager = new WizAssetManager(applicationContext); } + @SuppressWarnings("deprecation") + @SuppressLint("NewApi") + private void setBlockSize() { + android.os.StatFs stat = new android.os.StatFs(pathToStorage); + long blockSizeLong; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + blockSizeLong = stat.getBlockSizeLong(); + } else { + blockSizeLong = stat.getBlockSize(); + } + if (blockSizeLong < 1024) { + blockSizeLong = 1024; + } else if (blockSizeLong > 16384) { + blockSizeLong = 16384; + } + blockSize = (int)blockSizeLong; + } + @Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { if (action.equals(DOWNLOAD_FILE_ACTION)) { @@ -117,9 +150,8 @@ public void run() { } else if (action.equals(GET_FILE_URIS_ACTION)) { // Return all assets as asset map object - Log.d(TAG, "[getFileURIs] *********** >>>>>>> "); + Log.d(TAG, "[getFileURIs] returning all assets as map"); JSONObject assetObject = wizAssetManager.getAllAssets(); - Log.d(TAG, "[getFileURIs] RETURN *********** >>>>>>> " + assetObject.toString()); callbackContext.success(assetObject); return true; @@ -300,7 +332,7 @@ protected void onPostExecute(Integer result) { } } - private class AsyncDownload extends AsyncTask { + private class AsyncDownload extends AsyncTask { private String url; private String uri; @@ -317,63 +349,121 @@ public AsyncDownload(String url, String uri, String filePath, CallbackContext ca } @Override - protected String doInBackground(File... params) { - File file = new File(this.filePath); - - // Run async download task - File dir = file.getParentFile(); - if (dir != null && !(dir.mkdirs() || dir.isDirectory())) { - Log.e("WizAssetsPlugin", "Error : subdirectory " + dir.getPath() + " could not be created"); - this.callbackContext.error("subdirectoryCreationError"); - return null; - } + protected Void doInBackground(File... params) { + try { + File file = new File(this.filePath); + + // Run async download task + File dir = file.getParentFile(); + if (dir == null || !(dir.mkdirs() || dir.isDirectory())) { + Log.e("WizAssetsPlugin", "Error : subdirectory could not be created"); + this.callbackContext.error(createDownloadFileError(DIRECTORY_CREATION_ERROR)); + return null; + } - Log.d(TAG, "[Downloading] " + file.getAbsolutePath()); + Log.d(TAG, "[Downloading] " + file.getAbsolutePath()); - try { - URL url = new URL(this.url); - HttpGet httpRequest = null; - httpRequest = new HttpGet(url.toURI()); + HttpGet httpRequest; + try { + URL url = new URL(this.url); + httpRequest = new HttpGet(url.toURI()); + + // Credential check + String credentials = url.getUserInfo(); + if (credentials != null) { + // Add Basic Authentication header + httpRequest.setHeader("Authorization", "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP)); + } + } catch (MalformedURLException e) { + this.callbackContext.error(createDownloadFileError(INVALID_URL_ERROR)); + return null; + } catch (URISyntaxException e) { + this.callbackContext.error(createDownloadFileError(INVALID_URL_ERROR)); + return null; + } + HttpResponse response; HttpClient httpclient = new DefaultHttpClient(); - - // Credential check - String credentials = url.getUserInfo(); - if (credentials != null) { - // Add Basic Authentication header - httpRequest.setHeader("Authorization", "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP)); + try { + response = httpclient.execute(httpRequest); + if (response == null) { + this.callbackContext.error(createDownloadFileError(CONNECTIVITY_ERROR)); + return null; + } + StatusLine statusLine = response.getStatusLine(); + if (statusLine == null) { + this.callbackContext.error(createDownloadFileError(HTTP_REQUEST_ERROR, "No status available")); + return null; + } + int statusCode = statusLine.getStatusCode(); + if (statusCode < 200 || statusCode > 299) { + this.callbackContext.error(createDownloadFileError(HTTP_REQUEST_ERROR, statusCode)); + return null; + } + } catch (ClientProtocolException e) { + this.callbackContext.error(createDownloadFileError(CONNECTIVITY_ERROR)); + return null; + } catch (IOException e) { + this.callbackContext.error(createDownloadFileError(CONNECTIVITY_ERROR)); + return null; } - HttpResponse response = httpclient.execute(httpRequest); HttpEntity entity = response.getEntity(); - InputStream is; + InputStream is = null; - Header contentHeader = entity.getContentEncoding(); - if (contentHeader != null) { - if (contentHeader.getValue().contains("gzip")) { - Log.d(TAG, "GGGGGGGGGZIIIIIPPPPPED!"); + Header encodingHeader = entity.getContentEncoding(); + try { + String encodingHeaderValue = encodingHeader != null ? encodingHeader.getValue() : null; + if (encodingHeaderValue != null && encodingHeaderValue.contains("gzip")) { is = new GZIPInputStream(entity.getContent()); } else { BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity); is = bufHttpEntity.getContent(); } - } else { - BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity); - is = bufHttpEntity.getContent(); + if (is == null) { + this.callbackContext.error(createDownloadFileError(HTTP_REQUEST_CONTENT_ERROR)); + return null; + } + } catch (IOException e) { + this.callbackContext.error(createDownloadFileError(HTTP_REQUEST_CONTENT_ERROR)); + return null; } - byte[] buffer = new byte[1024]; + + byte[] buffer = new byte[blockSize]; int len1 = 0; - FileOutputStream fos = new FileOutputStream(file); + FileOutputStream fos = null; + boolean exceptionThrown = false; + try { + fos = new FileOutputStream(file); - while ((len1 = is.read(buffer)) > 0 ) { - fos.write(buffer,0, len1); + while ((len1 = is.read(buffer)) > 0) { + fos.write(buffer, 0, len1); + } + } catch (FileNotFoundException e) { + exceptionThrown = true; + } catch (IOException e) { + exceptionThrown = true; + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + exceptionThrown = true; + } + } + try { + is.close(); + } catch (IOException e) { + exceptionThrown = true; + } + } + if (exceptionThrown) { + this.callbackContext.error(createDownloadFileError(FILE_CREATION_ERROR)); + return null; } - - fos.close(); - is.close(); // Tell Asset Manager to register this download to asset database String fileAbsolutePath = file.getAbsolutePath(); @@ -381,16 +471,38 @@ protected String doInBackground(File... params) { wizAssetManager.downloadedAsset(this.uri, fileAbsolutePath); this.callbackContext.success("file://" + fileAbsolutePath); - } catch (MalformedURLException e) { - Log.e("WizAssetsPlugin", "Bad url : ", e); - this.callbackContext.error("notFoundError"); - } catch (Exception e) { - Log.e("WizAssetsPlugin", "Error : " + e); - e.printStackTrace(); - this.callbackContext.error("unknownError"); + } catch (JSONException e) { + this.callbackContext.error(JSON_CREATION_ERROR); + return null; } + return null; } } -} + JSONObject createDownloadFileError(int code) throws JSONException { + return createDownloadFileError(code, -1, null); + } + + JSONObject createDownloadFileError(int code, int status) throws JSONException { + return createDownloadFileError(code, status, null); + } + + JSONObject createDownloadFileError(int code, String message) throws JSONException { + return createDownloadFileError(code, -1, message); + } + + JSONObject createDownloadFileError(int code, int status, String message) throws JSONException { + JSONObject errorObject = new JSONObject(); + errorObject.put("code", code); + if (status != -1) { + errorObject.put("status", status); + } + if (message != null) { + errorObject.put("message", message); + } else { + errorObject.put("message", "No description"); + } + return errorObject; + } +} \ No newline at end of file diff --git a/platforms/ios/HelloCordova.xcodeproj/project.pbxproj b/platforms/ios/HelloCordova.xcodeproj/project.pbxproj index 3a64ca1..be5b8af 100644 --- a/platforms/ios/HelloCordova.xcodeproj/project.pbxproj +++ b/platforms/ios/HelloCordova.xcodeproj/project.pbxproj @@ -26,7 +26,7 @@ 308D05391370CCF300D202BF /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D05301370CCF300D202BF /* icon@2x.png */; }; 30A0434814DC770100060A13 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30A0434314DC770100060A13 /* Localizable.strings */; }; 30FC414916E50CA1004E6F35 /* icon-72@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 30FC414816E50CA1004E6F35 /* icon-72@2x.png */; }; - 419DE3071A1D838F005428C9 /* WizAssetsPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 419DE3051A1D838F005428C9 /* WizAssetsPlugin.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 419DE3071A1D838F005428C9 /* WizAssetsPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 419DE3051A1D838F005428C9 /* WizAssetsPlugin.m */; }; 5B1594DD16A7569C00FEF299 /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B1594DC16A7569C00FEF299 /* AssetsLibrary.framework */; }; 7E7966DE1810823500FA85AD /* icon-40.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D41810823500FA85AD /* icon-40.png */; }; 7E7966DF1810823500FA85AD /* icon-40@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D51810823500FA85AD /* icon-40@2x.png */; }; diff --git a/platforms/ios/HelloCordova/Plugins/WizAssetsPlugin/WizAssetsPlugin.h b/platforms/ios/HelloCordova/Plugins/WizAssetsPlugin/WizAssetsPlugin.h index 907bb73..51e4d4c 100755 --- a/platforms/ios/HelloCordova/Plugins/WizAssetsPlugin/WizAssetsPlugin.h +++ b/platforms/ios/HelloCordova/Plugins/WizAssetsPlugin/WizAssetsPlugin.h @@ -11,8 +11,22 @@ #import #import +enum CDVWizAssetsError { + NO_ERROR = 0, + ARGS_MISSING_ERROR = 1, + INVALID_URL_ERROR = 2, + CONNECTIVITY_ERROR = 3, + HTTP_REQUEST_ERROR = 4, + HTTP_REQUEST_CONTENT_ERROR = 5, + DIRECTORY_CREATION_ERROR = 6, + FILE_CREATION_ERROR = 7, + JSON_CREATION_ERROR = 8 +}; +typedef int CDVWizAssetsError; + @interface WizAssetsPlugin : CDVPlugin { NSString *_cachePath; + NSURLSession *_session; } - (void)pluginInitialize; @@ -25,5 +39,6 @@ - (void)deleteFiles:(CDVInvokedUrlCommand *)command; @property (nonatomic, retain) NSString *cachePath; +@property (nonatomic, retain) NSURLSession *session; @end \ No newline at end of file diff --git a/platforms/ios/HelloCordova/Plugins/WizAssetsPlugin/WizAssetsPlugin.m b/platforms/ios/HelloCordova/Plugins/WizAssetsPlugin/WizAssetsPlugin.m index a0ec689..63f28d7 100755 --- a/platforms/ios/HelloCordova/Plugins/WizAssetsPlugin/WizAssetsPlugin.m +++ b/platforms/ios/HelloCordova/Plugins/WizAssetsPlugin/WizAssetsPlugin.m @@ -13,6 +13,7 @@ @implementation WizAssetsPlugin @synthesize cachePath = _cachePath; +@synthesize session = _session; - (void)pluginInitialize { [super pluginInitialize]; @@ -22,108 +23,11 @@ - (void)pluginInitialize { NSString *gameDir = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; self.cachePath = [documentsDirectory stringByAppendingPathComponent:gameDir]; -} - -- (void)dealloc { - [_cachePath release]; - - [super dealloc]; -} - -- (void)backgroundDownload:(NSDictionary *)args { - // Create a pool - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - CDVInvokedUrlCommand *command = [args objectForKey:@"command"]; - NSString *filePath = [args objectForKey:@"filePath"]; - NSString *fullDir = [filePath stringByDeletingLastPathComponent]; - - // url - NSString *urlString = [command.arguments objectAtIndex:0]; - // holds our return data - NSString *returnString; - - if (urlString) { - - NSFileManager *filemgr; - filemgr =[NSFileManager defaultManager]; - NSURL *url = [NSURL URLWithString:urlString]; - - WizLog(@"downloading ---------------- >%@", url); - - NSError *error = nil; - NSData *urlData = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&error]; - - if (error) { - returnString = [NSString stringWithFormat:@"error - %@", error]; - } else if (urlData) { - // Check if we didn't received a 401 - // TODO: We might want to find another solution to check for this kind of error, and check other possible errors - NSString *dataContent = [[NSString alloc] initWithBytes:[urlData bytes] length:12 encoding:NSUTF8StringEncoding]; - bool urlUnauthorized = [dataContent isEqualToString:@"Unauthorized"]; - [dataContent release]; - - if (urlUnauthorized) { - returnString = @"error - url unauthorized"; - } else { - NSError *directoryError = nil; - bool isDirectoryCreated = [filemgr createDirectoryAtPath:fullDir withIntermediateDirectories:YES attributes:nil error: &directoryError]; - - if (!isDirectoryCreated) { - if ([[directoryError domain] isEqualToString:NSCocoaErrorDomain] && [directoryError code] == NSFileWriteFileExistsError) { - // Directory already exists, it's not an error - isDirectoryCreated = true; - } else { - returnString = [NSString stringWithFormat:@"error - unable to create directory (code: %ld - domain: %@)", [directoryError code], [directoryError domain]]; - } - } - if (isDirectoryCreated) { - // Success to create directory download data to temp and move to library/cache when complete - [urlData writeToFile:filePath atomically:YES]; - returnString = filePath; - } - } - } else { - WizLog(@"ERROR: URL no exist"); - returnString = @"error - bad url"; - } - } else { - returnString = @"error - no urlString"; - } - NSArray *callbackData = [[NSArray alloc] initWithObjects:command.callbackId, returnString, nil]; - // Download complete pass back confirmation to JS - [self performSelectorOnMainThread:@selector(completeDownload:) withObject:callbackData waitUntilDone:YES]; - [callbackData release]; - - // clean up - [pool release]; -} - -/* - * completeDownload - background thread callback to JavaScript - */ -- (void)completeDownload:(NSArray *)callbackdata { - // Faked the return string for now - NSString *callbackId = [callbackdata objectAtIndex:0]; - NSString *returnString = [callbackdata objectAtIndex:1]; - [self sendCallback:callbackId returnString:returnString]; -} - - -- (void)sendCallback:(NSString *)callbackId returnString:(NSString *)returnString { - WizLog(@"Path: %@", returnString); - WizLog(@"callbackId ----------> : %@", callbackId); - - if ([returnString rangeOfString:@"error"].location == NSNotFound) { - // no error - CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:returnString]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId]; - } else { - // found error - CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:returnString]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId]; - } + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; + configuration.URLCache = nil; + self.session = [NSURLSession sessionWithConfiguration:configuration]; } /* @@ -133,9 +37,9 @@ - (void)downloadFile:(CDVInvokedUrlCommand *)command { WizLog(@"[WizAssetsPlugin] ******* downloadFile-> " ); NSUInteger count = [command.arguments count]; - if (count == 0) { - CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"noParam"]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + if (count < 2) { + CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createDownloadFileError:ARGS_MISSING_ERROR]]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; return; } @@ -146,15 +50,76 @@ - (void)downloadFile:(CDVInvokedUrlCommand *)command { NSFileManager *fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:filePath] == YES) { // download complete pass back confirmation to JS - [self sendCallback:command.callbackId returnString:filePath]; + CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:filePath]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; return; } - NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys: - command, @"command", - filePath, @"filePath", - nil]; - [self performSelectorInBackground:@selector(backgroundDownload:) withObject:args]; + NSString *urlString = [command.arguments objectAtIndex:0]; + NSURL *URL = [NSURL URLWithString:urlString]; + NSURLRequest *request = [NSURLRequest requestWithURL:URL]; + + NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request + completionHandler: + ^(NSURL *location, NSURLResponse *response, NSError *error) { + CDVPluginResult *result = nil; + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + NSInteger statusCode = httpResponse.statusCode; + if (error) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createDownloadFileError:CONNECTIVITY_ERROR message:[error description]]]; + } else if (statusCode < 200 || statusCode >= 300) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createDownloadFileError:HTTP_REQUEST_ERROR status:statusCode]]; + } else { + NSError *ioError = nil; + NSString *fullDir = [filePath stringByDeletingLastPathComponent]; + BOOL isDirectoryCreated = [fileManager createDirectoryAtPath:fullDir withIntermediateDirectories:YES attributes:nil error:&ioError]; + if (ioError) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createDownloadFileError:DIRECTORY_CREATION_ERROR message:[ioError description]]]; + } else if (!isDirectoryCreated) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createDownloadFileError:DIRECTORY_CREATION_ERROR]]; + } else { + NSURL *filePathURL = [NSURL fileURLWithPath:filePath]; + BOOL isFileMoved = [fileManager moveItemAtURL:location toURL:filePathURL error:&ioError]; + if (ioError) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createDownloadFileError:FILE_CREATION_ERROR message:[ioError description]]]; + } else if (!isFileMoved) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createDownloadFileError:FILE_CREATION_ERROR]]; + } else { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:filePath]; + } + } + } + dispatch_async(dispatch_get_main_queue(), ^{ + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + }); + }]; + + [downloadTask resume]; +} + +- (NSDictionary *)createDownloadFileError:(int)code { + return [self createDownloadFileError:code status:-1 message:nil]; +} + +- (NSDictionary *)createDownloadFileError:(int)code status:(NSInteger)status { + return [self createDownloadFileError:code status:status message:nil]; +} + +- (NSDictionary *)createDownloadFileError:(int)code message:(NSString *)message { + return [self createDownloadFileError:code status:-1 message:message]; +} + +- (NSDictionary *)createDownloadFileError:(int)code status:(NSInteger)status message:(NSString *)message { + NSMutableDictionary* error = [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:code] forKey:@"code"]; + if (status != -1) { + [error setObject:[NSNumber numberWithInteger:status] forKey:@"status"]; + } + if (message) { + [error setObject:message forKey:@"message"]; + } else { + [error setObject:@"No description" forKey:@"message"]; + } + return error; } - (void)scanDir:(NSString *)basePath relPath:(NSString *)relPath assetMap:(NSMutableDictionary *)assetMap { @@ -236,8 +201,6 @@ - (void)getFileURI:(CDVInvokedUrlCommand *)command { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:returnURI]; } - [fileStruct release]; - [fileTypeStruct release]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } @@ -266,8 +229,6 @@ - (void)getFileURIs:(CDVInvokedUrlCommand *)command { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:assetMap]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - - [assetMap release]; } /* @@ -294,27 +255,20 @@ - (void)deleteFiles:(CDVInvokedUrlCommand *)command { } - (void)backgroundDelete:(CDVInvokedUrlCommand *)command { - // Create a pool - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSMutableArray *uris = [[NSMutableArray alloc] initWithArray:command.arguments copyItems:YES]; - - NSError *error = nil; - for (int i=0; i< [uris count]; i++) { - NSString *uri = [uris objectAtIndex:i]; - [self deleteAsset:uri error:&error]; - } - [uris release]; - - NSArray *callbackData = [[NSArray alloc] initWithObjects:command.callbackId, error, nil]; + @autoreleasepool { + NSArray *uris = command.arguments; - // download complete pass back confirmation to JS - [self performSelectorOnMainThread:@selector(completeDelete:) withObject:callbackData waitUntilDone:YES]; + NSError *error = nil; + for (int i = 0; i < [uris count]; i++) { + NSString *uri = [uris objectAtIndex:i]; + [self deleteAsset:uri error:&error]; + } - [callbackData release]; + NSArray *callbackData = [[NSArray alloc] initWithObjects:command.callbackId, error, nil]; - // clean up - [pool release]; + // download complete pass back confirmation to JS + [self performSelectorOnMainThread:@selector(completeDelete:) withObject:callbackData waitUntilDone:YES]; + } } /* diff --git a/plugin.xml b/plugin.xml index a0fc294..003fc26 100644 --- a/plugin.xml +++ b/plugin.xml @@ -18,6 +18,9 @@ + + + @@ -32,10 +35,10 @@ - + - + diff --git a/www/phonegap/plugin/wizAssets/WizAssetsError.js b/www/phonegap/plugin/wizAssets/WizAssetsError.js new file mode 100644 index 0000000..ffe453b --- /dev/null +++ b/www/phonegap/plugin/wizAssets/WizAssetsError.js @@ -0,0 +1,41 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Wizcorp Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + +/** + * WizAssetsError + */ +function WizAssetsError(error) { + this.code = error || null; +} + +// WizAssets error codes +WizAssetsError.ARGS_MISSING_ERROR = 1; +WizAssetsError.INVALID_URL_ERROR = 2; +WizAssetsError.CONNECTIVITY_ERROR = 3; +WizAssetsError.HTTP_REQUEST_ERROR = 4; +WizAssetsError.HTTP_REQUEST_CONTENT_ERROR = 5; +WizAssetsError.DIRECTORY_CREATION_ERROR = 6; +WizAssetsError.FILE_CREATION_ERROR = 7; +WizAssetsError.JSON_CREATION_ERROR = 8; + +module.exports = WizAssetsError; \ No newline at end of file diff --git a/www/phonegap/plugin/wizAssets/wizAssets.js b/www/phonegap/plugin/wizAssets/wizAssets.js index 788d2ef..1fbbafa 100644 --- a/www/phonegap/plugin/wizAssets/wizAssets.js +++ b/www/phonegap/plugin/wizAssets/wizAssets.js @@ -12,7 +12,13 @@ var wizAssets = { downloadFile: function (url, filePath, s, f) { window.setTimeout( function () { - exec(s, f, "WizAssetsPlugin", "downloadFile", [url, filePath]); + function failure(error) { + if (error === WizAssetsError.JSON_CREATION_ERROR) { + error = new WizAssetsError(error); + } + return f(error); + } + exec(s, failure, "WizAssetsPlugin", "downloadFile", [url, filePath]); }, 0); }, deleteFile: function (uri, s, f) {