Skip to content

Decouple HDR quick setting controls from capture modes#528

Draft
Kimblebee wants to merge 4 commits into
mainfrom
kim/hdr/decouple-capture-modes
Draft

Decouple HDR quick setting controls from capture modes#528
Kimblebee wants to merge 4 commits into
mainfrom
kim/hdr/decouple-capture-modes

Conversation

@Kimblebee
Copy link
Copy Markdown
Collaborator

This PR decouples HDR quick settings (HDR Video / HLG10 and HDR Image / Ultra HDR) from automatic capture mode transitions. Instead of forcing capture mode changes when toggling HDR, HDR availability is now constrained by the active capture mode:

  • HDR Video (HLG10) is restricted to VIDEO_ONLY mode.
  • HDR Image (Ultra HDR) is restricted to IMAGE_ONLY mode.
  • STANDARD(hybrid) Mode (both image and video bound) is restricted to SDR/JPEG

This PR does not modify the dedicated settings screen

Benign Behavioral Changes

  • No HDR in STANDARD Mode: HDR is now completely disabled in STANDARD mode (forces SDR/JPEG)
    binding and improve stability.
  • No Auto-Mode Switches: Toggling HDR in IMAGE_ONLY or VIDEO_ONLY no longer forces automatic capture mode changes.

⚠️ Identified Concerns (Future Work)

  • HDR Reset in STANDARD Mode / Unsupported Lens: Switching to STANDARD capture mode or a lens without HDR for the current capture mode permanently overwrites the respective active HDR setting to SDR/JPEG.
    • Future Fix: Separate "User Preferences" from "Active Constraints" to preserve user intent across temporary constraint violations.

Kimblebee added 3 commits June 1, 2026 18:22
    - Decoupled dynamic range (video HDR) from image format (image HDR) settings across UI, controller, and CameraX configuration layers.
    - Removed dynamic range constraints from the createImageUseCase configuration in CameraSession.kt, enabling independent Ultra HDR
  image capture.
    - Updated QuickSettings bottom sheet click handlers to mutate only the HDR setting relevant to the active capture mode.
    - Enforced specialized Low Light Boost vs Ultra HDR conflicts in CameraXCameraSystem.kt, prioritizing Low Light Boost.
    - Created HdrUiStateAdapterTest.kt covering all HDR availability states and flash conflicts.
    - Refactored CameraXCameraSystemTest.kt to run parameterized HDR decoupling tests on both front and rear lenses.
Copy link
Copy Markdown
Contributor

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

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 refactors HDR and image format constraint handling when switching capture modes, ensuring HDR settings are preserved or disabled appropriately. It also adds comprehensive instrumented and unit tests to verify these behaviors. The review feedback suggests simplifying a when block in QuickSettingsScreen.kt, enforcing concurrent camera mode constraints when changing capture modes in CameraXCameraSystem.kt, and restricting the visibility of the new test class HdrUiStateAdapterTest to internal.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +168 to +183
when (captureMode) {
CaptureMode.IMAGE_ONLY -> {
quickSettingsController.setImageFormat(i)
}
CaptureMode.VIDEO_ONLY -> {
quickSettingsController.setDynamicRange(d)
}
CaptureMode.STANDARD -> {
quickSettingsController.setDynamicRange(d)
quickSettingsController.setImageFormat(i)
}
null -> {
quickSettingsController.setDynamicRange(d)
quickSettingsController.setImageFormat(i)
}
}
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

The when block can be simplified by combining the redundant branches for CaptureMode.STANDARD and null into an else branch, and using single-line expressions for the other branches. This improves readability and aligns with the repository style guide's directive to simplify complex logic.

                                    when (captureMode) {
                                        CaptureMode.IMAGE_ONLY -> quickSettingsController.setImageFormat(i)
                                        CaptureMode.VIDEO_ONLY -> quickSettingsController.setDynamicRange(d)
                                        else -> {
                                            quickSettingsController.setDynamicRange(d)
                                            quickSettingsController.setImageFormat(i)
                                        }
                                    }
References
  1. Simplify Complex Logic: Look for needlessly complex code. If a multi-line block of logic can be condensed into a more concise and readable idiomatic expression (e.g., using Kotlin standard library functions), suggest the simplification. (link)

Comment on lines 1015 to 1021
override suspend fun setCaptureMode(captureMode: CaptureMode) {
currentSettings.update { old ->
old?.copy(captureMode = captureMode)
?.tryApplyDynamicRangeConstraints()
?.tryApplyImageFormatConstraints()
}
}
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

When switching the capture mode to STANDARD or IMAGE_ONLY, if concurrentCameraMode is currently DUAL, it should be reset to OFF because concurrent camera mode is only supported in VIDEO_ONLY mode. Calling tryApplyConcurrentCameraModeConstraints() here ensures that this constraint is properly enforced when the capture mode changes.

Note: You should also update tryApplyConcurrentCameraModeConstraints() to check captureMode == CaptureMode.VIDEO_ONLY to fully resolve this constraint.

Suggested change
override suspend fun setCaptureMode(captureMode: CaptureMode) {
currentSettings.update { old ->
old?.copy(captureMode = captureMode)
?.tryApplyDynamicRangeConstraints()
?.tryApplyImageFormatConstraints()
}
}
override suspend fun setCaptureMode(captureMode: CaptureMode) {
currentSettings.update { old ->
old?.copy(captureMode = captureMode)
?.tryApplyDynamicRangeConstraints()
?.tryApplyImageFormatConstraints()
?.tryApplyConcurrentCameraModeConstraints()
}
}
References
  1. Architectural Consistency: Does the new code align with the existing MVVM architecture? Are ViewModels, Repositories, and UI components used correctly? Does it introduce any anti-patterns or deviate from established conventions in the codebase? (link)

import org.junit.runners.JUnit4

@RunWith(JUnit4::class)
class HdrUiStateAdapterTest {
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

According to the repository style guide, all new classes should use the most restrictive visibility modifier possible. Since HdrUiStateAdapterTest is a test class and does not need to be accessed from outside its module, it should be declared as internal.

Suggested change
class HdrUiStateAdapterTest {
internal class HdrUiStateAdapterTest {
References
  1. Proper Visibility Modifiers: Ensure all new functions, properties, and classes use the most restrictive visibility modifier possible (e.g., private, internal) while still allowing necessary access. Avoid public visibility unless explicitly required for external API exposure. (link)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant