Skip to content

[camera_android_camerax] Add lens type information - #12564

Draft
johnsondigitalsystems wants to merge 1 commit into
flutter:mainfrom
johnsondigitalsystems:camerax-lens-type
Draft

johnsondigitalsystems wants to merge 1 commit into
flutter:mainfrom
johnsondigitalsystems:camerax-lens-type

Conversation

@johnsondigitalsystems

Copy link
Copy Markdown

Populates CameraDescription.lensType on Android, so that availableCameras()
reports whether each camera is an ultra wide or a telephoto lens instead of
always reporting CameraLensType.unknown.

This is the Android counterpart of the iOS implementation added in
#7653, and it consumes the CameraLensType enum and the
CameraDescription.lensType field that #8723 added to
camera_platform_interface (2.10.0). No platform interface change is needed —
this PR only fills in a field that already exists.

Implementation

CameraX exposes CameraInfo.getIntrinsicZoomRatio(), which is the
ratio of a camera's field of view to that of the default camera with the same
lens facing direction. That maps onto CameraLensType directly:

Intrinsic zoom ratio Reported lens type
< 1.0 (wider field of view than the default camera) CameraLensType.ultraWide
> 1.0 (narrower field of view than the default camera) CameraLensType.telephoto
== 1.0 CameraLensType.unknown

getIntrinsicZoomRatio() is stable public API — it carries no experimental
marker in the cameraxVersion this plugin pins (1.6.1) — and is implemented by
the camera2 backend's CameraInfoAdapter.

The change is:

  • pigeons/camerax_library.dart: adds double getIntrinsicZoomRatio() to the
    CameraInfo ProxyApi, plus the regenerated camerax_library.g.dart and
    CameraXLibrary.g.kt.
  • CameraInfoProxyApi.java: delegates straight to
    CameraInfo.getIntrinsicZoomRatio(), with no added logic, per the plugin's
    CONTRIBUTING guidance for host API implementations.
  • android_camera_camerax.dart: maps the ratio to a CameraLensType in
    availableCameras().

Why 1.0 is reported as unknown rather than wide

This is the one judgement call in the change, so it is worth spelling out.

CameraX's javadoc guarantees that the default camera of a lens facing
direction reports an intrinsic zoom ratio of 1.0, which would make 1.0 look
like a reliable signal for CameraLensType.wide. It is not, because 1.0 is
overloaded three ways:

  1. the default camera genuinely reports 1.0;
  2. INTRINSIC_ZOOM_RATIO_UNKNOWN is defined as 1.0f, and is what is
    returned when the ratio cannot be computed; and
  3. the default implementation of CameraInfo.getIntrinsicZoomRatio() also
    returns it.

Nothing in the returned value distinguishes those cases. Reporting wide would
therefore assert a specific lens role on the strength of a value that may simply
mean "I don't know", which is worse than reporting unknown — a caller can
handle unknown by falling back, but has no way to detect a wrong wide. So
1.0 maps to unknown, and the rationale is recorded in a comment at the
mapping site so it is not "simplified" away later.

Failure handling

Reporting a lens type is best effort. getIntrinsicZoomRatio() is called per
camera inside the availableCameras() loop, and a failure is caught and
reported as CameraLensType.unknown so that one camera refusing the query
cannot fail enumeration of the rest. Pigeon wraps every host-side Throwable,
as well as channel and codec failures, into a PlatformException, so catching
that one type covers every failure mode of the call.

The pigeon member is a method rather than an attached field for this reason:
an attached field is read eagerly when the CameraInfo instance is created, so
a throwing getter would break CameraInfo construction entirely rather than
degrading one field, and it would also change the generated
CameraInfo.pigeon_detached constructor signature.

Tests

  • Four new Dart tests in android_camera_camerax_test.dart covering the three
    ratio mappings and the error fallback.
  • A new CameraInfoTest.getIntrinsicZoomRatio_retrievesExpectedIntrinsicZoomRatio
    native unit test covering the host API delegation.
  • Two existing availableCameras tests gained a getIntrinsicZoomRatio stub.
    MockCameraInfo is a nice mock, so an unstubbed Future<double> returns
    0.0, which would have been reported as ultraWide. The stub supplies 1.0
    — the realistic value for a default camera — which preserves each test's
    existing expectation unchanged. No assertion was modified.

A note on flutter/flutter#178038

flutter/flutter#178038 ("CameraLensType.unknown returned on Android") was closed
as not planned on the basis that CameraX does not expose lens type data, which
made CameraLensType.unknown the expected value on Android rather than a bug.
CameraInfo.getIntrinsicZoomRatio() does expose enough to distinguish ultra wide
and telephoto lenses, so that premise no longer holds for those two cases —
though it remains true that CameraX offers no way to positively identify a wide
lens, which is the other half of why 1.0 is reported as unknown above.

Fixes flutter/flutter#91247

Pre-Review Checklist

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2

Populates CameraDescription.lensType on Android using CameraX's
CameraInfo.getIntrinsicZoomRatio(), which reports the ratio of a camera's
field of view to that of the default camera with the same lens facing
direction:

  ratio < 1.0  -> CameraLensType.ultraWide
  ratio > 1.0  -> CameraLensType.telephoto
  ratio == 1.0 -> CameraLensType.unknown

1.0 is deliberately not reported as CameraLensType.wide. CameraX documents
that the default camera reports 1.0, but 1.0 is also the value of
INTRINSIC_ZOOM_RATIO_UNKNOWN, which is returned both when the ratio cannot
be computed and by the default implementation of getIntrinsicZoomRatio().
Those cases are indistinguishable from the returned value, so 1.0 is
reported as unknown rather than asserting a lens role that may be wrong.

Retrieving the ratio is best effort: a failure is reported as unknown so
that one camera refusing the query cannot fail enumeration of the rest.

No camera_platform_interface change is required; CameraLensType and
CameraDescription.lensType already exist there as of 2.10.0.
@google-cla

google-cla Bot commented Aug 23, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds the lensType property to CameraDescriptions returned by availableCameras on Android using CameraX. It retrieves the intrinsic zoom ratio from CameraInfo via Pigeon and maps it to CameraLensType.ultraWide (ratio < 1.0), CameraLensType.telephoto (ratio > 1.0), or CameraLensType.unknown (ratio == 1.0 or on failure). Feedback suggests explicitly handling cases where the intrinsic zoom ratio is 0.0 or less by returning CameraLensType.unknown to prevent uninitialized values or mock defaults from being incorrectly classified as ultra-wide.

Comment on lines +364 to +366
if (intrinsicZoomRatio < 1.0) {
return CameraLensType.ultraWide;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

An intrinsic zoom ratio of 0.0 or less is physically impossible and typically indicates an uninitialized state, an error, or an unstubbed mock in tests (since Mockito's default return value for Future<double> is 0.0).

To prevent these cases from being incorrectly reported as CameraLensType.ultraWide, we should explicitly handle intrinsicZoomRatio <= 0.0 and return CameraLensType.unknown.

Suggested change
if (intrinsicZoomRatio < 1.0) {
return CameraLensType.ultraWide;
}
if (intrinsicZoomRatio <= 0.0) {
return CameraLensType.unknown;
}
if (intrinsicZoomRatio < 1.0) {
return CameraLensType.ultraWide;
}

@Piinks

Piinks commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Thanks for the submission, but we can’t proceed with reviewing this PR until the CLA is signed. Please let us know once you’ve signed the CLA.

@stuartmorgan-g
stuartmorgan-g marked this pull request as draft September 1, 2026 20:31
@stuartmorgan-g

Copy link
Copy Markdown
Contributor

Marking as Draft pending the CLA update, to get this off of our weekly triage until it's ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[camera] Support choosing lenses

3 participants