Repository: cordova-plugin-camera
Updated Branches:
  refs/heads/2.3.x cc1076d3c -> 9eba35e2f


CB-11625: Working on fix to API 24 no longer allowing File URIs to be passed 
across intents


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/commit/3d26986b
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/tree/3d26986b
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/diff/3d26986b

Branch: refs/heads/2.3.x
Commit: 3d26986bfd5b7894ee16c6785f7996ef8c4b5c4f
Parents: b695717
Author: Joe Bowser <bows...@apache.org>
Authored: Wed Jul 27 14:06:07 2016 -0700
Committer: Joe Bowser <bows...@apache.org>
Committed: Wed Jul 27 14:06:07 2016 -0700

----------------------------------------------------------------------
 plugin.xml                      |  17 ++-
 src/android/CameraLauncher.java | 197 ++++++++++++++++++++---------------
 2 files changed, 126 insertions(+), 88 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/blob/3d26986b/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index 99769ab..eb24adb 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -69,6 +69,17 @@
         <config-file target="AndroidManifest.xml" parent="/*">
             <uses-permission 
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
         </config-file>
+        <config file target="AndroidManifest.xml" parent="application">
+          <provider
+              android:name="android.support.v4.content.FileProvider"
+              android:authorities="${applicationId}.provider"
+              android:exported="false"
+              android:grantUriPermissions="true">
+              <meta-data
+                  android:name="android.support.FILE_PROVIDER_PATHS"
+                  android:resource="@xml/provider_paths"/>
+          </provider>
+        </config>
 
         <source-file src="src/android/CameraLauncher.java" 
target-dir="src/org/apache/cordova/camera" />
         <source-file src="src/android/FileHelper.java" 
target-dir="src/org/apache/cordova/camera" />
@@ -76,9 +87,11 @@
 
         <js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
             <clobbers target="CameraPopoverHandle" />
-        </js-module>
+          </js-module>
 
-     </platform>
+        <framework src="com.android.support:support-v4:24.1.1+" />
+
+      </platform>
 
     <!-- amazon-fireos -->
     <platform name="amazon-fireos">

http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/blob/3d26986b/src/android/CameraLauncher.java
----------------------------------------------------------------------
diff --git a/src/android/CameraLauncher.java b/src/android/CameraLauncher.java
index 9db9911..2f4a1e0 100644
--- a/src/android/CameraLauncher.java
+++ b/src/android/CameraLauncher.java
@@ -36,6 +36,7 @@ import org.apache.cordova.CordovaResourceApi;
 import org.apache.cordova.LOG;
 import org.apache.cordova.PermissionHelper;
 import org.apache.cordova.PluginResult;
+import org.apache.mobilespec.BuildConfig;
 import org.json.JSONArray;
 import org.json.JSONException;
 
@@ -56,6 +57,8 @@ import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.provider.MediaStore;
+import android.provider.OpenableColumns;
+import android.support.v4.content.FileProvider;
 import android.util.Base64;
 import android.util.Log;
 import android.content.pm.PackageManager;
@@ -281,8 +284,12 @@ public class CameraLauncher extends CordovaPlugin 
implements MediaScannerConnect
 
         // Specify file so that large image is captured and returned
         File photo = createCaptureFile(encodingType);
-        intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, 
Uri.fromFile(photo));
-        this.imageUri = Uri.fromFile(photo);
+        this.imageUri = FileProvider.getUriForFile(cordova.getActivity(),
+                BuildConfig.APPLICATION_ID + ".provider",
+                photo);
+        intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, 
this.imageUri);
+        //We can write to this URI, this will hopefully allow us to write 
files to get to the next step
+        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
 
         if (this.cordova != null) {
             // Let's check to make sure the camera is actually installed. 
(Legacy Nexus 7 code)
@@ -416,7 +423,9 @@ public class CameraLauncher extends CordovaPlugin 
implements MediaScannerConnect
           cropIntent.putExtra("aspectY", 1);
       }
       // create new file handle to get full resolution crop
-      croppedUri = Uri.fromFile(createCaptureFile(this.encodingType, 
System.currentTimeMillis() + ""));
+        croppedUri = FileProvider.getUriForFile(cordova.getActivity(),
+                BuildConfig.APPLICATION_ID + ".provider",
+                createCaptureFile(this.encodingType, 
System.currentTimeMillis() + ""));
       cropIntent.putExtra("output", croppedUri);
 
       // start the activity - we handle returning in onActivityResult
@@ -449,9 +458,11 @@ public class CameraLauncher extends CordovaPlugin 
implements MediaScannerConnect
 
         // Create an ExifHelper to save the exif data that is lost during 
compression
         ExifHelper exif = new ExifHelper();
+
         String sourcePath = (this.allowEdit && this.croppedUri != null) ?
-            FileHelper.stripFileProtocol(this.croppedUri.toString()) :
-            FileHelper.stripFileProtocol(this.imageUri.toString());
+                getFileNameFromUri(this.croppedUri) :
+                getFileNameFromUri(this.imageUri);
+
 
         if (this.encodingType == JPEG) {
             try {
@@ -472,12 +483,15 @@ public class CameraLauncher extends CordovaPlugin 
implements MediaScannerConnect
         // in the gallery and the modified image is saved in the temporary
         // directory
         if (this.saveToPhotoAlbum) {
-            galleryUri = Uri.fromFile(new File(getPicutresPath()));
+            galleryUri = Uri.fromFile(new File(getPicturesPath()));
+
 
-            if(this.allowEdit && this.croppedUri != null) {
-                writeUncompressedImage(this.croppedUri, galleryUri);
+            if (this.allowEdit && this.croppedUri != null) {
+                Uri croppedUri = Uri.fromFile(new 
File(getFileNameFromUri(this.croppedUri)));
+                writeUncompressedImage(croppedUri, galleryUri);
             } else {
-                writeUncompressedImage(this.imageUri, galleryUri);
+                Uri imageUri = Uri.fromFile(new 
File(getFileNameFromUri(this.imageUri)));
+                writeUncompressedImage(imageUri, galleryUri);
             }
 
             refreshGallery(galleryUri);
@@ -489,7 +503,7 @@ public class CameraLauncher extends CordovaPlugin 
implements MediaScannerConnect
 
             if (bitmap == null) {
                 // Try to get the bitmap from intent.
-                bitmap = (Bitmap)intent.getExtras().get("data");
+                bitmap = (Bitmap) intent.getExtras().get("data");
             }
 
             // Double-check the bitmap.
@@ -523,10 +537,12 @@ public class CameraLauncher extends CordovaPlugin 
implements MediaScannerConnect
                 } else {
                     Uri uri = 
Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + 
""));
 
-                    if(this.allowEdit && this.croppedUri != null) {
-                        writeUncompressedImage(this.croppedUri, uri);
+                    if (this.allowEdit && this.croppedUri != null) {
+                        Uri croppedUri = Uri.fromFile(new 
File(getFileNameFromUri(this.croppedUri)));
+                        writeUncompressedImage(croppedUri, uri);
                     } else {
-                        writeUncompressedImage(this.imageUri, uri);
+                        Uri imageUri = Uri.fromFile(new 
File(getFileNameFromUri(this.imageUri)));
+                        writeUncompressedImage(imageUri, uri);
                     }
 
                     this.callbackContext.success(uri.toString());
@@ -575,32 +591,30 @@ public class CameraLauncher extends CordovaPlugin 
implements MediaScannerConnect
         bitmap = null;
     }
 
-private String getPicutresPath()
-{
-    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new 
Date());
-    String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? 
".jpg" : ".png");
-    File storageDir = Environment.getExternalStoragePublicDirectory(
-            Environment.DIRECTORY_PICTURES);
-    String galleryPath = storageDir.getAbsolutePath() + "/" + imageFileName;
-    return galleryPath;
-}
+    private String getPicturesPath() {
+        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new 
Date());
+        String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG 
? ".jpg" : ".png");
+        File storageDir = Environment.getExternalStoragePublicDirectory(
+                Environment.DIRECTORY_PICTURES);
+        String galleryPath = storageDir.getAbsolutePath() + "/" + 
imageFileName;
+        return galleryPath;
+    }
 
-private void refreshGallery(Uri contentUri)
-{
-    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
-    mediaScanIntent.setData(contentUri);
-    this.cordova.getActivity().sendBroadcast(mediaScanIntent);
-}
+    private void refreshGallery(Uri contentUri) {
+        Intent mediaScanIntent = new 
Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+        mediaScanIntent.setData(contentUri);
+        this.cordova.getActivity().sendBroadcast(mediaScanIntent);
+    }
 
 
-private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
+    private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws 
IOException {
         // Some content: URIs do not map to file paths (e.g. picasa).
         String realPath = FileHelper.getRealPath(uri, this.cordova);
 
         // Get filename from uri
         String fileName = realPath != null ?
-            realPath.substring(realPath.lastIndexOf('/') + 1) :
-            "modified." + (this.encodingType == JPEG ? "jpg" : "png");
+                realPath.substring(realPath.lastIndexOf('/') + 1) :
+                "modified." + (this.encodingType == JPEG ? "jpg" : "png");
 
         String modifiedPath = getTempDirectoryPath() + "/" + fileName;
 
@@ -632,12 +646,11 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
     }
 
 
-
-/**
+    /**
      * Applies all needed transformation to the image received from the 
gallery.
      *
-     * @param destType          In which form should we return the image
-     * @param intent            An Intent, which can return result data to the 
caller (various data can be attached to Intent "extras").
+     * @param destType In which form should we return the image
+     * @param intent   An Intent, which can return result data to the caller 
(various data can be attached to Intent "extras").
      */
     private void processResultFromGallery(int destType, Intent intent) {
         Uri uri = intent.getData();
@@ -658,8 +671,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) 
throws IOException {
         // and there will be no attempt to resize any returned data
         if (this.mediaType != PICTURE) {
             this.callbackContext.success(fileLocation);
-        }
-        else {
+        } else {
             // This is a special case to just return the path as no scaling,
             // rotating, nor compressing needs to be done
             if (this.targetHeight == -1 && this.targetWidth == -1 &&
@@ -709,8 +721,8 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) 
throws IOException {
                 // If sending filename back
                 else if (destType == FILE_URI || destType == NATIVE_URI) {
                     // Did we modify the image?
-                    if ( (this.targetHeight > 0 && this.targetWidth > 0) ||
-                            (this.correctOrientation && 
this.orientationCorrected) ) {
+                    if ((this.targetHeight > 0 && this.targetWidth > 0) ||
+                            (this.correctOrientation && 
this.orientationCorrected)) {
                         try {
                             String modifiedPath = 
this.ouputModifiedBitmap(bitmap, uri);
                             // The modified image is cached by the app in 
order to get around this and not have to delete you
@@ -721,8 +733,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) 
throws IOException {
                             e.printStackTrace();
                             this.failPicture("Error retrieving image.");
                         }
-                    }
-                    else {
+                    } else {
                         this.callbackContext.success(fileLocation);
                     }
                 }
@@ -738,10 +749,10 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
     /**
      * Called when the camera view exits.
      *
-     * @param requestCode       The request code originally supplied to 
startActivityForResult(),
-     *                          allowing you to identify who this result came 
from.
-     * @param resultCode        The integer result code returned by the child 
activity through its setResult().
-     * @param intent            An Intent, which can return result data to the 
caller (various data can be attached to Intent "extras").
+     * @param requestCode The request code originally supplied to 
startActivityForResult(),
+     *                    allowing you to identify who this result came from.
+     * @param resultCode  The integer result code returned by the child 
activity through its setResult().
+     * @param intent      An Intent, which can return result data to the 
caller (various data can be attached to Intent "extras").
      */
     public void onActivityResult(int requestCode, int resultCode, Intent 
intent) {
 
@@ -778,12 +789,12 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
             // If image available
             if (resultCode == Activity.RESULT_OK) {
                 try {
-                    if(this.allowEdit)
-                    {
-                        Uri tmpFile = 
Uri.fromFile(createCaptureFile(this.encodingType));
+                    if (this.allowEdit) {
+                        Uri tmpFile = 
FileProvider.getUriForFile(cordova.getActivity(),
+                                BuildConfig.APPLICATION_ID + ".provider",
+                                createCaptureFile(this.encodingType));
                         performCrop(tmpFile, destType, intent);
-                    }
-                    else {
+                    } else {
                         this.processResultFromCamera(destType, intent);
                     }
                 } catch (IOException e) {
@@ -812,11 +823,9 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) 
throws IOException {
                         processResultFromGallery(finalDestType, i);
                     }
                 });
-            }
-            else if (resultCode == Activity.RESULT_CANCELED) {
+            } else if (resultCode == Activity.RESULT_CANCELED) {
                 this.failPicture("Selection cancelled.");
-            }
-            else {
+            } else {
                 this.failPicture("Selection did not complete!");
             }
         }
@@ -824,7 +833,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) 
throws IOException {
 
     private int getImageOrientation(Uri uri) {
         int rotate = 0;
-        String[] cols = { MediaStore.Images.Media.ORIENTATION };
+        String[] cols = {MediaStore.Images.Media.ORIENTATION};
         try {
             Cursor cursor = 
cordova.getActivity().getContentResolver().query(uri,
                     cols, null, null, null);
@@ -855,13 +864,10 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
             matrix.setRotate(rotate, (float) bitmap.getWidth() / 2, (float) 
bitmap.getHeight() / 2);
         }
 
-        try
-        {
+        try {
             bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), 
bitmap.getHeight(), matrix, true);
             exif.resetOrientation();
-        }
-        catch (OutOfMemoryError oom)
-        {
+        } catch (OutOfMemoryError oom) {
             // You can run out of memory if the image is very large:
             // 
http://simonmacdonald.blogspot.ca/2012/07/change-to-camera-code-in-phonegap-190.html
             // If this happens, simply do not rotate the image and return it 
unmodified.
@@ -897,14 +903,14 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
                 try {
                     os.close();
                 } catch (IOException e) {
-                    LOG.d(LOG_TAG,"Exception while closing output stream.");
+                    LOG.d(LOG_TAG, "Exception while closing output stream.");
                 }
             }
             if (fis != null) {
                 try {
                     fis.close();
                 } catch (IOException e) {
-                    LOG.d(LOG_TAG,"Exception while closing file input 
stream.");
+                    LOG.d(LOG_TAG, "Exception while closing file input 
stream.");
                 }
             }
         }
@@ -953,7 +959,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) 
throws IOException {
                     try {
                         fileStream.close();
                     } catch (IOException e) {
-                        LOG.d(LOG_TAG,"Exception while closing file input 
stream.");
+                        LOG.d(LOG_TAG, "Exception while closing file input 
stream.");
                     }
                 }
             }
@@ -972,14 +978,13 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
                 try {
                     fileStream.close();
                 } catch (IOException e) {
-                    LOG.d(LOG_TAG,"Exception while closing file input 
stream.");
+                    LOG.d(LOG_TAG, "Exception while closing file input 
stream.");
                 }
             }
         }
 
         //CB-2292: WTF? Why is the width null?
-        if(options.outWidth == 0 || options.outHeight == 0)
-        {
+        if (options.outWidth == 0 || options.outHeight == 0) {
             return null;
         }
 
@@ -998,7 +1003,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) 
throws IOException {
                 try {
                     fileStream.close();
                 } catch (IOException e) {
-                    LOG.d(LOG_TAG,"Exception while closing file input 
stream.");
+                    LOG.d(LOG_TAG, "Exception while closing file input 
stream.");
                 }
             }
         }
@@ -1067,15 +1072,15 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
      * @return
      */
     public static int calculateSampleSize(int srcWidth, int srcHeight, int 
dstWidth, int dstHeight) {
-        final float srcAspect = (float)srcWidth / (float)srcHeight;
-        final float dstAspect = (float)dstWidth / (float)dstHeight;
+        final float srcAspect = (float) srcWidth / (float) srcHeight;
+        final float dstAspect = (float) dstWidth / (float) dstHeight;
 
         if (srcAspect > dstAspect) {
             return srcWidth / dstWidth;
         } else {
             return srcHeight / dstHeight;
         }
-      }
+    }
 
     /**
      * Creates a cursor that can be used to determine how many images we have.
@@ -1085,7 +1090,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
     private Cursor queryImgDB(Uri contentStore) {
         return this.cordova.getActivity().getContentResolver().query(
                 contentStore,
-                new String[] { MediaStore.Images.Media._ID },
+                new String[]{MediaStore.Images.Media._ID},
                 null,
                 null,
                 null);
@@ -1093,6 +1098,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
 
     /**
      * Cleans up after picture taking. Checking for duplicates and that kind 
of stuff.
+     *
      * @param newImage
      */
     private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap 
bitmap) {
@@ -1144,6 +1150,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
 
     /**
      * Determine if we are storing the images in internal or external storage
+     *
      * @return Uri
      */
     private Uri whichContentStore() {
@@ -1192,7 +1199,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
 
     private void scanForGallery(Uri newImage) {
         this.scanMe = newImage;
-        if(this.conn != null) {
+        if (this.conn != null) {
             this.conn.disconnect();
         }
         this.conn = new 
MediaScannerConnection(this.cordova.getActivity().getApplicationContext(), 
this);
@@ -1200,9 +1207,9 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
     }
 
     public void onMediaScannerConnected() {
-        try{
+        try {
             this.conn.scanFile(this.scanMe.toString(), "image/*");
-        } catch (java.lang.IllegalStateException e){
+        } catch (java.lang.IllegalStateException e) {
             LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking 
picture");
         }
 
@@ -1214,18 +1221,14 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
 
 
     public void onRequestPermissionResult(int requestCode, String[] 
permissions,
-                                          int[] grantResults) throws 
JSONException
-    {
-        for(int r:grantResults)
-        {
-            if(r == PackageManager.PERMISSION_DENIED)
-            {
+                                          int[] grantResults) throws 
JSONException {
+        for (int r : grantResults) {
+            if (r == PackageManager.PERMISSION_DENIED) {
                 this.callbackContext.sendPluginResult(new 
PluginResult(PluginResult.Status.ERROR, PERMISSION_DENIED_ERROR));
                 return;
             }
         }
-        switch(requestCode)
-        {
+        switch (requestCode) {
             case TAKE_PIC_SEC:
                 takePicture(this.destType, this.encodingType);
                 break;
@@ -1254,11 +1257,11 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
         state.putBoolean("correctOrientation", this.correctOrientation);
         state.putBoolean("saveToPhotoAlbum", this.saveToPhotoAlbum);
 
-        if(this.croppedUri != null) {
+        if (this.croppedUri != null) {
             state.putString("croppedUri", this.croppedUri.toString());
         }
 
-        if(this.imageUri != null) {
+        if (this.imageUri != null) {
             state.putString("imageUri", this.imageUri.toString());
         }
 
@@ -1278,14 +1281,36 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri 
uri) throws IOException {
         this.correctOrientation = state.getBoolean("correctOrientation");
         this.saveToPhotoAlbum = state.getBoolean("saveToPhotoAlbum");
 
-        if(state.containsKey("croppedUri")) {
+        if (state.containsKey("croppedUri")) {
             this.croppedUri = Uri.parse(state.getString("croppedUri"));
         }
 
-        if(state.containsKey("imageUri")) {
+        if (state.containsKey("imageUri")) {
             this.imageUri = Uri.parse(state.getString("imageUri"));
         }
 
         this.callbackContext = callbackContext;
     }
+
+/*
+ * This is dirty, but it does the job.
+  *
+  * Since the FilesProvider doesn't really provide you a way of getting a URL 
from the file,
+  * and since we actually need the Camera to create the file for us most of 
the time, we don't
+  * actually write the file, just generate the location based on a timestamp, 
we need to get it
+  * back from the Intent.
+  *
+  * However, the FilesProvider preserves the path, so we can at least write to 
it from here, since
+  * we own the context in this case.
+ */
+
+
+    private String getFileNameFromUri(Uri uri) {
+        String fullUri = uri.toString();
+        String partial_path = fullUri.split("external_files")[1];
+        File external_storage = Environment.getExternalStorageDirectory();
+        String path = external_storage.getAbsolutePath() + partial_path;
+        return path;
+
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cordova.apache.org
For additional commands, e-mail: commits-h...@cordova.apache.org

Reply via email to