My capacitor app uses the Camera plugin to take pictures and/or allow the user to select photos from the gallery. In Android 7, iOS, and on the web, the app works correctly. In Android 10, the gallery works, but the camera does not. There are two issues:
First, in Android 10, the camera does not save the image to the gallery, but in Android 7 it does. The code for launching the camera is:
import { Camera, CameraResultType, Photo, CameraSource } from '@capacitor/camera';
import { CameraHelper } from "./CameraHelper"; // This hooks into a custom webpack build rule for bundling PWA on web, but not including on Android/iOS
export function takePicture(success: (image: string) => void, failure: (reason: any) => void) {
let f = () => {
Camera.getPhoto({
quality: 90,
allowEditing: false,
direction: CameraDirection.Front, // Commenting this does not change the behavior
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
saveToGallery: true // Commenting this does not change the behavior
}).then((image: Photo) => {
success(image.webPath!);
}).catch((reason: any) => {
failure(reason);
});
}
// NB: on web/electron, need to load PWA support first
if (platform === "web") {
new CameraHelper().injectPwa(f);
}
else {
f();
}
}
Second, my Android 10 device stops the activity when it switches to the camera, but I don’t get an appRestoredResult
when my app restarts. To be clear: I understand that the Android lifecycle allows for the app to stop and be restarted. That’s not my issue. My issue is that I make a checkpoint, but since I am not getting an appRestoredResult
event, I can’t restore my checkpoint. The code for listening for restart runs immediately when the app begins (not called from an async/await
function):
App.addListener('appRestoredResult', (event: RestoredListenerEvent) => {
console.log('Restored state:', event.data);
console.log('Restored state:', event.error);
console.log('Restored state:', event.methodName);
console.log('Restored state:', event.pluginId);
console.log('Restored state:', event.success);
}).then((v: PluginListenerHandle) => {
console.log("Handler installed");
});
While “Handler installed” does get printed to the log, the other messages do not… No event ever fires.
To chase this down, I started putting Log.d
messages into CameraPlugin.java
file, to see where things break. Things go badly right away:
public void processCameraImage(PluginCall call, ActivityResult result) {
if (imageFileSavePath == null) {
// processCameraImage() is always returning here, because imageFileSavePath is null
call.reject(IMAGE_PROCESS_NO_FILE_ERROR);
return;
}
// ...
}
In retrospect, it makes sense that when the imageFileSavePath
is null, the app won’t get an appRestoredResult
event, because there is nothing to use to restore. And if the camera plugin isn’t working correctly, then it would make sense for imageFileSavePath
to be null. So my guess is that the second problem will go away if I can figure out why the camera isn’t saving its picture to the gallery, or anywhere else.
The only thing I can think of is that there is some kind of misconfiguration of permissions in my app, which is causing the Camera to fail. But I cannot find any advice online about configuration changes for Capacitor and/or the Camera plugin for Android 10. And as stated earlier, the code works on Android 7, iOS, and PWA/Web.
Here are the important parts of the app’s configuration. First, the manifest’s permissions:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="com.android.vending.BILLING" />
<application
android:allowBackup="true"
android:fullBackupOnly="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:hardwareAccelerated="true"
android:requestLegacyExternalStorage="true">
// ...
Next, variables.gradle
:
ext {
minSdkVersion = 21
compileSdkVersion = 29
targetSdkVersion = 29
andridxActivityVersion = '1.2.0'
androidxAppCompatVersion = '1.3.1'
androidxCoreVersion = '1.3.2'
androidxMaterialVersion = '1.1.0-rc02'
androidxBrowserVersion = '1.2.0'
androidxLocalbroadcastmanagerVersion = '1.0.0'
androidxExifInterfaceVersion = '1.2.0'
firebaseMessagingVersion = '20.1.2'
playServicesLocationVersion = '17.0.0'
junitVersion = '4.12'
androidxJunitVersion = '1.1.3'
androidxEspressoCoreVersion = '3.4.0'
cordovaAndroidVersion = '7.0.0'
}
Finally: here is my npx cap doctor
result:
💊 Capacitor Doctor 💊
Latest Dependencies:
@capacitor/cli: 3.2.0
@capacitor/core: 3.2.0
@capacitor/android: 3.2.0
@capacitor/ios: 3.2.0
Installed Dependencies:
@capacitor/ios: not installed
@capacitor/cli: 3.2.0
@capacitor/android: 3.2.0
@capacitor/core: 3.2.0
[success] Android looking great! 👌
[error] Xcode is not installed
Any suggestions would be greatly appreciated!