使用Cordova 进行跨平台应用程序的开发
1.创建Cordova项目
$ cordova create hello com.example.hello HelloWorld
2.添加插件
2.1切换到Plugins目录
2.2 添加一下插件
cordova plugin add cordova-plugin-device cordova plugin add cordova-plugin-file cordova plugin add cordova-plugin-file-transfer cordova plugin add https://github.com/pwlin/cordova-plugin-file-opener2
3.修改插件
3.1需要修改Android项目的插件:
3.1.1修改cordova-plugin-file-transfer 下边的 FileTransfer.java 文件 ,引入import android.os.Environment;
/** * 获取当前目录在sdcard中的路径 * @param rootFolder 根目录 * @param aFolder 当前目录 */ public String getStorageDirectory(String rootFolder,String aFolder){ String storagePath= Environment.getExternalStorageDirectory().getPath()+"/"+rootFolder+"/"+aFolder; return storagePath; } /** * 创建目录 * @param fileDirectory 目录名称 */ public File createDirectory(String fileDirectory){ File sdcardFile=new File(fileDirectory); if(!sdcardFile.exists()){ sdcardFile.mkdirs(); } return sdcardFile; } /** * Downloads a file form a given URL and saves it to the specified directory. * * @param source URL of the server to receive the file * @param target Full path of the file on the file system */ private void download(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException { Log.d(LOG_TAG, "download " + source + " to " + target); String localPath=this.getStorageDirectory("GaussQGY", "download"); this.createDirectory(localPath); final CordovaResourceApi resourceApi = webView.getResourceApi(); final boolean trustEveryone = args.optBoolean(2); final String objectId = args.getString(3); final JSONObject headers = args.optJSONObject(4); final Uri sourceUri = resourceApi.remapUri(Uri.parse(source)); final String targetPath = localPath +'/'+ target; Log.e(LOG_TAG, "文件存放路径: " + targetPath); // Accept a path or a URI for the source. Uri tmpTarget = Uri.parse(targetPath); final Uri targetUri = resourceApi.remapUri( tmpTarget.getScheme() != null ? tmpTarget : Uri.fromFile(new File(targetPath))); int uriType = CordovaResourceApi.getUriType(sourceUri); final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS; final boolean isLocalTransfer = !useHttps && uriType != CordovaResourceApi.URI_TYPE_HTTP; if (uriType == CordovaResourceApi.URI_TYPE_UNKNOWN) { JSONObject error = createFileTransferError(INVALID_URL_ERR, source, targetPath, null, 0, null); Log.e(LOG_TAG, "Unsupported URI: " + sourceUri); callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); return; } /* This code exists for compatibility between 3.x and 4.x versions of Cordova. * Previously the CordovaWebView class had a method, getWhitelist, which would * return a Whitelist object. Since the fixed whitelist is removed in Cordova 4.x, * the correct call now is to shouldAllowRequest from the plugin manager. */ Boolean shouldAllowRequest = null; if (isLocalTransfer) { shouldAllowRequest = true; } if (shouldAllowRequest == null) { try { Method gwl = webView.getClass().getMethod("getWhitelist"); Whitelist whitelist = (Whitelist)gwl.invoke(webView); shouldAllowRequest = whitelist.isUrlWhiteListed(source); } catch (NoSuchMethodException e) { } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } } if (shouldAllowRequest == null) { try { Method gpm = webView.getClass().getMethod("getPluginManager"); PluginManager pm = (PluginManager)gpm.invoke(webView); Method san = pm.getClass().getMethod("shouldAllowRequest", String.class); shouldAllowRequest = (Boolean)san.invoke(pm, source); } catch (NoSuchMethodException e) { } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } } if (!Boolean.TRUE.equals(shouldAllowRequest)) { Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'"); JSONObject error = createFileTransferError(CONNECTION_ERR, source, targetPath, null, 401, null); callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); return; } final RequestContext context = new RequestContext(source, targetPath, callbackContext); synchronized (activeRequests) { activeRequests.put(objectId, context); } cordova.getThreadPool().execute(new Runnable() { public void run() { if (context.aborted) { return; } HttpURLConnection connection = null; HostnameVerifier oldHostnameVerifier = null; SSLSocketFactory oldSocketFactory = null; File file = null; PluginResult result = null; TrackingInputStream inputStream = null; boolean cached = false; OutputStream outputStream = null; try { OpenForReadResult readResult = null; file = resourceApi.mapUriToFile(targetUri); context.targetFile = file; Log.d(LOG_TAG, "Download file:" + sourceUri); FileProgressResult progress = new FileProgressResult(); if (isLocalTransfer) { readResult = resourceApi.openForRead(sourceUri); if (readResult.length != -1) { progress.setLengthComputable(true); progress.setTotal(readResult.length); } inputStream = new SimpleTrackingInputStream(readResult.inputStream); } else { // connect to server // Open a HTTP connection to the URL based on protocol connection = resourceApi.createHttpConnection(sourceUri); if (useHttps && trustEveryone) { // Setup the HTTPS connection class to trust everyone HttpsURLConnection https = (HttpsURLConnection)connection; oldSocketFactory = trustAllHosts(https); // Save the current hostnameVerifier oldHostnameVerifier = https.getHostnameVerifier(); // Setup the connection not to verify hostnames https.setHostnameVerifier(DO_NOT_VERIFY); } connection.setRequestMethod("GET"); // TODO: Make OkHttp use this CookieManager by default. String cookie = getCookies(sourceUri.toString()); if(cookie != null) { connection.setRequestProperty("cookie", cookie); } // This must be explicitly set for gzip progress tracking to work. connection.setRequestProperty("Accept-Encoding", "gzip"); // Handle the other headers if (headers != null) { addHeadersToRequest(connection, headers); } connection.connect(); if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) { cached = true; connection.disconnect(); Log.d(LOG_TAG, "Resource not modified: " + source); JSONObject error = createFileTransferError(NOT_MODIFIED_ERR, source, targetPath, connection, null); result = new PluginResult(PluginResult.Status.ERROR, error); } else { if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) { // Only trust content-length header if we understand // the encoding -- identity or gzip if (connection.getContentLength() != -1) { progress.setLengthComputable(true); progress.setTotal(connection.getContentLength()); } } inputStream = getInputStream(connection); } } if (!cached) { try { synchronized (context) { if (context.aborted) { return; } context.connection = connection; } // write bytes to file byte[] buffer = new byte[MAX_BUFFER_SIZE]; int bytesRead = 0; outputStream = resourceApi.openOutputStream(targetUri); while ((bytesRead = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, bytesRead); // Send a progress event. progress.setLoaded(inputStream.getTotalRawBytesRead()); PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject()); progressResult.setKeepCallback(true); context.sendPluginResult(progressResult); } } finally { synchronized (context) { context.connection = null; } safeClose(inputStream); safeClose(outputStream); } Log.d(LOG_TAG, "Saved file: " + targetPath); // create FileEntry object Class webViewClass = webView.getClass(); PluginManager pm = null; try { Method gpm = webViewClass.getMethod("getPluginManager"); pm = (PluginManager) gpm.invoke(webView); } catch (NoSuchMethodException e) { } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } if (pm == null) { try { Field pmf = webViewClass.getField("pluginManager"); pm = (PluginManager)pmf.get(webView); } catch (NoSuchFieldException e) { } catch (IllegalAccessException e) { } } file = resourceApi.mapUriToFile(targetUri); context.targetFile = file; FileUtils filePlugin = (FileUtils) pm.getPlugin("File"); if (filePlugin != null) { JSONObject fileEntry = filePlugin.getEntryForFile(file); if (fileEntry != null) { result = new PluginResult(PluginResult.Status.OK, fileEntry); } else { JSONObject error = createFileTransferError(CONNECTION_ERR, source, targetPath, connection, null); Log.e(LOG_TAG, "File plugin cannot represent download path"); result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error); } } else { Log.e(LOG_TAG, "File plugin not found; cannot save downloaded file"); result = new PluginResult(PluginResult.Status.ERROR, "File plugin not found; cannot save downloaded file"); } } } catch (FileNotFoundException e) { JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, targetPath, connection, e); Log.e(LOG_TAG, error.toString(), e); result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error); } catch (IOException e) { JSONObject error = createFileTransferError(CONNECTION_ERR, source, targetPath, connection, e); Log.e(LOG_TAG, error.toString(), e); result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); result = new PluginResult(PluginResult.Status.JSON_EXCEPTION); } catch (Throwable e) { JSONObject error = createFileTransferError(CONNECTION_ERR, source, targetPath, connection, e); Log.e(LOG_TAG, error.toString(), e); result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error); } finally { synchronized (activeRequests) { activeRequests.remove(objectId); } if (connection != null) { // Revert back to the proper verifier and socket factories if (trustEveryone && useHttps) { HttpsURLConnection https = (HttpsURLConnection) connection; https.setHostnameVerifier(oldHostnameVerifier); https.setSSLSocketFactory(oldSocketFactory); } } if (result == null) { result = new PluginResult(PluginResult.Status.ERROR, createFileTransferError(CONNECTION_ERR, source, targetPath, connection, null)); } // Remove incomplete download. if (!cached && result.getStatus() != PluginResult.Status.OK.ordinal() && file != null) { file.delete(); } context.sendPluginResult(result); } } }); }
3.1.2 修改Android项目下的 cordova-plugin-file-opener2 找到FileOpener2.java ,然后引入 import android.os.Environment;
修改_open方法
private void _open(String fileArg, String contentType, CallbackContext callbackContext) throws JSONException { String fileName = ""; String filePath = Environment.getExternalStorageDirectory().getPath() + fileArg; try { CordovaResourceApi resourceApi = webView.getResourceApi(); Uri fileUri = resourceApi.remapUri(Uri.parse(filePath)); fileName = this.stripFileProtocol(fileUri.toString()); } catch (Exception e) { fileName = filePath; } File file = new File(fileName); if (file.exists()) { try { Uri path = Uri.fromFile(file); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(path, contentType); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); /* * @see * http://stackoverflow.com/questions/14321376/open-an-activity-from-a-cordovaplugin */ cordova.getActivity().startActivity(intent); //cordova.getActivity().startActivity(Intent.createChooser(intent,"Open File in...")); callbackContext.success(); } catch (android.content.ActivityNotFoundException e) { JSONObject errorObj = new JSONObject(); errorObj.put("status", PluginResult.Status.ERROR.ordinal()); errorObj.put("message", "Activity not found: " + e.getMessage()); callbackContext.error(errorObj); } } else { JSONObject errorObj = new JSONObject(); errorObj.put("status", PluginResult.Status.ERROR.ordinal()); errorObj.put("message", "File not found"); callbackContext.error(errorObj); } }
3.2 需要修改iOS项目中的Cordova 插件 下的 cordova-plugin-file-opener2
3.2.1 修改FileOpener2.h
定义一个openURL 方法
#import <Cordova/CDV.h> @interface FileOpener2 : CDVPlugin <UIDocumentInteractionControllerDelegate> { NSString *localFile; } @property(nonatomic, strong) UIDocumentInteractionController *controller; - (void) open: (CDVInvokedUrlCommand*)command; - (void) openURL: (CDVInvokedUrlCommand*)command; @end
3.2.2 修改FileOpener2.m
实现FileOpener2.h中定义的openURL 方法
#import "FileOpener2.h" #import <Cordova/CDV.h> #import <QuartzCore/QuartzCore.h> #import <MobileCoreServices/MobileCoreServices.h> @implementation FileOpener2 #pragma mask --- 加载其它应用 - (void)openURL:(CDVInvokedUrlCommand *)command { NSString *path = command.arguments[0]; NSURL *url= [NSURL URLWithString: path]; [[UIApplication sharedApplication] openURL:url]; } #pragma mask --- 打开本地文件 - (void) open: (CDVInvokedUrlCommand*)command { NSString *path = command.arguments[0]; NSString *uti = command.arguments[1]; if (!uti || (NSNull*)uti == [NSNull null]) { NSArray *dotParts = [path componentsSeparatedByString:@"."]; NSString *fileExt = [dotParts lastObject]; uti = (__bridge NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExt, NULL); } CDVViewController* cont = (CDVViewController*)[ super viewController ]; dispatch_async(dispatch_get_main_queue(), ^{ // TODO: test if this is a URI or a path NSURL *fileURL = [NSURL URLWithString:path]; localFile = fileURL.path; NSLog(@"looking for file at %@", fileURL); NSFileManager *fm = [NSFileManager defaultManager]; if(![fm fileExistsAtPath:localFile]) { NSDictionary *jsonObj = @{@"status" : @"9", @"message" : @"File does not exist"}; CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:jsonObj]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; return; } self.controller = [UIDocumentInteractionController interactionControllerWithURL:fileURL]; self.controller.delegate = self; self.controller.UTI = uti; CGRect rect = CGRectMake(0, 0, 1000.0f, 150.0f); CDVPluginResult* pluginResult = nil; BOOL wasOpened = [self.controller presentOptionsMenuFromRect:rect inView:cont.view animated:NO]; if(wasOpened) { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: @""]; } else { NSDictionary *jsonObj = @{@"status" : @"9", @"message" : @"Could not handle UTI"}; pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:jsonObj]; } [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }); } @end