diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerDialog.kt index 88a373990e..0b133935ef 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerDialog.kt @@ -106,11 +106,9 @@ import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.quartz.utils.Log import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import java.util.concurrent.Executors import java.util.concurrent.TimeUnit -import kotlin.math.min /** What the caller did with a decoded payload — and therefore what the scanner does next. */ enum class ScanOutcome { @@ -350,8 +348,6 @@ private fun QrCameraScanner( snapshotFlow { state.zoomRatio }.collect { runCatching { control.setZoomRatio(it) } } } - AutoZoomSweep(state = state, enabled = camera != null) - LaunchedEffect(state.notice) { if (state.notice != null) { delay(NOTICE_DURATION_MS) @@ -394,7 +390,6 @@ private fun QrCameraScanner( .pointerInput(Unit) { detectTransformGestures { _, _, zoom, _ -> if (zoom != 1f) { - state.onPinch() state.zoomRatio = (state.zoomRatio * zoom).coerceIn(1f, maxOf(1f, state.maxZoomRatio)) } } @@ -503,44 +498,6 @@ private fun ScanOverlayCanvas( } } -/** - * Sweeps the zoom while nothing is decoding. - * - * A code too small in frame to resolve is the single most common reason a scan fails, and no - * amount of decoder tuning fixes it — there are not enough pixels per module to read. Rather than - * leave the user to work that out and walk closer, the camera pushes in and back out on its own. - * It stops for good once the user pinches: they have taken over. - */ -@Composable -private fun AutoZoomSweep( - state: QrScannerState, - enabled: Boolean, -) { - LaunchedEffect(enabled, state.autoZoomEnabled) { - if (!enabled || !state.autoZoomEnabled) return@LaunchedEffect - - var sweep = 0f - while (isActive) { - delay(AUTO_ZOOM_TICK_MS) - - if (state.msSinceLastDetection < QrScannerState.AUTO_ZOOM_AFTER_MS) { - if (sweep != 0f) { - sweep = 0f - state.zoomRatio = 1f - } - continue - } - - val ceiling = min(state.maxZoomRatio, QrScannerState.AUTO_ZOOM_MAX) - if (ceiling <= 1.01f) continue - - sweep = (sweep + AUTO_ZOOM_TICK_MS.toFloat() / AUTO_ZOOM_PERIOD_MS) % 1f - val triangle = if (sweep < 0.5f) sweep * 2f else (1f - sweep) * 2f - state.zoomRatio = 1f + triangle * (ceiling - 1f) - } - } -} - /** The visible code nearest the tap, or null when the tap was not on one. */ private fun pickCandidateAt( tap: Offset, @@ -620,8 +577,6 @@ private val ANALYSIS_RESOLUTION = ResolutionStrategy(Size(1280, 720), ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER), ).build() -private const val AUTO_ZOOM_TICK_MS = 100L -private const val AUTO_ZOOM_PERIOD_MS = 3_000f private const val NOTICE_DURATION_MS = 3_000L private const val FOCUS_RING_DURATION_MS = 800L private const val FOCUS_AUTO_CANCEL_SECONDS = 4L diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerOverlay.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerOverlay.kt index d420d78867..33a127f1df 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerOverlay.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerOverlay.kt @@ -187,7 +187,7 @@ fun QrScannerControls( ) } - if (!state.autoZoomEnabled && state.zoomRatio > 1.05f) { + if (state.zoomRatio > 1.05f) { ZoomChip( zoomRatio = state.zoomRatio, onReset = { state.resetZoom() }, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerState.kt index 10c56702cd..1767e27e01 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/scanner/QrScannerState.kt @@ -23,7 +23,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.scanner import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -63,13 +62,6 @@ class QrScannerState { var zoomRatio by mutableFloatStateOf(1f) var maxZoomRatio by mutableFloatStateOf(1f) - /** - * Auto-zoom stops for good the first time the user pinches. Someone who has framed the shot - * themselves does not want the camera arguing about it. - */ - var autoZoomEnabled by mutableStateOf(true) - private set - /** A payload we decoded but the caller could not use; drives the explain-what-happened sheet. */ var rejected by mutableStateOf(null) @@ -89,16 +81,6 @@ class QrScannerState { private var lastSubmittedAt = 0L private var darkSinceMs = 0L - /** Milliseconds since anything at all was decoded — what auto-zoom watches. */ - var msSinceLastDetection by mutableLongStateOf(0L) - private set - - private var lastDetectionMs = 0L - - fun onPinch() { - autoZoomEnabled = false - } - fun resetZoom() { zoomRatio = 1f } @@ -121,11 +103,6 @@ class QrScannerState { // whether or not the camera is busy reading something else. if (sequence.dropIfStale(nowMs)) sequenceProgress = null - // Seed the clock on the first frame. Left at zero, the very first empty frame would read - // as "nothing decoded since the epoch" and send auto-zoom hunting before the user has had - // a chance to aim. - if (lastDetectionMs == 0L) lastDetectionMs = nowMs - // Nothing is decided while the "we can't open this" sheet is up: the user is reading it, // and the offending code is very probably still sitting in front of the lens. if (rejected != null) { @@ -136,12 +113,9 @@ class QrScannerState { val found = scan.results.distinctBy { it.text } if (found.isEmpty()) { candidates = emptyList() - msSinceLastDetection = nowMs - lastDetectionMs return null } - lastDetectionMs = nowMs - msSinceLastDetection = 0 candidates = found // A multi-part code is never complete on its first part, so it can't be a single answer. @@ -232,11 +206,5 @@ class QrScannerState { /** Don't offer the torch for a thumb over the lens or a moment of shadow. */ const val DARK_DWELL_MS = 1_000L - - /** How long with nothing decoded before auto-zoom starts hunting. */ - const val AUTO_ZOOM_AFTER_MS = 1_200L - - /** Ceiling for the auto-zoom sweep — past this, focus and shake beat the extra reach. */ - const val AUTO_ZOOM_MAX = 2.5f } }