Skip to content

feat: Add ambi be ent-luminance stream for measured-dark torch decisions (fixes #693) - #1774

Open
sawan-scapia wants to merge 4 commits into
juliansteenbakker:developfrom
sawan-scapia:feat/ambient-luminance-stream
Open

feat: Add ambi be ent-luminance stream for measured-dark torch decisions (fixes #693)#1774
sawan-scapia wants to merge 4 commits into
juliansteenbakker:developfrom
sawan-scapia:feat/ambient-luminance-stream

Conversation

@sawan-scapia

Copy link
Copy Markdown

Fixes #693.

Problem

analyzeImage/returnImage only surface a frame once a barcode actually decodes. That means a scene too dark to decode at all — the exact scene where an app would want to prompt "turn on the torch?" — never produces a frame the app can inspect. #693 has been open since 2023 asking for auto-torch-in-the-dark, with no signal available that works for a fully dark scene (the ambient-light-sensor route mentioned in the issue isn't available on iOS either).

What this adds

An opt-in ambient-luminance signal that's independent of barcode decoding:

  • Android: samples the ImageAnalysis Y-plane (Y is luminance) on a 16x16 grid, indexed via rowStride/pixelStride so per-row buffer padding is never averaged in (a naive linear scan over the raw buffer would bias the sample darker than the real scene).
  • iOS/macOS: samples the pixel buffer on a 16x16 grid in captureOutput, handling both planar (YUV) and packed BGRA formats, since videoSettings can negotiate either.
  • Both platforms throttle to ~500ms and run off the main thread (Android: the existing analyzer executor; iOS/macOS: the existing sample-buffer queue). Both are gated by luminanceEnabled/setLuminanceEnabled, off by default, so an app that never calls it pays zero extra sampling cost — no behavior change for existing users.
  • Dart: MobileScannerController.luminanceStream (Stream<double>, 0–255) and setLuminanceEnabled({required bool enabled}). Web has no raw-frame access via the BarcodeDetector API it's built on, so it exposes a stream that never emits and a no-op setter (rather than throwing), so cross-platform app code doesn't need a web-specific branch.

This PR only answers "how dark is it right now." Deciding what to do with that reading — auto-enable once per session, debounce, pick a threshold — is left to the app, since that policy varies (e.g. you generally don't want to keep re-toggling the torch once it's already on and lighting the scene).

Non-goals / left to a maintainer

  • CHANGELOG.md / pubspec.yaml version are untouched — I noticed recent feature PRs (e.g. the close-range-lens work) get merged into develop without a version bump, and CHANGELOG entries get written/bumped separately at release time, sometimes in the project's own house style (e.g. "docs: rewrite 7.4.0 changelog entry in house style"). Happy to add an entry in whatever format you prefer if that's not the right read.

Testing

  • 4 new tests in test/method_channel/luminance_test.dart: method-channel dispatch + argument passthrough for setLuminanceEnabled, event filtering/parsing for luminanceStream, and the malformed-sample fallback to 255.0 (bright) — a parse failure should never be mistaken for a dark reading.
  • Full existing suite (389 tests) passes; flutter analyze is clean across lib/ and test/.
  • This is a generalized, from-scratch reimplementation for this PR — not copy-pasted from anywhere — but the overall approach (native per-frame luminance + Dart-side torch policy) has been running in a downstream fork in production for several weeks, which is where the rowStride/pixelStride indexing fix came from (an earlier linear-scan version of the Android sampling read slightly dark on devices with row padding).

Example usage

await controller.setLuminanceEnabled(enabled: true);

final subscription = controller.luminanceStream.listen((luminance) {
  if (luminance < 50 && controller.value.torchState == TorchState.off) {
    controller.toggleTorch();
  }
});

// ...later, e.g. when the scanner is paused or disposed:
await subscription.cancel();
await controller.setLuminanceEnabled(enabled: false);

README updated with this section and a Features Supported table row.

…eenbakker#693)

analyzeImage/returnImage only surface a frame once a barcode decodes, so
they can't measure a scene that's too dark to decode at all — exactly the
case an app needs to detect to offer "turn on the torch?" (juliansteenbakker#693, open
since 2023, several users asking if this ever landed).

Adds an opt-in ambient-luminance signal instead:

- Android: samples the ImageAnalysis Y-plane (Y *is* luminance) on a 16x16
  grid, indexed via rowStride/pixelStride so per-row padding is never
  averaged in (a naive linear scan would bias the sample darker).
- iOS/macOS: samples the pixel buffer on a 16x16 grid via
  captureOutput, handling both planar (YUV) and packed BGRA formats.
- Both platforms throttle to ~500ms and run off the main thread; both are
  gated by `luminanceEnabled`/`setLuminanceEnabled`, off by default, so an
  app that never calls it pays zero sampling cost.
- Dart: `MobileScannerController.luminanceStream` (Stream<double>, 0-255)
  and `setLuminanceEnabled({required bool enabled})`. Web has no raw-frame
  access via the BarcodeDetector API, so it exposes a stream that never
  emits and a no-op setter, rather than throwing, so cross-platform code
  needs no web-specific branch.

This only answers "how dark is it right now" — deciding what to do with
that (auto-enable once, debounce, threshold) is left to the app, since
that policy varies. README documents the new stream with a torch-auto-
enable example; CHANGELOG/version are left for a maintainer release commit,
matching this repo's existing convention.

Tests: 4 new (method-channel dispatch + argument passthrough, event
parsing/filtering, malformed-sample fallback to 255.0/bright). Full suite
(389 tests) and `flutter analyze` across lib/ and test/ are green.
@sawan-scapia sawan-scapia changed the title Add ambient-luminance stream for measured-dark torch decisions (fixes #693) feat:Add ambi be ent-luminance stream for measured-dark torch decisions (fixes #693) Aug 17, 2026
@sawan-scapia sawan-scapia changed the title feat:Add ambi be ent-luminance stream for measured-dark torch decisions (fixes #693) feat: Add ambi be ent-luminance stream for measured-dark torch decisions (fixes #693) Aug 17, 2026
sawan-scapia and others added 2 commits September 1, 2026 18:05
…ance-stream

# Conflicts:
#	android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScanner.kt
#	darwin/mobile_scanner/Sources/mobile_scanner/MobileScannerPlugin.swift
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 42.85714% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.95%. Comparing base (5349d04) to head (9d2f334).

Files with missing lines Patch % Lines
lib/src/mobile_scanner_controller.dart 0.00% 4 Missing ⚠️
lib/src/mobile_scanner_platform_interface.dart 0.00% 4 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1774      +/-   ##
===========================================
+ Coverage    62.85%   62.95%   +0.09%     
===========================================
  Files           48       48              
  Lines         1233     1247      +14     
===========================================
+ Hits           775      785      +10     
- Misses         458      462       +4     
Flag Coverage Δ
unittests 62.95% <42.85%> (+0.09%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

Auto Torch open in both ios and android in dark

3 participants