feat: white-balance mode control (fixes iOS yellow/warm tint)#375
feat: white-balance mode control (fixes iOS yellow/warm tint)#375twtpat wants to merge 2 commits into
Conversation
…eMode/setWhiteBalanceMode)
Mirrors the existing exposure-mode controls. Fixes a warm/yellow cast on iOS by
also defaulting the device to .continuousAutoWhiteBalance on start.
- TS: add WhiteBalanceMode ('AUTO' | 'LOCK' | 'CONTINUOUS') + 3 methods (definitions.ts), web stubs (web.ts).
- iOS: AVCaptureDevice.whiteBalanceMode (.locked/.autoWhiteBalance/.continuousAutoWhiteBalance) in CameraController, bridge in Plugin.swift, and continuous-auto-WB default on start.
- Android: Camera2 CONTROL_AWB_MODE / CONTROL_AWB_LOCK in CameraXView, bridge in CameraPreview.java.
- README regenerated via docgen.
Closes Cap-go#374
Beta npm buildThis PR comes from a fork, so beta publish is disabled for security. If you need a beta package, move the branch into this repository first. |
|
Warning Review limit reached
More reviews will be available in 46 minutes and 50 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds white-balance mode control to the camera preview plugin. A ChangesWhite Balance Mode Control
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java`:
- Line 194: The cached white balance state in CameraXView is drifting from the
actual camera after rebinds, so getWhiteBalanceMode() can return CONTINUOUS even
when the newly bound Camera2CameraControl has reset to its default. Update the
binding/rebind flow in bindCameraUseCases() (and any related camera setup paths)
to reapply the last requested white balance mode to the newly created camera
control, and keep currentWhiteBalanceMode in sync only after the camera request
is successfully applied. Make sure setWhiteBalanceMode() stores the desired mode
for later rebinding, and that getWhiteBalanceMode() reflects the active camera
state after camera switches or startup.
- Around line 3316-3356: The white-balance API is using a fixed capability list
and a cached value that can get out of sync after camera rebinds; update
CameraXView so getWhiteBalanceModes() derives supported modes from the active
camera’s actual Camera2 white-balance capabilities, and make
setWhiteBalanceMode() persist/apply the real native state instead of only
updating currentWhiteBalanceMode. Reapply the selected mode when the camera is
rebound or switched, and ensure AUTO and CONTINUOUS are represented by distinct
behavior only if the camera supports them, otherwise expose only the modes the
camera can actually use.
In `@src/definitions.ts`:
- Around line 496-497: The public WhiteBalanceMode contract is missing the
requested CUSTOM option, so update the type in WhiteBalanceMode in
src/definitions.ts to include CUSTOM alongside the existing modes. Make sure any
related white-balance declarations that mirror this contract elsewhere in the
same definitions area are kept in sync so the API surface matches the intended
exposure-mode pattern.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: defe9e83-65b7-4279-aec8-f3e3d44b59fa
📒 Files selected for processing (7)
README.mdandroid/src/main/java/app/capgo/capacitor/camera/preview/CameraPreview.javaandroid/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.javaios/Sources/CapgoCameraPreviewPlugin/CameraController.swiftios/Sources/CapgoCameraPreviewPlugin/Plugin.swiftsrc/definitions.tssrc/web.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
| private Runnable pendingFrameRateBindSuccess; | ||
| private java.util.function.Consumer<String> pendingFrameRateBindError; | ||
| private String currentExposureMode = "CONTINUOUS"; // Default behavior | ||
| private String currentWhiteBalanceMode = "CONTINUOUS"; // Default behavior |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Rebinds can leave getWhiteBalanceMode() lying about the real camera state.
Line 194 introduces a cached default of CONTINUOUS, and getWhiteBalanceMode() always returns that cache. But setWhiteBalanceMode() only pushes the request to the current Camera2CameraControl; later bindCameraUseCases() paths replace camera after unbindAll() without reapplying white balance. After a camera switch/rebind/start, JS can still read the old cached mode while the newly bound camera is back on its default AWB behavior.
Also applies to: 3320-3353
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java` at
line 194, The cached white balance state in CameraXView is drifting from the
actual camera after rebinds, so getWhiteBalanceMode() can return CONTINUOUS even
when the newly bound Camera2CameraControl has reset to its default. Update the
binding/rebind flow in bindCameraUseCases() (and any related camera setup paths)
to reapply the last requested white balance mode to the newly created camera
control, and keep currentWhiteBalanceMode in sync only after the camera request
is successfully applied. Make sure setWhiteBalanceMode() stores the desired mode
for later rebinding, and that getWhiteBalanceMode() reflects the active camera
state after camera switches or startup.
| public java.util.List<String> getWhiteBalanceModes() { | ||
| return Arrays.asList("AUTO", "LOCK", "CONTINUOUS"); | ||
| } | ||
|
|
||
| public String getWhiteBalanceMode() { | ||
| return currentWhiteBalanceMode; | ||
| } | ||
|
|
||
| @OptIn(markerClass = ExperimentalCamera2Interop.class) | ||
| public void setWhiteBalanceMode(String mode) throws Exception { | ||
| if (camera == null) { | ||
| throw new Exception("Camera not initialized"); | ||
| } | ||
| if (mode == null) { | ||
| throw new Exception("mode is required"); | ||
| } | ||
| String normalized = mode.toUpperCase(Locale.US); | ||
|
|
||
| Camera2CameraControl c2 = Camera2CameraControl.from(camera.getCameraControl()); | ||
| switch (normalized) { | ||
| case "LOCK": { | ||
| CaptureRequestOptions opts = new CaptureRequestOptions.Builder() | ||
| .setCaptureRequestOption(CaptureRequest.CONTROL_AWB_LOCK, true) | ||
| .setCaptureRequestOption(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO) | ||
| .build(); | ||
| mainExecutor.execute(() -> c2.setCaptureRequestOptions(opts)); | ||
| currentWhiteBalanceMode = "LOCK"; | ||
| break; | ||
| } | ||
| case "AUTO": | ||
| case "CONTINUOUS": { | ||
| CaptureRequestOptions opts = new CaptureRequestOptions.Builder() | ||
| .setCaptureRequestOption(CaptureRequest.CONTROL_AWB_LOCK, false) | ||
| .setCaptureRequestOption(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO) | ||
| .build(); | ||
| mainExecutor.execute(() -> c2.setCaptureRequestOptions(opts)); | ||
| currentWhiteBalanceMode = normalized; | ||
| break; | ||
| } | ||
| default: | ||
| throw new Exception("Unsupported white balance mode: " + mode); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the relevant Android implementation and nearby capability checks
git ls-files android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java
ast-grep outline android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java --view expanded | sed -n '1,220p'
printf '\n--- search white balance / camera capability usages ---\n'
rg -n "getWhiteBalanceModes|getWhiteBalanceMode|setWhiteBalanceMode|AWB_LOCK|CONTROL_AWB_MODE|Camera2CameraControl|supported.*white|white balance" android/src/main/java/app/capgo/capacitor/camera/preview -S
printf '\n--- nearby lines around implementation ---\n'
sed -n '190,220p;3300,3375p' android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.javaRepository: Cap-go/capacitor-camera-preview
Length of output: 18863
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check whether white balance state is reapplied during camera rebind or startup
rg -n "bindCameraUseCases|unbindAll|setWhiteBalanceMode|currentWhiteBalanceMode|whiteBalance" android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java -S
printf '\n--- bindCameraUseCases area ---\n'
sed -n '1180,1295p' android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java
printf '\n--- related camera control/state methods ---\n'
sed -n '3260,3395p' android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.javaRepository: Cap-go/capacitor-camera-preview
Length of output: 13349
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect iOS/JS contract for white balance to compare behavior
rg -n "white balance|getWhiteBalanceModes|getWhiteBalanceMode|setWhiteBalanceMode|supported mode" -S .
# If there are platform-specific files, show the relevant snippets
fd -a ".*(white|White|balance|Balance).*" .Repository: Cap-go/capacitor-camera-preview
Length of output: 5975
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read the exact implementation and any surrounding camera rebind/state code.
sed -n '180,220p;1180,1295p;3300,3375p' android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java
printf '\n--- occurrences of white balance / capabilities across repo ---\n'
rg -n "currentWhiteBalanceMode|getWhiteBalanceModes|getWhiteBalanceMode|setWhiteBalanceMode|CONTROL_AWB_LOCK|CONTROL_AWB_MODE|CameraCharacteristics|INFO_SUPPORTED_HARDWARE_LEVEL" android/src/main/java -SRepository: Cap-go/capacitor-camera-preview
Length of output: 18167
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the documented contract and iOS behavior for white balance
sed -n '1370,1425p' README.md
printf '\n--- iOS white balance implementation ---\n'
sed -n '2590,2665p' ios/Sources/CapgoCameraPreviewPlugin/CameraController.swiftRepository: Cap-go/capacitor-camera-preview
Length of output: 4082
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the documented contract and iOS behavior for white balance
sed -n '1370,1425p' README.md
printf '\n--- iOS white balance implementation ---\n'
sed -n '2590,2665p' ios/Sources/CapgoCameraPreviewPlugin/CameraController.swiftRepository: Cap-go/capacitor-camera-preview
Length of output: 4082
Derive white-balance support from the active camera and reapply it on rebind.
getWhiteBalanceModes() is hard-coded to AUTO, LOCK, and CONTINUOUS, but the Android path never checks the active camera’s white-balance capabilities. setWhiteBalanceMode() also only updates currentWhiteBalanceMode; after a camera rebind or switch, the cached value survives while the native state resets. AUTO and CONTINUOUS currently map to the same Camera2 request, so the exposed mode can drift from what the camera is actually using.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java`
around lines 3316 - 3356, The white-balance API is using a fixed capability list
and a cached value that can get out of sync after camera rebinds; update
CameraXView so getWhiteBalanceModes() derives supported modes from the active
camera’s actual Camera2 white-balance capabilities, and make
setWhiteBalanceMode() persist/apply the real native state instead of only
updating currentWhiteBalanceMode. Reapply the selected mode when the camera is
rebound or switched, and ensure AUTO and CONTINUOUS are represented by distinct
behavior only if the camera supports them, otherwise expose only the modes the
camera can actually use.
| /** Reusable white-balance mode type for cross-platform support. */ | ||
| export type WhiteBalanceMode = 'AUTO' | 'LOCK' | 'CONTINUOUS'; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Include CUSTOM in the public white-balance contract.
The new WhiteBalanceMode only allows 'AUTO' | 'LOCK' | 'CONTINUOUS', so callers cannot express the linked issue's requested CUSTOM mode and this API no longer fully mirrors the exposure-mode surface it was modeled after.
As per coding guidelines, src/definitions.ts: "TypeScript interfaces and types must be defined in src/definitions.ts as the source of truth for API documentation."
Also applies to: 1102-1121
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/definitions.ts` around lines 496 - 497, The public WhiteBalanceMode
contract is missing the requested CUSTOM option, so update the type in
WhiteBalanceMode in src/definitions.ts to include CUSTOM alongside the existing
modes. Make sure any related white-balance declarations that mirror this
contract elsewhere in the same definitions area are kept in sync so the API
surface matches the intended exposure-mode pattern.
Source: Coding guidelines
Camera2 maps AUTO and CONTINUOUS to the same CONTROL_AWB_MODE_AUTO, so getWhiteBalanceModes() shouldn't list AUTO as separate (matches getExposureModes). Addresses review feedback on PR Cap-go#375.
|
Thanks for the review! Addressed: 1. Android WB mode list (CONTROL_AWB): Fixed in 28a7b07 — 2. 3. Android cache drift on rebind: Acknowledged — For our use case the key fix is the continuous-auto-WB default on |
Adds white-balance mode control, mirroring the existing exposure-mode controls. Resolves #374.
Why
On iOS, captured photos can have a warm/yellow cast and there's no way to influence white balance (the plugin exposes flash + exposure only). This adds a WB API and, more importantly, defaults the device to
.continuousAutoWhiteBalanceon start, which fixes the cast for the common case.API (mirrors exposure)
Changes
src/definitions.ts):WhiteBalanceModetype + 3 methods. Web (src/web.ts): unsupported-platform stubs.CameraController.swiftget/get/set viaAVCaptureDevice.whiteBalanceMode(.locked/.autoWhiteBalance/.continuousAutoWhiteBalance, guarded byisWhiteBalanceModeSupported);Plugin.swiftregistrations + bridge; continuous-auto-WB default on camera start (next to the existing continuous-exposure default).CameraXView.javaget/get/set via Camera2CONTROL_AWB_MODE/CONTROL_AWB_LOCK(Camera2CameraControl);CameraPreview.java@PluginMethodbridge.docgen.Validation
tsccompiles clean;docgenregenerated the README;npm run check:wiringpasses (methods consistent across TS/iOS/Android).verify:ios/verify:androidin my environment — would appreciate a maintainer build/on-device check, and happy to adjust per review.Thanks for maintaining this plugin!
Summary by CodeRabbit
New Features
Bug Fixes
Documentation