Julian Steenbakker

feat: add new resolution selector with parameter for android

1 package dev.steenbakker.mobile_scanner 1 package dev.steenbakker.mobile_scanner
2 2
3 import android.app.Activity 3 import android.app.Activity
  4 +import android.content.Context
4 import android.graphics.Bitmap 5 import android.graphics.Bitmap
5 import android.graphics.Matrix 6 import android.graphics.Matrix
6 import android.graphics.Rect 7 import android.graphics.Rect
  8 +import android.hardware.display.DisplayManager
7 import android.net.Uri 9 import android.net.Uri
  10 +import android.os.Build
8 import android.os.Handler 11 import android.os.Handler
9 import android.os.Looper 12 import android.os.Looper
  13 +import android.util.Size
10 import android.view.Surface 14 import android.view.Surface
11 -import androidx.camera.core.* 15 +import android.view.WindowManager
  16 +import androidx.camera.core.Camera
  17 +import androidx.camera.core.CameraSelector
  18 +import androidx.camera.core.ExperimentalGetImage
  19 +import androidx.camera.core.ImageAnalysis
  20 +import androidx.camera.core.ImageProxy
  21 +import androidx.camera.core.Preview
  22 +import androidx.camera.core.resolutionselector.AspectRatioStrategy
  23 +import androidx.camera.core.resolutionselector.ResolutionSelector
  24 +import androidx.camera.core.resolutionselector.ResolutionStrategy
12 import androidx.camera.lifecycle.ProcessCameraProvider 25 import androidx.camera.lifecycle.ProcessCameraProvider
13 import androidx.core.content.ContextCompat 26 import androidx.core.content.ContextCompat
14 import androidx.lifecycle.LifecycleOwner 27 import androidx.lifecycle.LifecycleOwner
@@ -22,11 +35,7 @@ import dev.steenbakker.mobile_scanner.utils.YuvToRgbConverter @@ -22,11 +35,7 @@ import dev.steenbakker.mobile_scanner.utils.YuvToRgbConverter
22 import io.flutter.view.TextureRegistry 35 import io.flutter.view.TextureRegistry
23 import java.io.ByteArrayOutputStream 36 import java.io.ByteArrayOutputStream
24 import kotlin.math.roundToInt 37 import kotlin.math.roundToInt
25 -import android.util.Size  
26 -import android.hardware.display.DisplayManager  
27 -import android.view.WindowManager  
28 -import android.content.Context  
29 -import android.os.Build 38 +
30 39
31 class MobileScanner( 40 class MobileScanner(
32 private val activity: Activity, 41 private val activity: Activity,
@@ -216,7 +225,8 @@ class MobileScanner( @@ -216,7 +225,8 @@ class MobileScanner(
216 mobileScannerStartedCallback: MobileScannerStartedCallback, 225 mobileScannerStartedCallback: MobileScannerStartedCallback,
217 mobileScannerErrorCallback: (exception: Exception) -> Unit, 226 mobileScannerErrorCallback: (exception: Exception) -> Unit,
218 detectionTimeout: Long, 227 detectionTimeout: Long,
219 - cameraResolution: Size? 228 + cameraResolution: Size?,
  229 + newCameraResolutionSelector: Boolean
220 ) { 230 ) {
221 this.detectionSpeed = detectionSpeed 231 this.detectionSpeed = detectionSpeed
222 this.detectionTimeout = detectionTimeout 232 this.detectionTimeout = detectionTimeout
@@ -277,9 +287,19 @@ class MobileScanner( @@ -277,9 +287,19 @@ class MobileScanner(
277 val displayManager = activity.applicationContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager 287 val displayManager = activity.applicationContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
278 288
279 if (cameraResolution != null) { 289 if (cameraResolution != null) {
280 - // TODO: migrate to ResolutionSelector with ResolutionStrategy when upgrading to camera 1.3.0  
281 - // Override initial resolution  
282 - analysisBuilder.setTargetResolution(getResolution(cameraResolution)) 290 + if (newCameraResolutionSelector) {
  291 + val selectorBuilder = ResolutionSelector.Builder()
  292 + selectorBuilder.setResolutionStrategy(
  293 + ResolutionStrategy(
  294 + cameraResolution,
  295 + ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER
  296 + )
  297 + )
  298 + analysisBuilder.setResolutionSelector(selectorBuilder.build()).build()
  299 + } else {
  300 + @Suppress("DEPRECATION")
  301 + analysisBuilder.setTargetResolution(getResolution(cameraResolution))
  302 + }
283 303
284 if (displayListener == null) { 304 if (displayListener == null) {
285 displayListener = object : DisplayManager.DisplayListener { 305 displayListener = object : DisplayManager.DisplayListener {
@@ -288,7 +308,19 @@ class MobileScanner( @@ -288,7 +308,19 @@ class MobileScanner(
288 override fun onDisplayRemoved(displayId: Int) {} 308 override fun onDisplayRemoved(displayId: Int) {}
289 309
290 override fun onDisplayChanged(displayId: Int) { 310 override fun onDisplayChanged(displayId: Int) {
291 - analysisBuilder.setTargetResolution(getResolution(cameraResolution)) 311 + if (newCameraResolutionSelector) {
  312 + val selectorBuilder = ResolutionSelector.Builder()
  313 + selectorBuilder.setResolutionStrategy(
  314 + ResolutionStrategy(
  315 + cameraResolution,
  316 + ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER
  317 + )
  318 + )
  319 + analysisBuilder.setResolutionSelector(selectorBuilder.build()).build()
  320 + } else {
  321 + @Suppress("DEPRECATION")
  322 + analysisBuilder.setTargetResolution(getResolution(cameraResolution))
  323 + }
292 } 324 }
293 } 325 }
294 326
@@ -137,6 +137,7 @@ class MobileScannerHandler( @@ -137,6 +137,7 @@ class MobileScannerHandler(
137 val speed: Int = call.argument<Int>("speed") ?: 1 137 val speed: Int = call.argument<Int>("speed") ?: 1
138 val timeout: Int = call.argument<Int>("timeout") ?: 250 138 val timeout: Int = call.argument<Int>("timeout") ?: 250
139 val cameraResolutionValues: List<Int>? = call.argument<List<Int>>("cameraResolution") 139 val cameraResolutionValues: List<Int>? = call.argument<List<Int>>("cameraResolution")
  140 + val useNewCameraSelector: Boolean = call.argument<Boolean>("useNewCameraSelector") ?: false
140 val cameraResolution: Size? = if (cameraResolutionValues != null) { 141 val cameraResolution: Size? = if (cameraResolutionValues != null) {
141 Size(cameraResolutionValues[0], cameraResolutionValues[1]) 142 Size(cameraResolutionValues[0], cameraResolutionValues[1])
142 } else { 143 } else {
@@ -219,6 +220,7 @@ class MobileScannerHandler( @@ -219,6 +220,7 @@ class MobileScannerHandler(
219 }, 220 },
220 timeout.toLong(), 221 timeout.toLong(),
221 cameraResolution, 222 cameraResolution,
  223 + useNewCameraSelector
222 ) 224 )
223 } 225 }
224 226
@@ -17,7 +17,7 @@ class _BarcodeScannerWithControllerState @@ -17,7 +17,7 @@ class _BarcodeScannerWithControllerState
17 BarcodeCapture? barcode; 17 BarcodeCapture? barcode;
18 18
19 final MobileScannerController controller = MobileScannerController( 19 final MobileScannerController controller = MobileScannerController(
20 - torchEnabled: true, 20 + torchEnabled: true, useNewCameraSelector: true,
21 // formats: [BarcodeFormat.qrCode] 21 // formats: [BarcodeFormat.qrCode]
22 // facing: CameraFacing.front, 22 // facing: CameraFacing.front,
23 // detectionSpeed: DetectionSpeed.normal 23 // detectionSpeed: DetectionSpeed.normal
@@ -16,7 +16,7 @@ class _BarcodeScannerReturningImageState @@ -16,7 +16,7 @@ class _BarcodeScannerReturningImageState
16 extends State<BarcodeScannerReturningImage> 16 extends State<BarcodeScannerReturningImage>
17 with SingleTickerProviderStateMixin { 17 with SingleTickerProviderStateMixin {
18 BarcodeCapture? barcode; 18 BarcodeCapture? barcode;
19 - MobileScannerArguments? arguments; 19 + // MobileScannerArguments? arguments;
20 20
21 final MobileScannerController controller = MobileScannerController( 21 final MobileScannerController controller = MobileScannerController(
22 torchEnabled: true, 22 torchEnabled: true,
@@ -225,12 +225,22 @@ class _MobileScannerState extends State<MobileScanner> @@ -225,12 +225,22 @@ class _MobileScannerState extends State<MobileScanner>
225 return Stack( 225 return Stack(
226 alignment: Alignment.center, 226 alignment: Alignment.center,
227 children: [ 227 children: [
228 - _scanner(value.size, value.webId, value.textureId, value.nrOfCameras), 228 + _scanner(
  229 + value.size,
  230 + value.webId,
  231 + value.textureId,
  232 + value.nrOfCameras,
  233 + ),
229 widget.overlay!, 234 widget.overlay!,
230 ], 235 ],
231 ); 236 );
232 } else { 237 } else {
233 - return _scanner(value.size, value.webId, value.textureId, value.nrOfCameras); 238 + return _scanner(
  239 + value.size,
  240 + value.webId,
  241 + value.textureId,
  242 + value.nrOfCameras,
  243 + );
234 } 244 }
235 }, 245 },
236 ); 246 );
@@ -23,6 +23,7 @@ class MobileScannerController { @@ -23,6 +23,7 @@ class MobileScannerController {
23 this.onPermissionSet, 23 this.onPermissionSet,
24 this.autoStart = true, 24 this.autoStart = true,
25 this.cameraResolution, 25 this.cameraResolution,
  26 + this.useNewCameraSelector = false,
26 }); 27 });
27 28
28 /// Select which camera should be used. 29 /// Select which camera should be used.
@@ -74,6 +75,12 @@ class MobileScannerController { @@ -74,6 +75,12 @@ class MobileScannerController {
74 /// Currently only supported on Android. 75 /// Currently only supported on Android.
75 final Size? cameraResolution; 76 final Size? cameraResolution;
76 77
  78 + /// Use the new resolution selector. Warning: not fully tested, may produce
  79 + /// unwanted/zoomed images.
  80 + ///
  81 + /// Only supported on Android
  82 + final bool useNewCameraSelector;
  83 +
77 /// Sets the barcode stream 84 /// Sets the barcode stream
78 final StreamController<BarcodeCapture> _barcodesController = 85 final StreamController<BarcodeCapture> _barcodesController =
79 StreamController.broadcast(); 86 StreamController.broadcast();
@@ -136,6 +143,7 @@ class MobileScannerController { @@ -136,6 +143,7 @@ class MobileScannerController {
136 arguments['speed'] = detectionSpeed.rawValue; 143 arguments['speed'] = detectionSpeed.rawValue;
137 arguments['timeout'] = detectionTimeoutMs; 144 arguments['timeout'] = detectionTimeoutMs;
138 arguments['returnImage'] = returnImage; 145 arguments['returnImage'] = returnImage;
  146 + arguments['useNewCameraSelector'] = useNewCameraSelector;
139 147
140 /* if (scanWindow != null) { 148 /* if (scanWindow != null) {
141 arguments['scanWindow'] = [ 149 arguments['scanWindow'] = [
@@ -24,6 +24,6 @@ class MobileScannerArguments { @@ -24,6 +24,6 @@ class MobileScannerArguments {
24 required this.hasTorch, 24 required this.hasTorch,
25 this.textureId, 25 this.textureId,
26 this.webId, 26 this.webId,
27 - this.nrOfCameras 27 + this.nrOfCameras,
28 }); 28 });
29 } 29 }