Skip to content

[camera_android_camerax] Allow high-resolution still captures for max preset#11956

Open
kalyujniy wants to merge 13 commits into
flutter:mainfrom
brickit-app:fix-camerax-max-high-resolution
Open

[camera_android_camerax] Allow high-resolution still captures for max preset#11956
kalyujniy wants to merge 13 commits into
flutter:mainfrom
brickit-app:fix-camerax-max-high-resolution

Conversation

@kalyujniy

@kalyujniy kalyujniy commented Jun 22, 2026

Copy link
Copy Markdown

Updates camera_android_camerax so ResolutionPreset.max can select Camera2 high-resolution JPEG still capture sizes on Android. For ResolutionPreset.max, ImageCapture now uses ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE; preview and image analysis keep the existing selector without high-resolution mode.

Fixes flutter/flutter#188310

Problem

On some Android devices, Camera2 exposes the largest 4:3 still resolution only through getHighResolutionOutputSizes(ImageFormat.JPEG). The CameraX backend used ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY without ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE, so CameraX could pick the largest regular JPEG size instead.

Observed

  • Camera2 regular JPEG max: 3264x2448
  • Camera2 high-resolution JPEG: 4624x3468
  • Flutter takePicture(): 2448x3264

After fix

  • CameraX ImageCapture: 4624x3468
  • Flutter takePicture(): 3468x4624

Risk / tradeoff

High-resolution still capture can be slower and use more memory. Limited to ResolutionPreset.max and ImageCapture only.

Note

Generated Pigeon files (camerax_library.g.dart, CameraXLibrary.g.kt) include formatting churn from local pigeon regeneration; functional changes are limited to ResolutionSelector allowed resolution mode and max-preset ImageCapture selector behavior.

Pre-Review Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools.
  • I read the [Tree Hygiene] page, which explains my responsibilities.
  • I read and followed the [relevant style guides] and ran [the auto-formatter].
  • I signed the [CLA].
  • The title of the PR starts with the name of the package surrounded by square brackets, e.g. [shared_preferences]
  • I [linked to at least one issue that this PR fixes] in the description above.
  • I followed [the version and CHANGELOG instructions], using [semantic versioning] and the [repository CHANGELOG style], or I have commented below to indicate which documented exception this PR falls under[^1].
  • I updated/added any relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or I have commented below to indicate which [test exemption] this PR falls under[^1].
  • All existing and new tests are passing.

Test plan

  • flutter test test/android_camera_camerax_test.dart
  • dart run script/tool/bin/flutter_plugin_tools.dart format --packages camera_android_camerax --no-java --no-kotlin --no-clang-format --no-swift
  • dart run script/tool/bin/flutter_plugin_tools.dart analyze --packages camera_android_camerax
  • dart run script/tool/bin/flutter_plugin_tools.dart dart-test --packages camera_android_camerax
  • Java: ResolutionSelectorTest
  • Android native tests (native-test --android) — not run locally (no JRE); left to CI

@google-cla

google-cla Bot commented Jun 22, 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 updates ResolutionPreset.max to support CameraX high-resolution still capture sizes on Android by introducing an allowedResolutionMode configuration to ResolutionSelector. It adds a separate _imageCaptureResolutionSelector for still image capture, allowing the camera to prefer higher resolution over capture rate. The review feedback suggests passing the preset resolution selector as an argument to _getImageCaptureResolutionSelectorFromPreset to eliminate temporal coupling and preserve existing configuration properties.

Comment thread packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart Outdated
Comment thread packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart Outdated
…lver

Avoid temporal coupling in _getImageCaptureResolutionSelectorFromPreset by
passing the preset resolution selector as an argument and copying its
properties when building the max-preset ImageCapture selector.
@stuartmorgan-g stuartmorgan-g requested a review from camsim99 June 23, 2026 18:52

@camsim99 camsim99 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.

Thank you so much for this change! Without ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE, the plugin was not truly using the highest available resolution.

With that being said, I think this fix should expand beyond ImageCapture. That is, I think all camera use cases should prefer a higher resolution over the capture rate if ResolutionPreset.max is specified. That way, we give developers access to the true highest resolution across the board. Is there some specific reason you think it should only apply to ImageCapture?

///
/// See
/// https://developer.android.com/reference/kotlin/androidx/camera/core/resolutionselector/ResolutionSelector#PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION().
enum ResolutionSelectorAllowedResolutionMode {

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.

I would prefer if these were a class of Ints like Surface to better match the actual Android API for clarity.

/// Allowed resolution mode for [ResolutionSelector].
///
/// See
/// https://developer.android.com/reference/kotlin/androidx/camera/core/resolutionselector/ResolutionSelector#PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION().

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.

Nit: put links in the dart doc for each mode individually

/// For [ResolutionPreset.max], this may differ from [_presetResolutionSelector]
/// to allow CameraX high-resolution still capture sizes without affecting
/// preview or image analysis resolution selection.
ResolutionSelector? _imageCaptureResolutionSelector;

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.

See my overall review comment. If you agree, this can just be _presetResolutionSelector when ResolutionPreset.max is specified.

public ResolutionSelector pigeon_defaultConstructor(
@Nullable ResolutionFilter resolutionFilter,
@Nullable ResolutionStrategy resolutionStrategy,
@Nullable ResolutionSelectorAllowedResolutionMode allowedResolutionMode,

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.

See my comment below about this just be an Integer instead of creating a new class.

Use a shared ResolutionSelector with preferHigherResolutionOverCaptureRate
for all ResolutionSelector-backed use cases when ResolutionPreset.max is
set. Replace the Pigeon enum with nullable int plumbing and Dart int
constants documented with Android API links.
@kalyujniy

Copy link
Copy Markdown
Author

Thank you so much for this change! Without ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE, the plugin was not truly using the highest available resolution.

With that being said, I think this fix should expand beyond ImageCapture. That is, I think all camera use cases should prefer a higher resolution over the capture rate if ResolutionPreset.max is specified. That way, we give developers access to the true highest resolution across the board. Is there some specific reason you think it should only apply to ImageCapture?

Good point. I initially scoped this to ImageCapture because the original issue was specific to still capture, but I agree that ResolutionPreset.max should consistently prefer higher resolution for all ResolutionSelector-backed use cases.

I updated the shared ResolutionPreset.max ResolutionSelector so Preview, ImageCapture, and ImageAnalysis all use PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE. VideoCapture still uses QualitySelector, so this setting does not apply there directly.

I also replaced the Pigeon enum with nullable int plumbing and Dart int constants with per-mode Android API links, matching the Surface-style wrapper.

I manually verified the updated behavior on a real Android device. With ResolutionPreset.max, CameraX now selects JPEG 4624x3468, and takePicture returns 3468x4624; before this change it selected JPEG 3264x2448 and returned 2448x3264.

@kalyujniy kalyujniy requested a review from camsim99 June 29, 2026 21:30

@camsim99 camsim99 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.

This LGTM! Thanks for the explanation and the quick changes :) let's get a second reviewer

@camsim99 camsim99 requested review from a team, mboetger and reidbaker and removed request for a team and mboetger June 30, 2026 22:40
@camsim99 camsim99 added the CICD Run CI/CD label Jun 30, 2026
@flutter-dashboard

Copy link
Copy Markdown

This pull request is not mergeable in its current state, likely because of a merge conflict. Pre-submit CI jobs were not triggered. Pushing a new commit to this branch that resolves the issue will result in pre-submit jobs being scheduled.

@camsim99

Copy link
Copy Markdown
Contributor

@kalyujniy Can you take a look at the merge conflicts?

@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Jul 1, 2026
@camsim99 camsim99 requested review from a team and gmackall and removed request for a team and reidbaker July 1, 2026 15:57
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_android_camerax] ResolutionPreset.max does not select Camera2 high-resolution JPEG still capture sizes

2 participants