@@ -61,6 +61,12 @@ Licensed to the Apache Software Foundation (ASF) under one
6161import java .io .OutputStream ;
6262import java .text .SimpleDateFormat ;
6363import java .util .Date ;
64+ import java .util .ArrayList ;
65+ import java .nio .file .Files ;
66+
67+ import android .webkit .MimeTypeMap ;
68+ import android .provider .OpenableColumns ;
69+ import android .util .Log ;
6470
6571/**
6672 * This class launches the camera view, allows the user to take a picture, closes the camera view,
@@ -227,34 +233,19 @@ else if ((this.srcType == PHOTOLIBRARY) || (this.srcType == SAVEDPHOTOALBUM)) {
227233 //--------------------------------------------------------------------------
228234
229235 private String [] getPermissions (boolean storageOnly , int mediaType ) {
230- if (android .os .Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
231- if (storageOnly ) {
232- switch (mediaType ) {
233- case PICTURE :
234- return new String []{ Manifest .permission .READ_MEDIA_IMAGES };
235- case VIDEO :
236- return new String []{ Manifest .permission .READ_MEDIA_VIDEO };
237- default :
238- return new String []{ Manifest .permission .READ_MEDIA_IMAGES , Manifest .permission .READ_MEDIA_VIDEO };
239- }
240- }
241- else {
242- switch (mediaType ) {
243- case PICTURE :
244- return new String []{ Manifest .permission .CAMERA , Manifest .permission .CAMERA , Manifest .permission .READ_MEDIA_IMAGES };
245- case VIDEO :
246- return new String []{ Manifest .permission .CAMERA , Manifest .permission .READ_MEDIA_VIDEO };
247- default :
248- return new String []{ Manifest .permission .CAMERA , Manifest .permission .READ_MEDIA_IMAGES , Manifest .permission .READ_MEDIA_VIDEO };
249- }
250- }
251- } else {
252- if (storageOnly ) {
253- return new String []{Manifest .permission .READ_EXTERNAL_STORAGE , Manifest .permission .WRITE_EXTERNAL_STORAGE };
254- } else {
255- return new String []{Manifest .permission .CAMERA , Manifest .permission .READ_EXTERNAL_STORAGE , Manifest .permission .WRITE_EXTERNAL_STORAGE };
256- }
236+ ArrayList <String > permissions = new ArrayList <>();
237+
238+ if (android .os .Build .VERSION .SDK_INT <= Build .VERSION_CODES .R ) {
239+ // Android API 30 or lower
240+ permissions .add (Manifest .permission .READ_EXTERNAL_STORAGE );
241+ permissions .add (Manifest .permission .WRITE_EXTERNAL_STORAGE );
257242 }
243+ if (!storageOnly ) {
244+ // Add camera permission when not storage.
245+ permissions .add (Manifest .permission .CAMERA );
246+ }
247+
248+ return permissions .toArray (new String [0 ]);
258249 }
259250
260251 private String getTempDirectoryPath () {
@@ -381,7 +372,10 @@ private File createCaptureFile(int encodingType, String fileName) {
381372 throw new IllegalArgumentException ("Invalid Encoding Type: " + encodingType );
382373 }
383374
384- return new File (getTempDirectoryPath (), fileName );
375+ File cacheDir = new File (getTempDirectoryPath (), "org.apache.cordova.camera" );
376+ cacheDir .mkdir ();
377+
378+ return new File (cacheDir , fileName );
385379 }
386380
387381
@@ -529,7 +523,7 @@ private void processResultFromCamera(int destType, Intent intent) throws IOExcep
529523 if (this .allowEdit && this .croppedUri != null ) {
530524 writeUncompressedImage (croppedUri , galleryUri );
531525 } else {
532- if (Build .VERSION .SDK_INT <= 28 ) { // Between LOLLIPOP_MR1 and P, can be changed later to the constant Build.VERSION_CODES.P
526+ if (Build .VERSION .SDK_INT <= Build .VERSION_CODES .P ) {
533527 writeTakenPictureToGalleryLowerThanAndroidQ (galleryUri );
534528 } else { // Android Q or higher
535529 writeTakenPictureToGalleryStartingFromAndroidQ (galleryPathVO );
@@ -752,7 +746,13 @@ private void processResultFromGallery(int destType, Intent intent) {
752746 // If you ask for video or the selected file cannot be processed
753747 // there will be no attempt to resize any returned data.
754748 if (this .mediaType == VIDEO || !isImageMimeTypeProcessable (mimeTypeOfGalleryFile )) {
755- this .callbackContext .success (finalLocation );
749+
750+ if (Build .VERSION .SDK_INT < Build .VERSION_CODES .UPSIDE_DOWN_CAKE ) {
751+ Uri tempUri = copyFromUriToTempFile (uri );
752+ this .callbackContext .success (tempUri .toString ());
753+ } else {
754+ this .callbackContext .success (finalLocation );
755+ }
756756 } else {
757757
758758 // This is a special case to just return the path as no scaling,
@@ -1022,7 +1022,7 @@ private Bitmap getScaledAndRotatedBitmap(String imageUrl) throws IOException {
10221022 // Generate a temporary file
10231023 String timeStamp = new SimpleDateFormat (TIME_FORMAT ).format (new Date ());
10241024 String fileName = "IMG_" + timeStamp + (getExtensionForEncodingType ());
1025- localFile = new File (getTempDirectoryPath () + fileName );
1025+ localFile = new File (getTempDirectoryPath (), fileName );
10261026 galleryUri = Uri .fromFile (localFile );
10271027 writeUncompressedImage (fileStream , galleryUri );
10281028 try {
@@ -1408,7 +1408,7 @@ public Bundle onSaveInstanceState() {
14081408 }
14091409
14101410 if (this .imageUri != null ) {
1411- state .putString (IMAGE_URI_KEY , this .imageFilePath );
1411+ state .putString (IMAGE_URI_KEY , this .imageUri . toString () );
14121412 }
14131413
14141414 if (this .imageFilePath != null ) {
@@ -1455,4 +1455,78 @@ private boolean hasPermissions(String[] permissions) {
14551455 }
14561456 return true ;
14571457 }
1458+
1459+ public Uri copyFromUriToTempFile (Uri uri ) {
1460+ InputStream inputStream = null ;
1461+ OutputStream outputStream = null ;
1462+
1463+ try {
1464+ inputStream = cordova .getActivity ().getContentResolver ().openInputStream (uri );
1465+
1466+ String filename = getFileName (uri );
1467+ String extension = null ;
1468+
1469+ if (filename == null ) {
1470+ filename = String .valueOf (System .currentTimeMillis ());
1471+ } else {
1472+ int lastDotIndex = filename .lastIndexOf ('.' );
1473+
1474+ if (lastDotIndex != -1 ) {
1475+ extension = filename .substring (lastDotIndex );
1476+ filename = filename .substring (0 , lastDotIndex );
1477+ }
1478+ }
1479+
1480+ if (extension == null ) {
1481+ extension = "." + getFileExtension (uri );
1482+ }
1483+
1484+ File file = File .createTempFile ("tmp_" + filename + "_" , extension );
1485+
1486+ outputStream = Files .newOutputStream (file .toPath ());
1487+
1488+ byte [] buffer = new byte [4096 ];
1489+ int bytesRead ;
1490+
1491+ while ((bytesRead = inputStream .read (buffer )) != -1 ) {
1492+ outputStream .write (buffer , 0 , bytesRead );
1493+ }
1494+
1495+ outputStream .flush ();
1496+ return Uri .fromFile (file );
1497+ } catch (Exception e ) {
1498+ Log .e ("TMA" , "CameraLauncher exception" , e );
1499+ return null ;
1500+
1501+ } finally {
1502+ try {
1503+ if (inputStream != null ) inputStream .close ();
1504+ } catch (Exception ignored ) {}
1505+
1506+ try {
1507+ if (outputStream != null ) outputStream .close ();
1508+ } catch (Exception ignored ) {}
1509+ }
1510+ }
1511+
1512+ private String getFileExtension (Uri uri ) {
1513+ return MimeTypeMap .getSingleton ()
1514+ .getExtensionFromMimeType (cordova .getContext ().getContentResolver ().getType (uri ));
1515+ }
1516+
1517+ private String getFileName (Uri uri ) {
1518+ String [] projection = new String [] { OpenableColumns .DISPLAY_NAME };
1519+ Cursor returnCursor =
1520+ cordova .getContext ().getContentResolver ().query (uri , projection , null , null , null );
1521+
1522+ String name = null ;
1523+
1524+ if (returnCursor != null ) {
1525+ returnCursor .moveToFirst ();
1526+ name = returnCursor .getString (0 );
1527+ returnCursor .close ();
1528+ }
1529+
1530+ return name ;
1531+ }
14581532}
0 commit comments