apache / cordova-plugin-camera

Apache Cordova Plugin camera
https://cordova.apache.org/
Apache License 2.0
966 stars 1.55k forks source link

Cannot use saveToPhotoAlbum: true on Android 10 #611

Closed AleFons closed 3 years ago

AleFons commented 4 years ago

Bug Report

Problem

What is expected to happen?

Using saveToPhotoAlbum: true should allow the user to take a picture and save a backup to the phone's gallery.

What does actually happen?

The plugin instead throws an error on Android 10.

Information

Setting the option to false avoids the problem, but then no backup is made.

Command or Code

I use these options for Camera.getPicture():

annex_options: CameraOptions = {
    destinationType: this.Camera.DestinationType.FILE_URI,
    sourceType: this.Camera.PictureSourceType.PHOTOLIBRARY,
    correctOrientation: false,
    saveToPhotoAlbum: false
  }

Environment, Platform, Device

Android 10, has happened on multiple devices.

Version information

Ionic:

ionic (Ionic CLI) : 4.6.0 Ionic Framework : ionic-angular 3.9.6 @ionic/app-scripts : 3.2.4

Cordova:

cordova (Cordova CLI) : 9.0.0 (cordova-lib@9.0.1) Cordova Platforms : android 8.1.0, ios 5.0.0 Cordova Plugins : cordova-plugin-ionic-keyboard 2.0.5, cordova-plugin-ionic-webview 2.2.0, (and 18 other plugins)

System:

Android SDK Tools : 26.1.1 NodeJS : v12.16.3 npm : 6.14.4 OS : Linux 4.15

Checklist

bartdeveloper commented 4 years ago

I also confirm that issue. On Android 9 all features works great, but after update my device to Android 10 I got "Error capturing image" when flag saveToPhotoAlbum is set to the value true.

Device: Samsung A40

AleFons commented 4 years ago

From my understanding, what appears to be happening is that saving to photo album is triggering an access to storage outside the app's permitted storage, as android 10 comes with changes to where the app is permitted to do file operations. It may be possible to fix it with android:requestLegacyExternalStorage="true" but I have had issues with testing that.

bartdeveloper commented 4 years ago

Unfortunately requestLegacyExternalStorage in config.xml doesn't work. I tried this code:

`

    </edit-config>`

And I got:

AAPT: error: attribute android:requestLegacyExternalStorage not found.

bartdeveloper commented 4 years ago

I downgraded compileSdkVersion from 29 to 28 and now saveToPhotoAlbum works properly.

AleFons commented 4 years ago

Even on an Android 10 phone?

bartdeveloper commented 4 years ago

Yes, I tested this solution on Samsung A40 with Android 10. When I had Android 9 then all camera features worked properly with setting compileSdkVersion=29, but after upgrade to Android 10 I had to downgrade compileSdkVersion. Of course if you utilize features from Android 10, then this solution isn't correct.

kriskhoavu commented 4 years ago

@bartdeveloper Appreciate if you could share your git repo link.

breautek commented 4 years ago

Looks like API 29 requires Scoped Locations

There are two current workarounds:

  1. Reduce the target sdk to 28, which can be done via the android-targetSdkVersion preference.
  2. Use the requestLegacyExternalStorage flag. Which can be added using the <edit-config>. This flag requires you to target/compile for API 29 (the default in cordova-android@9)

I'm not familiar with this plugin's codebase, but it looks like MediaStore is already used, and that's what the Android docs suggest that we need to use. So further investigation is going to be required to determine what the actual problem is.

The plugin instead throws an error on Android 10.

It would be nice to know exactly what that error is, and perhaps stacktrace.

isamuAsial commented 4 years ago

The commit e9aba07926072a069acefe614726e171c25004a6 is bad and causes this issue.

erisu commented 4 years ago

@isamuAsial That commit is not officially released and only exists in master.

I would recommend using 4.1.0 which had been voted on and official released for production use. 4.1.0 release does not contain that commit.

AleFons commented 4 years ago

Looks like API 29 requires Scoped Locations

There are two current workarounds:

  1. Reduce the target sdk to 28, which can be done via the android-targetSdkVersion preference.
  2. Use the requestLegacyExternalStorage flag. Which can be added using the <edit-config>. This flag requires you to target/compile for API 29 (the default in cordova-android@9)

I'm not familiar with this plugin's codebase, but it looks like MediaStore is already used, and that's what the Android docs suggest that we need to use. So further investigation is going to be required to determine what the actual problem is.

The plugin instead throws an error on Android 10.

It would be nice to know exactly what that error is, and perhaps stacktrace.

I had white screen issues when trying to use that flag.

jwasnoggin commented 4 years ago

It would be nice to know exactly what that error is, and perhaps stacktrace.

This is the stacktrace that I got in the logcat:

java.io.FileNotFoundException: open failed: EACCES (Permission denied)
    at android.os.ParcelFileDescriptor.openInternal(ParcelFileDescriptor.java:315)
    at android.os.ParcelFileDescriptor.open(ParcelFileDescriptor.java:220)
    at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1498)
    at android.content.ContentResolver.openOutputStream(ContentResolver.java:1227)
    at android.content.ContentResolver.openOutputStream(ContentResolver.java:1203)
    at org.apache.cordova.camera.CameraLauncher.writeUncompressedImage(CameraLauncher.java:866)
    at org.apache.cordova.camera.CameraLauncher.writeUncompressedImage(CameraLauncher.java:902)
    at org.apache.cordova.camera.CameraLauncher.processResultFromCamera(CameraLauncher.java:508)
    at org.apache.cordova.camera.CameraLauncher.onActivityResult(CameraLauncher.java:806)
    at org.apache.cordova.CordovaInterfaceImpl.onActivityResult(CordovaInterfaceImpl.java:159)
    at org.apache.cordova.CordovaActivity.onActivityResult(CordovaActivity.java:361)

I was ultimately able to resolve the error using the requestLegacyExternalStorage flag.

anatolykazantsev commented 4 years ago

@isamuAsial That commit is not officially released and only exists in master.

I would recommend using 4.1.0 which had been voted on and official released for production use. 4.1.0 release does not contain that commit.

We're using 4.1.0 and we have exactly same problem on Android 10 devices. The stacktrace is same as provided by @jwasnoggin above. So it seems production version is affected as well.

Cordova CLI: 10.0.0 Cordova platform: android 9.0.0

olidotjpeg commented 4 years ago

I am having this same issue on API SDK 29, tried adding this snippet to my Android platform in the config.xml file.

<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application" xmlns:android="http://schemas.android.com/apk/res/android"> 
    <application android:requestLegacyExternalStorage="true" /> 
</edit-config>

Which either causes the app to crash when I accept permissions or do nothing at all.

Cordova CLI: 10.0.0 Cordova Platform : Android 9.0.0 As I mentioned above I am building for Android 29

jsBiztech commented 4 years ago

Hello, Is there any solution for this yet? I have tried solutions given in this, but the issue persists.

pc-partner commented 4 years ago

Hello, Is there any solution for this yet? I have tried solutions given in this, but the issue persists.

Same here.

wifisher commented 4 years ago

Not sure if this is related or not, but v5.0.0 of the plugin fails for me as well with saveToPhotoAlbum set true. For me, it is also failing on my Android 8 test device with the following stack trace:

 java.io.FileNotFoundException: content:/com.fi.pmp.cordova.plugin.camera.provider/cache_files/.Pic.jpg (No such file or directory)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(FileInputStream.java:200)
    at java.io.FileInputStream.<init>(FileInputStream.java:150)
    at java.io.FileInputStream.<init>(FileInputStream.java:103)
    at org.apache.cordova.camera.CameraLauncher.writeUncompressedImage(CameraLauncher.java:906)
    at org.apache.cordova.camera.CameraLauncher.processResultFromCamera(CameraLauncher.java:503)
    at org.apache.cordova.camera.CameraLauncher.onActivityResult(CameraLauncher.java:808)
    at org.apache.cordova.CordovaInterfaceImpl.onActivityResult(CordovaInterfaceImpl.java:159)
    at org.apache.cordova.CordovaActivity.onActivityResult(CordovaActivity.java:361)
    at android.app.Activity.dispatchActivityResult(Activity.java:7599)
    at android.app.ActivityThread.deliverResults(ActivityThread.java:4487)
    at android.app.ActivityThread.handleSendResult(ActivityThread.java:4534)
    at android.app.ActivityThread.-wrap20(Unknown Source:0)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1752)
    at android.os.Handler.dispatchMessage(Handler.java:105)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6944)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

It appears that the source URI has a content: scheme instead of a file: scheme that writeUncompressedImage() expects.

I was able to get it working by changing line 903 of CameraLauncher.java from:

FileInputStream fis = new FileInputStream(FileHelper.stripFileProtocol(src.toString()));

to

InputStream fis = this.cordova.getActivity().getContentResolver().openInputStream(src);

openInputStream() is documented to accept both file: and content: schemes, so this change should handle both cases.

With this change, saveToPhotoAlbum works again on this Android 8 device. I haven't had a chance to test on any other devices yet.

alex96gm commented 4 years ago

Hello, Is there any solution for this yet? I have tried solutions given in this, but the issue persists.

Same here. Any solution or workarounds for Android 10

wifisher commented 4 years ago

With my above change, it is now working for me on my Android 10 device using "cordova-android": "^9.0.0" and "cordova-plugin-camera": "^5.0.0".

The options that I am passing in to getPicture() are:

            sourceType: navigator.camera.PictureSourceType.CAMERA
            destinationType: navigator.camera.DestinationType.FILE_URI,,
            encodingType: navigator.camera.EncodingType.JPEG,
            quality: 75,
            saveToPhotoAlbum: true,
            targetWidth: 2048,
            targetHeight: 2048
jsBiztech commented 4 years ago

Hey @wifisher , Have you tried setting targetSDKVersion to 29? Thanks!

wifisher commented 4 years ago

Isn't the targetSDKVersion set to 29 by cordova-android 9.0.0?

jsBiztech commented 4 years ago

Isn't the targetSDKVersion set to 29 by cordova-android 9.0.0?

I am unaware of this. Can you check whether it is 29 or not?

wifisher commented 4 years ago

According to https://cordova.apache.org/announcements/2020/06/29/cordova-android-9.0.0.html the default targetSDKVersion is 29 with cordova-android 9.0.0.

I did a test by setting the version in config.xml with the following line: <preference name="android-targetSdkVersion" value="29"/>

I was able to take a picture and have it appear in the gallery.

olidotjpeg commented 4 years ago

The app I am working on we also have <preference name="android-targetSdkVersion" value="29" /> enabled, but it still fails when the savePhotoToAlbum is enabled. Only way to fix is force it to sdk28 which is not preferred :/

wifisher commented 4 years ago

You can test my fix by making the change to CameraLauncher.java that I mentioned above. Test by making the change to the file at this location: platforms/android/app/src/main/java/org/apache/cordova/camera. Then build and run.

If it works, then your problem is the same as mine. I wouldn't build a release with this change hacked in, but it would be useful to know if it is the same problem or not.

It's a small change, but it would be nice to see it come out in a 5.0.1 version of the camera plugin.

PieterVanPoyer commented 4 years ago

@wifisher I did made a change to the code. I've read your suggestion of the content uri.

You can test the changes with next commands.

npx cordova plugin remove cordova-plugin-camera
npx cordova plugin add https://github.com/PieterVanPoyer/cordova-plugin-camera.git#bugfix/issue-341-save-to-photo-gallery

Can you test this? After testing I could make a PR for this.

Another remark: It should be saved to the photo album, if you take a picture with the camera. This issue is about selecting from the gallery and saving a copy to the gallery of it. ( sourceType: this.Camera.PictureSourceType.PHOTOLIBRARY, ) But that's another issue. The issue you are refering to is https://github.com/apache/cordova-plugin-camera/issues/577 or https://github.com/apache/cordova-plugin-camera/issues/341

Kind regards.

wifisher commented 4 years ago

@PieterVanPoyer I installed your plugin and it appears to be working correctly for me.

basvdijk commented 4 years ago

I "solved" this issue by using Capacitor instead of Cordova using https://capacitorjs.com/docs/apis/camera

olidotjpeg commented 4 years ago

@PieterVanPoyer I can confirm that your fix is also working for me.

jsBiztech commented 4 years ago

@PieterVanPoyer , I have following config:

Ionic Framework : ionic-angular 3.9.2
@ionic/app-scripts : 3.2.4
Cordova CLI : 8.1.2
Cordova Platforms : android 7.1.4, ios 4.5.5
cordova-plugin-camera : 4.0.3

Should updating Cordova-plugin-camera to 5.0 resolve this without changing the Cordova version? Thanks!

PieterVanPoyer commented 4 years ago

I did make a Pull Request to solve the issue with saveToPhotoAlbum and the camera. https://github.com/apache/cordova-plugin-camera/pull/669 But this code is not in the master. You could try (for testing purposes) to install my fork (for now).

npx cordova plugin remove cordova-plugin-camera
npx cordova plugin add https://github.com/PieterVanPoyer/cordova-plugin-camera.git#bugfix/issue-341-save-to-photo-gallery

I don't think updating (or installing the fork) is possible without updating the Cordova CLI and cordova android version. (Current version of the cli is 10, I believe.)

Kind regards

HarelM commented 4 years ago

I'm using the latest version of this plugin from master after the above merge but it seems that it doesn't work with android 10, even if I set requestLegacyExternalStorage to true (using the latest cordova-file-plugin adds this to the manifest). When I'm trying to open the camera to take a picture I get to the following code:

if (takePicturePermission && saveAlbumPermission) {
            takePicture(returnType, encodingType);
        } else if (saveAlbumPermission && !takePicturePermission) {
            PermissionHelper.requestPermission(this, TAKE_PIC_SEC, Manifest.permission.CAMERA);
        } else if (!saveAlbumPermission && takePicturePermission) {
/// ---> I'm getting here which doesn't do anything in android 10...
            PermissionHelper.requestPermissions(this, TAKE_PIC_SEC,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE});
        } else {
            PermissionHelper.requestPermissions(this, TAKE_PIC_SEC, permissions);
        }

Has anyone else got to this point? Am I missing something? Should I just remove the saveToPhoto album? Any help would be appreciated. Cordova 10, cordova-android 9.0, of course.

PieterVanPoyer commented 4 years ago

@HarelM I was able to reproduce it.

In my testsetup I removed the <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - tag of the AndroidManifest.xml. Is this tag present in your AndroidManifest?

image

When I remove this line <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />, I can reproduce the behaviour. What is the content of your AndroidManifest.xml?

Kind regards Pieter

jsBiztech commented 4 years ago

Hello @PieterVanPoyer ,

I have used the latest Camera plugin version and I am facing an issue in which I am unable to upload files from the google drive.

I have created an issue in the file plugin repo, but haven't got a reply. If you can look into it. Here is the link: https://github.com/apache/cordova-plugin-file/issues/432

Thanks.

HarelM commented 4 years ago

@PieterVanPoyer Thanks! Seems like this permission is now missing, I have no idea why, I see it on both the camera plugin plugin.xml and file plugin plugin.xml with the same annotation:

<config-file target="AndroidManifest.xml" parent="/*">
            <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        </config-file>

But it doesn't get to the manifest.xml file as it used to. Since I upgraded to Cordova 10 the entire environment had become shaky and I need to place stuff in different places in the 'package.json` file to make the app compile. So it might be another bug I'm just not sure where to report it...

PieterVanPoyer commented 4 years ago

@HarelM indeed normally the permission must be set by cordova itself. Because the WRITE_EXTERNAL_STORAGE must be declared in the manifest, else the user is unable to allow it later. The reason is, that the permission holds a high risk and it must be whitelisted. [ https://developer.android.com/reference/android/Manifest.permission.html#WRITE_EXTERNAL_STORAGE ] It is indeed correctly declared in the plugin.xml. Does removing the platform and plugin and afterwards reïnstalling it solve the problem?

Can you reproduce it in a blank Cordova 10 project in anyway, with just the camera plugin included? If yes, then you could make a new issue for the camera plugin.

HarelM commented 4 years ago

I'm not sure I can reproduce it on a blank project. but my project is open source and the CI build machine is always a clean start. The turn point from my point of view is upgrading the cordova cli version and not the camera plugin... I have opened some issues in the cordova-fetch and cordova-cli project but didn't get any response yet...

HarelM commented 4 years ago

BTW @PieterVanPoyer, after fixing all the permission issues and build issues I still don't see the image I take in the photo gallery so I'm not sure the latest version (after the #669 PR merged here) does fix this bug in all android versions... Let me know if you want me to test anything specific... Package.json and config.xml can be found here: https://github.com/IsraelHikingMap/Site/tree/master/IsraelHiking.Web

PieterVanPoyer commented 4 years ago

@HarelM I won't be able to test every Android device offcourse.

But just to be curious, is the taken picture returned to your app? So it is only the save to gallery that fails, or do you receive an error in your javascript. If yes, what is the error?

Do you use DATA_URI or FILE_URI as type? In what Android version does it fail? Android 10, Android 11, Android 9, ... ?

HarelM commented 4 years ago

I'm using android 9. The image is returned to the client without any error as far as I understand. I'm using DATA_URI as can be seen here. @PieterVanPoyer I fully understand you won't be able to test every android device, this is why I'm reporting this, so I can help in testing. You can see here the configuration: https://github.com/IsraelHikingMap/Site/blob/master/IsraelHiking.Web/sources/application/directives/image-capture.directive.ts#L49

PieterVanPoyer commented 4 years ago

Originally, there was an error thrown and no image was returned.

You could try to test with next repo: https://github.com/PieterVanPoyer/cordova-camera-plugin-testing-app/tree/master If you set the camera plugin to the master, you could try this repo.

Then maybe we can pinpoint the issue. Do you test on an emulator or on a real device?

HarelM commented 4 years ago

My bad, I now see the photo I took in the Pictures folder (not the camera folder, which is what google photos is showing). Thanks for the fix! Sorry for miss-leading...

PieterVanPoyer commented 4 years ago

No problem. We figured out the permission issue. Cheers!

jsBiztech commented 4 years ago

Hello @PieterVanPoyer ,

I have used the latest Camera plugin version and I am facing an issue in which I am unable to upload files from the google drive.

I have created an issue in the file plugin repo, but haven't got a reply. If you can look into it. Here is the link: apache/cordova-plugin-file#432

Thanks.

Can anyone help me on this?

PieterVanPoyer commented 3 years ago

@AleFons is this issue still happening with the latest released version? If not, feel free to close this issue.

Franco-Alejandro commented 3 years ago

Any updates on this?

byronigoe commented 3 years ago

Can't speak for @AleFons, but I encountered the same issue, and the latest version resolved it for me.

AleFons commented 3 years ago

The latest version has solved it for me too. I believe the issue has been solved with Cordova 10.

PieterVanPoyer commented 3 years ago

@AleFons nice, maybe you should close this issue then? Kind regards Pieter

mfsikalyan commented 3 years ago

From my understanding, what appears to be happening is that saving to photo album is triggering an access to storage outside the app's permitted storage, as android 10 comes with changes to where the app is permitted to do file operations. It may be possible to fix it with android:requestLegacyExternalStorage="true" but I have had issues with testing that.

This worked for me, it is a great solution