Navaron Bracke

make scanner nullable to prevent leak; use early returns in process image callback; format

@@ -20,12 +20,12 @@ import androidx.camera.core.ImageAnalysis @@ -20,12 +20,12 @@ import androidx.camera.core.ImageAnalysis
20 import androidx.camera.core.ImageProxy 20 import androidx.camera.core.ImageProxy
21 import androidx.camera.core.Preview 21 import androidx.camera.core.Preview
22 import androidx.camera.core.TorchState 22 import androidx.camera.core.TorchState
23 -import androidx.camera.core.resolutionselector.AspectRatioStrategy  
24 import androidx.camera.core.resolutionselector.ResolutionSelector 23 import androidx.camera.core.resolutionselector.ResolutionSelector
25 import androidx.camera.core.resolutionselector.ResolutionStrategy 24 import androidx.camera.core.resolutionselector.ResolutionStrategy
26 import androidx.camera.lifecycle.ProcessCameraProvider 25 import androidx.camera.lifecycle.ProcessCameraProvider
27 import androidx.core.content.ContextCompat 26 import androidx.core.content.ContextCompat
28 import androidx.lifecycle.LifecycleOwner 27 import androidx.lifecycle.LifecycleOwner
  28 +import com.google.mlkit.vision.barcode.BarcodeScanner
29 import com.google.mlkit.vision.barcode.BarcodeScannerOptions 29 import com.google.mlkit.vision.barcode.BarcodeScannerOptions
30 import com.google.mlkit.vision.barcode.BarcodeScanning 30 import com.google.mlkit.vision.barcode.BarcodeScanning
31 import com.google.mlkit.vision.barcode.common.Barcode 31 import com.google.mlkit.vision.barcode.common.Barcode
@@ -37,7 +37,6 @@ import io.flutter.view.TextureRegistry @@ -37,7 +37,6 @@ import io.flutter.view.TextureRegistry
37 import java.io.ByteArrayOutputStream 37 import java.io.ByteArrayOutputStream
38 import kotlin.math.roundToInt 38 import kotlin.math.roundToInt
39 39
40 -  
41 class MobileScanner( 40 class MobileScanner(
42 private val activity: Activity, 41 private val activity: Activity,
43 private val textureRegistry: TextureRegistry, 42 private val textureRegistry: TextureRegistry,
@@ -50,7 +49,7 @@ class MobileScanner( @@ -50,7 +49,7 @@ class MobileScanner(
50 private var camera: Camera? = null 49 private var camera: Camera? = null
51 private var preview: Preview? = null 50 private var preview: Preview? = null
52 private var textureEntry: TextureRegistry.SurfaceTextureEntry? = null 51 private var textureEntry: TextureRegistry.SurfaceTextureEntry? = null
53 - private var scanner = BarcodeScanning.getClient() 52 + private var scanner: BarcodeScanner? = null
54 private var lastScanned: List<String?>? = null 53 private var lastScanned: List<String?>? = null
55 private var scannerTimeout = false 54 private var scannerTimeout = false
56 private var displayListener: DisplayManager.DisplayListener? = null 55 private var displayListener: DisplayManager.DisplayListener? = null
@@ -76,38 +75,50 @@ class MobileScanner( @@ -76,38 +75,50 @@ class MobileScanner(
76 scannerTimeout = true 75 scannerTimeout = true
77 } 76 }
78 77
79 - scanner.process(inputImage)  
80 - .addOnSuccessListener { barcodes -> 78 + scanner?.let {
  79 + it.process(inputImage).addOnSuccessListener { barcodes ->
81 if (detectionSpeed == DetectionSpeed.NO_DUPLICATES) { 80 if (detectionSpeed == DetectionSpeed.NO_DUPLICATES) {
82 - val newScannedBarcodes = barcodes.mapNotNull { barcode -> barcode.rawValue }.sorted() 81 + val newScannedBarcodes = barcodes.mapNotNull {
  82 + barcode -> barcode.rawValue
  83 + }.sorted()
  84 +
83 if (newScannedBarcodes == lastScanned) { 85 if (newScannedBarcodes == lastScanned) {
84 // New scanned is duplicate, returning 86 // New scanned is duplicate, returning
85 return@addOnSuccessListener 87 return@addOnSuccessListener
86 } 88 }
87 - if (newScannedBarcodes.isNotEmpty()) lastScanned = newScannedBarcodes 89 + if (newScannedBarcodes.isNotEmpty()) {
  90 + lastScanned = newScannedBarcodes
  91 + }
88 } 92 }
89 93
90 val barcodeMap: MutableList<Map<String, Any?>> = mutableListOf() 94 val barcodeMap: MutableList<Map<String, Any?>> = mutableListOf()
91 95
92 for (barcode in barcodes) { 96 for (barcode in barcodes) {
93 - if (scanWindow != null) {  
94 - val match = isBarcodeInScanWindow(scanWindow!!, barcode, imageProxy)  
95 - if (!match) {  
96 - continue  
97 - } else { 97 + if (scanWindow == null) {
98 barcodeMap.add(barcode.data) 98 barcodeMap.add(barcode.data)
  99 + continue
99 } 100 }
100 - } else { 101 +
  102 + if (isBarcodeInScanWindow(scanWindow!!, barcode, imageProxy)) {
101 barcodeMap.add(barcode.data) 103 barcodeMap.add(barcode.data)
102 } 104 }
103 } 105 }
104 106
  107 + if (barcodeMap.isEmpty()) {
  108 + return@addOnSuccessListener
  109 + }
105 110
106 - if (barcodeMap.isNotEmpty()) {  
107 - if (returnImage) { 111 + if (!returnImage) {
  112 + mobileScannerCallback(
  113 + barcodeMap,
  114 + null,
  115 + null,
  116 + null
  117 + )
  118 + return@addOnSuccessListener
  119 + }
108 120
109 val bitmap = Bitmap.createBitmap(mediaImage.width, mediaImage.height, Bitmap.Config.ARGB_8888) 121 val bitmap = Bitmap.createBitmap(mediaImage.width, mediaImage.height, Bitmap.Config.ARGB_8888)
110 -  
111 val imageFormat = YuvToRgbConverter(activity.applicationContext) 122 val imageFormat = YuvToRgbConverter(activity.applicationContext)
112 123
113 imageFormat.yuvToRgb(mediaImage, bitmap) 124 imageFormat.yuvToRgb(mediaImage, bitmap)
@@ -121,31 +132,18 @@ class MobileScanner( @@ -121,31 +132,18 @@ class MobileScanner(
121 val bmHeight = bmResult.height 132 val bmHeight = bmResult.height
122 bmResult.recycle() 133 bmResult.recycle()
123 134
124 -  
125 mobileScannerCallback( 135 mobileScannerCallback(
126 barcodeMap, 136 barcodeMap,
127 byteArray, 137 byteArray,
128 bmWidth, 138 bmWidth,
129 bmHeight 139 bmHeight
130 ) 140 )
131 -  
132 - } else {  
133 -  
134 - mobileScannerCallback(  
135 - barcodeMap,  
136 - null,  
137 - null,  
138 - null  
139 - )  
140 - }  
141 - }  
142 - }  
143 - .addOnFailureListener { e -> 141 + }.addOnFailureListener { e ->
144 mobileScannerErrorCallback( 142 mobileScannerErrorCallback(
145 e.localizedMessage ?: e.toString() 143 e.localizedMessage ?: e.toString()
146 ) 144 )
  145 + }.addOnCompleteListener { imageProxy.close() }
147 } 146 }
148 - .addOnCompleteListener { imageProxy.close() }  
149 147
150 if (detectionSpeed == DetectionSpeed.NORMAL) { 148 if (detectionSpeed == DetectionSpeed.NORMAL) {
151 // Set timer and continue 149 // Set timer and continue