Repository: cordova-plugin-file Updated Branches: refs/heads/master 1e2593f42 -> 6aeb9d9d1
CB-10977 android: Removing global state used for permission requests This closes #174 Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/commit/6aeb9d9d Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/tree/6aeb9d9d Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/diff/6aeb9d9d Branch: refs/heads/master Commit: 6aeb9d9d1a91c2657218e6c81dfe2b57bd46f7d7 Parents: 1e2593f Author: Richard Knoll <richard.b.kn...@gmail.com> Authored: Mon Mar 28 12:09:21 2016 -0700 Committer: Richard Knoll <richard.b.kn...@gmail.com> Committed: Wed Mar 30 10:24:35 2016 -0700 ---------------------------------------------------------------------- plugin.xml | 1 + src/android/FileUtils.java | 142 ++++++++++++++++------------------ src/android/PendingRequests.java | 94 ++++++++++++++++++++++ 3 files changed, 163 insertions(+), 74 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/6aeb9d9d/plugin.xml ---------------------------------------------------------------------- diff --git a/plugin.xml b/plugin.xml index 4c79325..46dbbdb 100644 --- a/plugin.xml +++ b/plugin.xml @@ -146,6 +146,7 @@ to config.xml in order for the application to find previously stored files. <source-file src="src/android/LocalFilesystem.java" target-dir="src/org/apache/cordova/file" /> <source-file src="src/android/ContentFilesystem.java" target-dir="src/org/apache/cordova/file" /> <source-file src="src/android/AssetFilesystem.java" target-dir="src/org/apache/cordova/file" /> + <source-file src="src/android/PendingRequests.java" target-dir="src/org/apache/cordova/file" /> <source-file src="src/android/PermissionHelper.java" target-dir="src/org/apache/cordova/file" /> <!-- android specific file apis --> http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/6aeb9d9d/src/android/FileUtils.java ---------------------------------------------------------------------- diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java index e9169bc..cf193d0 100644 --- a/src/android/FileUtils.java +++ b/src/android/FileUtils.java @@ -73,18 +73,20 @@ public class FileUtils extends CordovaPlugin { * Permission callback codes */ - public static final int GET_FILE_CALLBACK_CODE = 0; - public static final int WRITE_CALLBACK_CODE = 1; - public static final int GET_DIRECTORY_CALLBACK_CODE = 2; + public static final int ACTION_GET_FILE = 0; + public static final int ACTION_WRITE = 1; + public static final int ACTION_GET_DIRECTORY = 2; + public static final int WRITE = 3; public static final int READ = 4; public static int UNKNOWN_ERR = 1000; private boolean configured = false; - private String lastRawArgs; - private CallbackContext callback; + private PendingRequests pendingRequests; + + /* * We need both read and write when accessing the storage, I think. @@ -171,6 +173,7 @@ public class FileUtils extends CordovaPlugin { public void initialize(CordovaInterface cordova, CordovaWebView webView) { super.initialize(cordova, webView); this.filesystems = new ArrayList<Filesystem>(); + this.pendingRequests = new PendingRequests(); String tempRoot = null; String persistentRoot = null; @@ -262,14 +265,12 @@ public class FileUtils extends CordovaPlugin { } public boolean execute(String action, final String rawArgs, final CallbackContext callbackContext) { - this.callback = callbackContext; - lastRawArgs = rawArgs; if (!configured) { callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "File plugin is not configured. Please see the README.md file for details on how to update config.xml")); return true; } if (action.equals("testSaveLocationExists")) { - threadhelper( new FileOp( ){ + threadhelper(new FileOp() { public void run(JSONArray args) { boolean b = DirectoryManager.testSaveLocationExists(); callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b)); @@ -356,7 +357,7 @@ public class FileUtils extends CordovaPlugin { Boolean isBinary=args.getBoolean(3); if(needPermission(nativeURL, WRITE)) { - getWritePermission(); + getWritePermission(rawArgs, ACTION_WRITE, callbackContext); } else { long fileSize = write(fname, data, offset, isBinary); @@ -440,10 +441,10 @@ public class FileUtils extends CordovaPlugin { boolean containsCreate = (args.isNull(2)) ? false : args.getJSONObject(2).optBoolean("create", false); if(containsCreate && needPermission(nativeURL, WRITE)) { - getPermissionDir(WRITE); + getWritePermission(rawArgs, ACTION_GET_DIRECTORY, callbackContext); } else if(!containsCreate && needPermission(nativeURL, READ)) { - getPermissionDir(READ); + getReadPermission(rawArgs, ACTION_GET_DIRECTORY, callbackContext); } else { JSONObject obj = getFile(dirname, path, args.optJSONObject(2), true); @@ -461,10 +462,10 @@ public class FileUtils extends CordovaPlugin { boolean containsCreate = (args.isNull(2)) ? false : args.getJSONObject(2).optBoolean("create", false); if(containsCreate && needPermission(nativeURL, WRITE)) { - getPermissionFile(WRITE); + getWritePermission(rawArgs, ACTION_GET_FILE, callbackContext); } else if(!containsCreate && needPermission(nativeURL, READ)) { - getPermissionFile(READ); + getReadPermission(rawArgs, ACTION_GET_FILE, callbackContext); } else { JSONObject obj = getFile(dirname, path, args.optJSONObject(2), false); @@ -547,26 +548,14 @@ public class FileUtils extends CordovaPlugin { return true; } - private void getPermissionFile(int permissionType) { - if(permissionType == READ) { - PermissionHelper.requestPermission(this, GET_FILE_CALLBACK_CODE, Manifest.permission.READ_EXTERNAL_STORAGE); - } - else { - PermissionHelper.requestPermission(this, GET_FILE_CALLBACK_CODE, Manifest.permission.WRITE_EXTERNAL_STORAGE); - } + private void getReadPermission(String rawArgs, int action, CallbackContext callbackContext) { + int requestCode = pendingRequests.createRequest(rawArgs, action, callbackContext); + PermissionHelper.requestPermission(this, requestCode, Manifest.permission.READ_EXTERNAL_STORAGE); } - private void getPermissionDir(int permissionType) { - if(permissionType == READ) { - PermissionHelper.requestPermission(this, GET_DIRECTORY_CALLBACK_CODE, Manifest.permission.READ_EXTERNAL_STORAGE); - } - else { - PermissionHelper.requestPermission(this, GET_DIRECTORY_CALLBACK_CODE, Manifest.permission.WRITE_EXTERNAL_STORAGE); - } - } - - private void getWritePermission() { - PermissionHelper.requestPermission(this, WRITE_CALLBACK_CODE, Manifest.permission.WRITE_EXTERNAL_STORAGE); + private void getWritePermission(String rawArgs, int action, CallbackContext callbackContext) { + int requestCode = pendingRequests.createRequest(rawArgs, action, callbackContext); + PermissionHelper.requestPermission(this, requestCode, Manifest.permission.WRITE_EXTERNAL_STORAGE); } private boolean hasReadPermission() { @@ -1151,51 +1140,56 @@ public class FileUtils extends CordovaPlugin { public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException { - for(int r:grantResults) - { - if(r == PackageManager.PERMISSION_DENIED) + + final PendingRequests.Request req = pendingRequests.getAndRemove(requestCode); + if (req != null) { + for(int r:grantResults) { - callback.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, SECURITY_ERR)); + if(r == PackageManager.PERMISSION_DENIED) + { + req.getCallbackContext().sendPluginResult(new PluginResult(PluginResult.Status.ERROR, SECURITY_ERR)); + return; + } } + switch(req.getAction()) + { + case ACTION_GET_FILE: + threadhelper( new FileOp( ){ + public void run(JSONArray args) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { + String dirname = args.getString(0); + + String path = args.getString(1); + JSONObject obj = getFile(dirname, path, args.optJSONObject(2), false); + req.getCallbackContext().success(obj); + } + }, req.getRawArgs(), req.getCallbackContext()); + break; + case ACTION_GET_DIRECTORY: + threadhelper( new FileOp( ){ + public void run(JSONArray args) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { + String dirname = args.getString(0); + + String path = args.getString(1); + JSONObject obj = getFile(dirname, path, args.optJSONObject(2), true); + req.getCallbackContext().success(obj); + } + }, req.getRawArgs(), req.getCallbackContext()); + break; + case ACTION_WRITE: + threadhelper( new FileOp( ){ + public void run(JSONArray args) throws JSONException, FileNotFoundException, IOException, NoModificationAllowedException { + String fname=args.getString(0); + String data=args.getString(1); + int offset=args.getInt(2); + Boolean isBinary=args.getBoolean(3); + long fileSize = write(fname, data, offset, isBinary); + req.getCallbackContext().sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize)); + } + }, req.getRawArgs(), req.getCallbackContext()); + break; + } + } else { + Log.d(LOG_TAG, "Received permission callback for unknown request code"); } - switch(requestCode) - { - case GET_FILE_CALLBACK_CODE: - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { - String dirname = args.getString(0); - - String path = args.getString(1); - JSONObject obj = getFile(dirname, path, args.optJSONObject(2), false); - callback.success(obj); - } - }, lastRawArgs, callback); - break; - case GET_DIRECTORY_CALLBACK_CODE: - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { - String dirname = args.getString(0); - - String path = args.getString(1); - JSONObject obj = getFile(dirname, path, args.optJSONObject(2), true); - callback.success(obj); - } - }, lastRawArgs, callback); - break; - case WRITE_CALLBACK_CODE: - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, FileNotFoundException, IOException, NoModificationAllowedException { - String fname=args.getString(0); - String data=args.getString(1); - int offset=args.getInt(2); - Boolean isBinary=args.getBoolean(3); - long fileSize = write(fname, data, offset, isBinary); - callback.sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize)); - } - }, lastRawArgs, callback); - break; - - } - } } http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/6aeb9d9d/src/android/PendingRequests.java ---------------------------------------------------------------------- diff --git a/src/android/PendingRequests.java b/src/android/PendingRequests.java new file mode 100644 index 0000000..23d6d73 --- /dev/null +++ b/src/android/PendingRequests.java @@ -0,0 +1,94 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.file; + +import android.util.SparseArray; + +import org.apache.cordova.CallbackContext; + +/** + * Holds pending runtime permission requests + */ +class PendingRequests { + private int currentReqId = 0; + private SparseArray<Request> requests = new SparseArray<Request>(); + + /** + * Creates a request and adds it to the array of pending requests. Each created request gets a + * unique result code for use with requestPermission() + * @param rawArgs The raw arguments passed to the plugin + * @param action The action this request corresponds to (get file, etc.) + * @param callbackContext The CallbackContext for this plugin call + * @return The request code that can be used to retrieve the Request object + */ + public synchronized int createRequest(String rawArgs, int action, CallbackContext callbackContext) { + Request req = new Request(rawArgs, action, callbackContext); + requests.put(req.requestCode, req); + return req.requestCode; + } + + /** + * Gets the request corresponding to this request code and removes it from the pending requests + * @param requestCode The request code for the desired request + * @return The request corresponding to the given request code or null if such a + * request is not found + */ + public synchronized Request getAndRemove(int requestCode) { + Request result = requests.get(requestCode); + requests.remove(requestCode); + return result; + } + + /** + * Holds the options and CallbackContext for a call made to the plugin. + */ + public class Request { + + // Unique int used to identify this request in any Android permission callback + private int requestCode; + + // Action to be performed after permission request result + private int action; + + // Raw arguments passed to plugin + private String rawArgs; + + // The callback context for this plugin request + private CallbackContext callbackContext; + + private Request(String rawArgs, int action, CallbackContext callbackContext) { + this.rawArgs = rawArgs; + this.action = action; + this.callbackContext = callbackContext; + this.requestCode = currentReqId ++; + } + + public int getAction() { + return this.action; + } + + public String getRawArgs() { + return rawArgs; + } + + public CallbackContext getCallbackContext() { + return callbackContext; + } + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cordova.apache.org For additional commands, e-mail: commits-h...@cordova.apache.org