diff --git a/quickie/src/main/kotlin/io/github/g00fy2/quickie/QRScannerActivity.kt b/quickie/src/main/kotlin/io/github/g00fy2/quickie/QRScannerActivity.kt index 2312e3a5..9b5194d6 100644 --- a/quickie/src/main/kotlin/io/github/g00fy2/quickie/QRScannerActivity.kt +++ b/quickie/src/main/kotlin/io/github/g00fy2/quickie/QRScannerActivity.kt @@ -6,7 +6,6 @@ import android.app.Dialog import android.content.Intent import android.content.pm.PackageManager import android.os.Bundle -import android.util.Size import android.view.HapticFeedbackConstants import android.view.KeyEvent import android.view.View @@ -15,12 +14,10 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ContextThemeWrapper import androidx.camera.core.CameraSelector -import androidx.camera.core.ImageAnalysis -import androidx.camera.core.Preview import androidx.camera.core.TorchState -import androidx.camera.core.resolutionselector.ResolutionSelector -import androidx.camera.core.resolutionselector.ResolutionStrategy import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.camera.view.LifecycleCameraController +import androidx.camera.view.PreviewView import androidx.core.content.ContextCompat import androidx.core.content.IntentCompat import androidx.core.view.ViewCompat @@ -43,6 +40,7 @@ internal class QRScannerActivity : AppCompatActivity() { private var showTorchToggle = false private var showCloseButton = false private var useFrontCamera = false + private var usePinchToZoom = false internal var errorDialog: Dialog? = null set(value) { field = value @@ -102,47 +100,45 @@ internal class QRScannerActivity : AppCompatActivity() { return@addListener } - val preview = Preview.Builder().build().also { it.surfaceProvider = binding.previewView.surfaceProvider } - val imageAnalysis = ImageAnalysis.Builder() - .setResolutionSelector( - ResolutionSelector.Builder().setResolutionStrategy( - ResolutionStrategy( - Size(1280, 720), - ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER - ) - ).build() - ) - .build() - .also { - it.setAnalyzer( + cameraProvider.unbindAll() + + try { + val controller = LifecycleCameraController(this).apply { + cameraSelector = + if (useFrontCamera) CameraSelector.DEFAULT_FRONT_CAMERA else CameraSelector.DEFAULT_BACK_CAMERA + setEnabledUseCases(LifecycleCameraController.IMAGE_ANALYSIS) + setImageAnalysisAnalyzer( analysisExecutor, QRCodeAnalyzer( barcodeFormats = barcodeFormats, onSuccess = { barcode -> - it.clearAnalyzer() + clearImageAnalysisAnalyzer() onSuccess(barcode) }, onFailure = { exception -> onFailure(exception) }, onPassCompleted = { failureOccurred -> onPassCompleted(failureOccurred) } ) ) + isPinchToZoomEnabled = usePinchToZoom + isTapToFocusEnabled = true } - cameraProvider.unbindAll() - - val cameraSelector = - if (useFrontCamera) CameraSelector.DEFAULT_FRONT_CAMERA else CameraSelector.DEFAULT_BACK_CAMERA + binding.previewView.scaleType = PreviewView.ScaleType.FILL_CENTER + binding.previewView.controller = controller + controller.bindToLifecycle(this) - try { - val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis) binding.overlayView.visibility = View.VISIBLE binding.overlayView.setCloseVisibilityAndOnClick(showCloseButton) { finish() } - if (showTorchToggle && camera.cameraInfo.hasFlashUnit()) { - binding.overlayView.setTorchVisibilityAndOnClick(true) { camera.cameraControl.enableTorch(it) } - camera.cameraInfo.torchState.observe(this) { binding.overlayView.setTorchState(it == TorchState.ON) } - } else { - binding.overlayView.setTorchVisibilityAndOnClick(false) - } + + controller.initializationFuture.addListener({ + val info = controller.cameraInfo ?: return@addListener + if (showTorchToggle && info.hasFlashUnit()) { + binding.overlayView.setTorchVisibilityAndOnClick(true) { controller.enableTorch(it) } + info.torchState.observe(this) { binding.overlayView.setTorchState(it == TorchState.ON) } + } else { + binding.overlayView.setTorchVisibilityAndOnClick(false) + } + }, ContextCompat.getMainExecutor(this)) } catch (e: Exception) { binding.overlayView.visibility = View.INVISIBLE onFailure(e) @@ -196,6 +192,7 @@ internal class QRScannerActivity : AppCompatActivity() { showTorchToggle = it.showTorchToggle useFrontCamera = it.useFrontCamera showCloseButton = it.showCloseButton + usePinchToZoom = it.usePinchToZoom if (it.keepScreenOn) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) } diff --git a/quickie/src/main/kotlin/io/github/g00fy2/quickie/config/ParcelableScannerConfig.kt b/quickie/src/main/kotlin/io/github/g00fy2/quickie/config/ParcelableScannerConfig.kt index 3f9d34b0..05019c54 100644 --- a/quickie/src/main/kotlin/io/github/g00fy2/quickie/config/ParcelableScannerConfig.kt +++ b/quickie/src/main/kotlin/io/github/g00fy2/quickie/config/ParcelableScannerConfig.kt @@ -14,4 +14,5 @@ internal class ParcelableScannerConfig( val useFrontCamera: Boolean, val showCloseButton: Boolean, val keepScreenOn: Boolean, + val usePinchToZoom: Boolean ) : Parcelable \ No newline at end of file diff --git a/quickie/src/main/kotlin/io/github/g00fy2/quickie/config/ScannerConfig.kt b/quickie/src/main/kotlin/io/github/g00fy2/quickie/config/ScannerConfig.kt index 9352d480..828b8abe 100644 --- a/quickie/src/main/kotlin/io/github/g00fy2/quickie/config/ScannerConfig.kt +++ b/quickie/src/main/kotlin/io/github/g00fy2/quickie/config/ScannerConfig.kt @@ -17,6 +17,7 @@ public class ScannerConfig( internal val useFrontCamera: Boolean, internal val showCloseButton: Boolean, internal val keepScreenOn: Boolean, + internal val usePinchToZoom: Boolean ) { public class Builder { @@ -29,6 +30,7 @@ public class ScannerConfig( private var useFrontCamera: Boolean = false private var showCloseButton: Boolean = false private var keepScreenOn: Boolean = false + private var usePinchToZoom: Boolean = false /** * Set a list of interested barcode formats. List must not be empty. @@ -63,6 +65,11 @@ public class ScannerConfig( */ public fun setShowTorchToggle(enable: Boolean): Builder = apply { showTorchToggle = enable } + /** + * Use pinch to zoom gesture to control the camera zoom level. + */ + public fun setUsePinchToZoom(enable: Boolean): Builder = apply { usePinchToZoom = enable } + /** * Use the front camera. */ @@ -92,6 +99,7 @@ public class ScannerConfig( useFrontCamera = useFrontCamera, showCloseButton = showCloseButton, keepScreenOn = keepScreenOn, + usePinchToZoom = usePinchToZoom, ) } diff --git a/quickie/src/main/kotlin/io/github/g00fy2/quickie/extensions/ScannerConfigExtensions.kt b/quickie/src/main/kotlin/io/github/g00fy2/quickie/extensions/ScannerConfigExtensions.kt index b2cfc837..76b440a1 100644 --- a/quickie/src/main/kotlin/io/github/g00fy2/quickie/extensions/ScannerConfigExtensions.kt +++ b/quickie/src/main/kotlin/io/github/g00fy2/quickie/extensions/ScannerConfigExtensions.kt @@ -14,4 +14,5 @@ internal fun ScannerConfig.toParcelableConfig() = useFrontCamera = useFrontCamera, showCloseButton = showCloseButton, keepScreenOn = keepScreenOn, + usePinchToZoom = usePinchToZoom ) \ No newline at end of file diff --git a/sample/src/main/kotlin/io/github/g00fy2/quickiesample/MainActivity.kt b/sample/src/main/kotlin/io/github/g00fy2/quickiesample/MainActivity.kt index 3414d3ff..7e1f05ed 100644 --- a/sample/src/main/kotlin/io/github/g00fy2/quickiesample/MainActivity.kt +++ b/sample/src/main/kotlin/io/github/g00fy2/quickiesample/MainActivity.kt @@ -50,6 +50,7 @@ class MainActivity : AppCompatActivity() { setHorizontalFrameRatio(2.2f) // set the horizontal overlay ratio (default is 1 / square frame) setUseFrontCamera(false) // use the front camera setKeepScreenOn(true) // keep the device's screen turned on + setUsePinchToZoom(true) } ) }