-
Notifications
You must be signed in to change notification settings - Fork 185
feat: Android Camera v2 API #339
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
… of 2 rather than device pixel ratio. This fixes the offset for devices with pixel ratio of 3 while continuing to work on devices with pixel ratio of 2.
…omLimit startPreview() option
…mit startPreview() option
|
Hey @dpa99c feel free to propose your PR here: Cap-go/capacitor-camera-preview#61 |
|
@dpa99c Is this changes works? Maybe a demo video demonstrating this changes could be good to add in PR description. It could get easy for @pbowyer or @arielhernandezmusa to get quick reviewed. Hope this get merged soon. I am waiting for the Camera2 API to be integrated in the capacitor soon :) |
…s to ensure all forms of exception are caught
Send error and log messages as plugin events back to JS layer
…re no errors due to incorrect escaping
…and do not attempt to restore previous orientation
It seems that I faced the same issue while testing this PR with my existing implementation. Here is my preview UI and here is the photo taken/saved In my implementation I use the below camera preview options (note that x, y, width and height are not provided!) while the test/demo app for this PR uses this https://github.com/dpa99c/capacitor-camera-preview-test/blob/master/src/views/HomePage.vue#L95 camera preview options (note that x, y, width and height are provided!) which makes the preview to be a square with the same width/height. Therefore, I think that, the camera preview works incorrectly when x, y, width and height are not provided. Please note that these are optional (see https://github.com/capacitor-community/camera-preview?tab=readme-ov-file#startoptions) and, I think, that the camera plugin should work as before when these are not provided. |
…sing `storeToFile: true`
|
I've been unable to get this code to work reliably across all a variety of devices / multiple cameras and aspect ratios. It's pretty close for some android tablets that we have especially landscape mode, but in portrait it's ever so slightly out. Then on different devices (still android) with multiple cameras - which is really where this is needed I end up either with weird aspect ratio changes or a picture that is at a significantly different zoom between the preview and the picture. I'm out of ideas |
Can you please list devices and conditions where you see the problem? Thank you |
…an be due to transient error.
…ror in plugin methods
…ent ANR on devices with flaky OEM camera HAL implementations.
…so it can be interrogated in web layer
…devices that don’t fully support the latter
- camera1: introduce constants and replace magic numbers - Add DEFAULT_PICTURE_QUALITY, FRONT_CAMERA_RECOMPRESS_JPEG_QUALITY, ASPECT_TOLERANCE, TAP_AREA_* and area/coeff constants. - Use constants for takePicture quality, aspect tolerance and tap focus/ metering calculations. - Limit picture resolution selection using MAX_PICTURE_PIXELS_BELOW_2MP. - Remove unused import. - camera1-preview: add saved-call lifecycle and timeouts, fix callbacks - Add Handler/Looper-based timeout scheduling and cancellation for saved PluginCalls. - Set calls to keepAlive for capture/snapshot/record/camera start and schedule timeouts. - Add onPictureTaken/onSnapshotTaken and corresponding error handlers to resolve/reject and release saved calls. - Improve record start/stop handling to avoid leaked saved calls and race conditions. - preview: extract ASPECT_TOLERANCE constant - Move local aspect tolerance literal into a shared constant for clarity and reuse. - camera2: add defaults, handler fallbacks and robust retry/error handling - Introduce DEFAULT_PICTURE_QUALITY, ASPECT_TOLERANCE, TAP_AREA_BASE, FOCUS/METERING coeffs and METERING_WEIGHT. - Use a fallback handler (main looper) for image readers when background handler is unavailable. - Ensure background thread is started before opening camera. - Reset configureSessionRetryCount on successful config/capture to avoid stale retry state. - Notify eventListener.onCameraStartedError on camera device errors and on persistent configure failures. - Use constants for picture quality and tap-area logic. - camera2-preview: saved-call lifecycle, timeouts and null-safety - Add timeout scheduling and cancellation for saved calls; cancel on success and reject + release on timeout. - Set keepAlive on saved calls and resolve/reject + release them safely in all on* callbacks. - Guard against null fragment when flipping or starting actions. - Ensure start/stop record and camera start flows resolve/reject the correct saved calls and avoid leaking callback ids.
…n safety to reduce NPEs and improve stability
- Corrected capture-session configuration order to avoid committing without a matching beginConfiguration() and to start the session only after all outputs are added. Also set the photo completion callback before capture to avoid races. - Hardened stop to always clean up preview layers and orientation observers and to treat already-stopped as success (reduces noisy errors). - Fixed setMaxZoomLimit to read the correct parameter (maxZoomLimit).
…ratios and alignments of preview
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Reworks the plugin’s native camera implementation to support Android Camera2 (v2) while also adding cross-platform zoom APIs and exposing Android camera characteristics to enable future multi-camera selection.
Changes:
- Introduces a new Android Camera2-based implementation with Camera1 fallback selection logic.
- Adds zoom APIs (
getZoom,setZoom,getMaxZoom, plus max-zoom limiting) across Android/iOS and exposes AndroidgetCameraCharacteristics(). - Updates TypeScript definitions, web stubs, and README documentation for the new API surface.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 23 comments.
Show a summary per file
| File | Description |
|---|---|
| src/web.ts | Adds web stubs for new zoom + Camera2-related APIs (throws unimplemented). |
| src/definitions.ts | Extends plugin typings/options to include zoom APIs, max zoom limit, camera characteristics, and API selection. |
| ios/Sources/CameraPreviewPlugin/CameraPreviewPlugin.swift | Registers zoom/max-zoom-limit methods and wires zoom limit into start/handler logic. |
| ios/Sources/CameraPreviewPlugin/CameraController.swift | Adds zoom/max-zoom-limit implementation and emits zoom change events. |
| android/src/main/java/com/ahm/capacitor/camera/preview/camera2api/Camera2Preview.java | New Camera2 bridge layer handling plugin calls, timeouts, and camera characteristics. |
| android/src/main/java/com/ahm/capacitor/camera/preview/camera2api/Camera2Activity.java | New Camera2 Fragment implementing preview/capture/record/zoom and orientation handling. |
| android/src/main/java/com/ahm/capacitor/camera/preview/camera1api/Preview.java | Moves Camera1 preview implementation into a dedicated package and refactors constants. |
| android/src/main/java/com/ahm/capacitor/camera/preview/camera1api/CustomTextureView.java | Updates package namespace for Camera1 components. |
| android/src/main/java/com/ahm/capacitor/camera/preview/camera1api/CustomSurfaceView.java | Updates package namespace for Camera1 components. |
| android/src/main/java/com/ahm/capacitor/camera/preview/camera1api/Camera1Preview.java | New Camera1 bridge layer (timeouts/call lifecycle fixes, zoom APIs). |
| android/src/main/java/com/ahm/capacitor/camera/preview/camera1api/Camera1Activity.java | Renames/refactors the old CameraActivity to Camera1Activity and adjusts camera switching/rotation logic. |
| android/src/main/java/com/ahm/capacitor/camera/preview/TapGestureDetector.java | Makes TapGestureDetector public for reuse by Camera2 implementation. |
| android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java | Routes calls to Camera1 vs Camera2 and adds API selection + Camera2 support reporting. |
| README.md | Documents max zoom limit option and new zoom-related methods. |
Comments suppressed due to low confidence (1)
android/src/main/java/com/ahm/capacitor/camera/preview/camera1api/Camera1Activity.java:457
switchCamera()now advancescameraCurrentlyLockedby(cameraCurrentlyLocked + 1) % numberOfCameras, which cycles through all cameras (including multiple rear lenses) rather than toggling front/back. This changes the behavior of the publicflip()API on Camera1 devices and may lead to unexpected lens selection. Ifflip()is intended to mean front↔rear, select the next camera based onCameraInfo.facinginstead of cycling by index.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
android/src/main/java/com/ahm/capacitor/camera/preview/camera2api/Camera2Preview.java
Outdated
Show resolved
Hide resolved
android/src/main/java/com/ahm/capacitor/camera/preview/camera2api/Camera2Activity.java
Outdated
Show resolved
Hide resolved
android/src/main/java/com/ahm/capacitor/camera/preview/camera2api/Camera2Activity.java
Outdated
Show resolved
Hide resolved
android/src/main/java/com/ahm/capacitor/camera/preview/camera2api/Camera2Preview.java
Outdated
Show resolved
Hide resolved
android/src/main/java/com/ahm/capacitor/camera/preview/camera2api/Camera2Preview.java
Outdated
Show resolved
Hide resolved
android/src/main/java/com/ahm/capacitor/camera/preview/camera1api/Camera1Preview.java
Outdated
Show resolved
Hide resolved
android/src/main/java/com/ahm/capacitor/camera/preview/camera2api/Camera2Activity.java
Outdated
Show resolved
Hide resolved
android/src/main/java/com/ahm/capacitor/camera/preview/camera2api/Camera2Preview.java
Show resolved
Hide resolved
|
The preview aspect ratio/offset issues described by @eggbeard should now be addressed, and the latest Note that while it now defaults to Camera2, the implementation performs feature detection and falls back to the Camera(1) API if the Camera2 API is not sufficiently supported on the device. My test project can be used to validate this PR branch implementation: |


Problem
The Android implementation currently relies on the deprecated Camera (v1) API. On newer devices this can cause mismatches between the preview/captured image and the stock camera app, and in some cases v1 is no longer fully supported. This has led to real-world issues like incorrect preview/capture framing (see #275).
At the same time, Camera2 support is inconsistent across the Android ecosystem. Some devices expose full Camera2 capabilities while others (including newer but lower-end models) deliberately hobble Camera2 to legacy/limited modes. We therefore need a Camera2 implementation with feature detection and a robust Camera1 fallback, rather than a hard switch.
In addition, there is no cross‑platform zoom API, and the plugin does not yet expose camera capability discovery needed for future per‑lens selection (see #256).
Analysis
This PR contains a rewrite of the Android implementation to use the Camera v2 API instead of the deprecated Camera API.
The main reason for this change is to fix issues where the preview/captured image from this plugin did not correspond to the preview/captured image from the default Camera app. This addresses #275.
It also opens the way to fetch the device's camera details and select a specific camera/lens based on these (see #256). While the fetch side is implemented by this PR (
getCameraCharacteristics()), the selection of a specific camera/lens is not (as it was not needed for this use case).It also implements
getZoom(),setZoom(), andgetMaxZoom()on both Android and iOS, reworking prior work from #326 on Android to work with the Camera v2 API.Solution
This PR rewrites the Android implementation to use the Camera2 API by default, with runtime detection to fall back to Camera1 when Camera2 is unavailable or inadequate. The Camera2 path aligns preview and capture behavior with the platform camera app, addressing the framing mismatch in #275.
It also introduces a cross‑platform zoom surface (
getZoom,setZoom,getMaxZoom, plusgetMaxZoomLimit/setMaxZoomLimit) and exposes Android camera characteristics viagetCameraCharacteristics()so future lens selection is possible without re‑architecting.Implementation details
Android (Camera2 + fallback)
android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java
setApi.android/src/main/java/com/ahm/capacitor/camera/preview/camera2api/Camera2Activity.java
android/src/main/java/com/ahm/capacitor/camera/preview/camera2api/Camera2Preview.java
android/src/main/java/com/ahm/capacitor/camera/preview/camera1api/Camera1Preview.java
android/src/main/java/com/ahm/capacitor/camera/preview/camera1api/Camera1Activity.java
android/src/main/java/com/ahm/capacitor/camera/preview/TapGestureDetector.java
iOS
ios/Sources/CameraPreviewPlugin/CameraPreviewPlugin.swift
startand replaces hardcoded/2scaling withUIScreen.main.scale.ios/Sources/CameraPreviewPlugin/CameraController.swift
Web + TypeScript
src/definitions.ts
maxZoomLimittyping to match native signatures.src/web.ts
README.md
Review comments
All substantive review comments have been addressed in the updated implementation. Highlights include:
setOpacitynow resolves correctly on Camera2; start/stop record callbacks follow the actual recorder state.updatePreview,getCurrentZoomLevel, and null‑safe handling of camera characteristics and preview sizes.maxZoomLimitparameter naming aligned across native, TS, web, and docs.off/on/auto/red‑eye/torch) to Camera2 AE/FLASH modes, with safe parsing.One minor stylistic suggestion (removing a redundant null check in Camera2 stop cleanup) was left intentionally as harmless defensive code.