ionic-team / ionic-team/capacitor-camera
bug(android): NullPointerException on ActivityResultLauncher.unregister() in LegacyCameraFlow
- Dominant language
- TypeScript
- Stars
- 4
- Forks
- 1
- Avg merge
- 2d 1h
- Merged PRs (30d)
- 2
Description
## Bug Report
### Plugin version
`@capacitor/camera` 8.2.0 – 8.2.3 (still present on `main`)
### Platform
Android (observed on Android 14/15/16)
### Current Behavior
Opening the system photo picker via `Camera.pickImages()` / `Camera.getPhoto()` can crash the app when returning from the gallery.
Fatal crash:
```
java.lang.NullPointerException: Attempt to invoke virtual method
'void androidx.activity.result.ActivityResultLauncher.unregister()'
on a null object reference
at com.capacitorjs.plugins.camera.LegacyCameraFlow.lambda$openPhotos$3 (LegacyCameraFlow.java:349)
at androidx.activity.result.ActivityResultRegistry.register (ActivityResultRegistry.kt:177)
at com.capacitorjs.plugins.camera.LegacyCameraFlow.registerActivityResultLauncher (LegacyCameraFlow.java:301)
at com.capacitorjs.plugins.camera.LegacyCameraFlow.openPhotos (LegacyCameraFlow.java:324)
at com.capacitorjs.plugins.camera.LegacyCameraFlow.pickImages (LegacyCameraFlow.java:120)
at com.capacitorjs.plugins.camera.CameraPlugin.pickImages (CameraPlugin.kt:132)
```
### Root cause
In `LegacyCameraFlow.openPhotos`, the activity-result callback calls `pickMultipleMedia.unregister()` / `pickMedia.unregister()` without a null check:
https://github.com/ionic-team/capacitor-camera/blob/main/android/src/main/java/com/capacitorjs/plugins/camera/LegacyCameraFlow.java#L349-L363
`ActivityResultRegistry.register()` can synchronously invoke the callback when a pending result exists (e.g. after the Activity was destroyed while the Photo Picker was open and later recreated). At that moment the field has not been assigned yet (`pickMultipleMedia` / `pickMedia` is still `null`), so `unregister()` NPEs.
Note: `onDestroy()` already null-checks before `unregister()`, but the callbacks in `openPhotos` do not.
### Expected Behavior
Returning from the gallery after Activity recreation should not crash. Selected images should still be processed when possible.
### Reproduction
1. Capacitor app with `@capacitor/camera`
2. Android Developer Options → enable **Don't keep activities**
3. Call `Camera.pickImages({ quality: 80, limit: 5 })` (or `getPhoto` / gallery source)
4. Select photo(s) and return
5. App crashes with the NPE above
Also reproducible on low-memory devices when the OS kills the Activity while the system Photo Picker is in the foreground.
### Proposed Fix
Null-check before unregister in both callbacks (same pattern as `onDestroy()`):
```java
if (pickMultipleMedia != null) {
pickMultipleMedia.unregister();
}
if (pickMedia != null) {
pickMedia.unregister();
}
```
A slightly more robust approach is to capture the launcher in a local `final` holder assigned before the callback can run, so unregister always targets the instance created by that `register()` call.
### Related
Previously reported (wrong repo, closed without fix, redirected here):
https://github.com/ionic-team/capacitor-plugins/issues/2537
### Additional context
Affects production apps using the deprecated `pickImages` / `getPhoto` APIs that still go through `LegacyCameraFlow` on Android.
Contributor guide
Research direction
Start in android/src/main/java/com/capacitorjs/plugins/camera/LegacyCameraFlow.java, especially openPhotos, registerActivityResultLauncher, and the onDestroy cleanup around lines 301-363. Reproduce with Android's “Don't keep activities” setting and Camera.pickImages or getPhoto, then verify that returning after Activity recreation no longer crashes and selected images are processed when possible.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, java
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100