From f601652f94209b728755a2740d35f2ec00378e64 Mon Sep 17 00:00:00 2001 From: Adam Velebil Date: Wed, 2 Nov 2022 14:12:09 +0100 Subject: [PATCH 1/3] add verbose logs --- .../qrscanner_zxing/android/build.gradle | 2 +- .../qrscanner_zxing/QRScannerView.kt | 61 +++++++++++++++++-- .../example/android/app/build.gradle | 4 +- .../android/app/src/main/AndroidManifest.xml | 2 +- .../qrscanner_zxing_example/App.kt | 28 +++++++++ .../example/android/build.gradle | 4 +- 6 files changed, 89 insertions(+), 12 deletions(-) create mode 100644 android/flutter_plugins/qrscanner_zxing/example/android/app/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing_example/App.kt diff --git a/android/flutter_plugins/qrscanner_zxing/android/build.gradle b/android/flutter_plugins/qrscanner_zxing/android/build.gradle index 7f3bd6d3..4d8a14a5 100644 --- a/android/flutter_plugins/qrscanner_zxing/android/build.gradle +++ b/android/flutter_plugins/qrscanner_zxing/android/build.gradle @@ -2,7 +2,7 @@ group 'com.yubico.authenticator.flutter_plugins.qrscanner_zxing' version '1.0' buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.7.20' repositories { google() mavenCentral() diff --git a/android/flutter_plugins/qrscanner_zxing/android/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing/QRScannerView.kt b/android/flutter_plugins/qrscanner_zxing/android/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing/QRScannerView.kt index 857a1731..444beb60 100644 --- a/android/flutter_plugins/qrscanner_zxing/android/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing/QRScannerView.kt +++ b/android/flutter_plugins/qrscanner_zxing/android/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing/QRScannerView.kt @@ -137,6 +137,7 @@ internal class QRScannerView( } override fun getView(): View { + Log.v(TAG, "getView()") barcodeAnalyzer.analysisPaused = false return qrScannerView } @@ -149,7 +150,7 @@ internal class QRScannerView( imageAnalysis = null cameraExecutor.shutdown() methodChannel.setMethodCallHandler(null) - Log.d(TAG, "View disposed") + Log.v(TAG, "dispose()") } private val methodChannel: MethodChannel = MethodChannel(binaryMessenger, CHANNEL_NAME) @@ -166,12 +167,16 @@ internal class QRScannerView( } } + Log.v(TAG, "marginPct: $marginPct") + if (context is Activity) { permissionsGranted = allPermissionsGranted(context) if (!permissionsGranted) { + Log.v(TAG, "permissionsGranted = false -> requesting permission") requestPermissionsFromUser(context) } else { + Log.v(TAG, "permissionsGranted = true -> binding use cases") bindUseCases(context) } @@ -293,6 +298,8 @@ internal class QRScannerView( it.setHints(mapOf(DecodeHintType.POSSIBLE_FORMATS to listOf(BarcodeFormat.QR_CODE))) } + var analyzedImagesCount = 0 + private fun ByteBuffer.toByteArray(): ByteArray { rewind() val data = ByteArray(remaining()) @@ -307,7 +314,33 @@ internal class QRScannerView( return } - val buffer = imageProxy.planes[0].buffer + val plane0 = imageProxy.planes[0] + + if (analyzedImagesCount == 0) { + Log.v(TAG, "First image received for analysis:") + Log.v(TAG, " Image format: ${imageProxy.format}") + Log.v(TAG, " WxH: ${imageProxy.width}x${imageProxy.height}") + + for (indexedPlane in imageProxy.planes.withIndex()) { + val index = indexedPlane.index + val plane = indexedPlane.value + + try { + Log.v(TAG, " plane[$index].rawStride: ${plane.rowStride} ") + } catch (_: UnsupportedOperationException) { + Log.v(TAG, " plane[$index].rawStride: Unsupported Operation") + } + try { + Log.v(TAG, " plane[$index].pixelStride: ${plane.pixelStride}") + } catch (_: UnsupportedOperationException) { + Log.v(TAG, " plane[$index].pixelStride: Unsupported Operation") + } + + Log.v(TAG, " plane[$index].buffer.size: ${plane.buffer.toByteArray().size}") + } + } + + val buffer = plane0.buffer val intArray = buffer.toByteArray().map { it.toInt() }.toIntArray() val source: LuminanceSource = @@ -321,6 +354,9 @@ internal class QRScannerView( val cropWH = shorterDim - 2.0 * cropMargin val cropT = (imageProxy.height - cropWH) / 2.0 val cropL = (imageProxy.width - cropWH) / 2.0 + if(analyzedImagesCount == 0) { + Log.v(TAG, " bitmap l:t:w:h $cropL:$cropT:$cropWH:$cropWH") + } fullSize.crop( cropL.toInt(), cropT.toInt(), @@ -328,18 +364,31 @@ internal class QRScannerView( cropWH.toInt() ) } else { + if(analyzedImagesCount == 0) { + Log.v( + TAG, + " bitmap l:t:w:h 0:0:${imageProxy.width}:${imageProxy.height} (full size)" + ) + } fullSize } val result: com.google.zxing.Result = multiFormatReader.decode(bitmapToProcess) analysisPaused = true // pause - Log.d(TAG, "Analysis result: ${result.text}") + Log.v(TAG, "Analysis result: ${result.text}") listener.invoke(Result.success(result.text)) } catch (_: NotFoundException) { - // ignored: no code was found + if (analyzedImagesCount == 0) { + Log.v(TAG, " No QR code found (NotFoundException)") + } } finally { // important call imageProxy.close() + analyzedImagesCount++ + + if (analyzedImagesCount % 50 == 0) { + Log.v(TAG, "Count of analyzed images so far: $analyzedImagesCount") + } } } } @@ -348,14 +397,14 @@ internal class QRScannerView( private var cameraOpened: Boolean = false override fun onChanged(t: CameraState) { - Log.d(TAG, "Camera state changed to ${t.type}") + Log.v(TAG, "Camera state changed to ${t.type}") if (t.type == CameraState.Type.OPEN) { cameraOpened = true } if (cameraOpened && t.type == CameraState.Type.CLOSED) { - Log.d(TAG, "Camera closed") + Log.v(TAG, "Camera closed") val stateChangedIntent = Intent("com.yubico.authenticator.QRScannerView.CameraClosed") context.sendBroadcast(stateChangedIntent) diff --git a/android/flutter_plugins/qrscanner_zxing/example/android/app/build.gradle b/android/flutter_plugins/qrscanner_zxing/example/android/app/build.gradle index 1b7aa68d..69894779 100644 --- a/android/flutter_plugins/qrscanner_zxing/example/android/app/build.gradle +++ b/android/flutter_plugins/qrscanner_zxing/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion 33 ndkVersion flutter.ndkVersion compileOptions { @@ -48,7 +48,7 @@ android { // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. minSdkVersion 21 - targetSdkVersion flutter.targetSdkVersion + targetSdkVersion 33 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/android/flutter_plugins/qrscanner_zxing/example/android/app/src/main/AndroidManifest.xml b/android/flutter_plugins/qrscanner_zxing/example/android/app/src/main/AndroidManifest.xml index ce164f62..a00432d7 100644 --- a/android/flutter_plugins/qrscanner_zxing/example/android/app/src/main/AndroidManifest.xml +++ b/android/flutter_plugins/qrscanner_zxing/example/android/app/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ package="com.yubico.authenticator.flutter_plugins.qrscanner_zxing_example"> Date: Thu, 3 Nov 2022 08:58:21 +0100 Subject: [PATCH 2/3] consider image plane's rowStride for analysis --- .../qrscanner_zxing/QRScannerView.kt | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/android/flutter_plugins/qrscanner_zxing/android/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing/QRScannerView.kt b/android/flutter_plugins/qrscanner_zxing/android/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing/QRScannerView.kt index 444beb60..9c6b7f17 100644 --- a/android/flutter_plugins/qrscanner_zxing/android/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing/QRScannerView.kt +++ b/android/flutter_plugins/qrscanner_zxing/android/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing/QRScannerView.kt @@ -137,7 +137,6 @@ internal class QRScannerView( } override fun getView(): View { - Log.v(TAG, "getView()") barcodeAnalyzer.analysisPaused = false return qrScannerView } @@ -343,10 +342,25 @@ internal class QRScannerView( val buffer = plane0.buffer val intArray = buffer.toByteArray().map { it.toInt() }.toIntArray() - val source: LuminanceSource = - RGBLuminanceSource(imageProxy.width, imageProxy.height, intArray) + val planeLuminanceSource = + RGBLuminanceSource(plane0.rowStride, imageProxy.height, intArray) - val fullSize = BinaryBitmap(HybridBinarizer(source)) + val luminanceSource = + if (plane0.rowStride > imageProxy.width && planeLuminanceSource.isCropSupported) { + if (analyzedImagesCount == 0) { + Log.v( + TAG, " row stride greater than image -> "+ + "cropping luminance source of size " + + "${plane0.rowStride}x${imageProxy.height} to " + + "${imageProxy.width}x${imageProxy.height}" + ) + } + planeLuminanceSource.crop(0, 0, imageProxy.width, imageProxy.height) + } else { + planeLuminanceSource + } + + val fullSize = BinaryBitmap(HybridBinarizer(luminanceSource)) val bitmapToProcess = if (marginPct != null) { val shorterDim = min(imageProxy.width, imageProxy.height) From 10b5d1552c413ba59eb5802edff3b14712bd7118 Mon Sep 17 00:00:00 2001 From: Adam Velebil Date: Thu, 3 Nov 2022 12:25:25 +0100 Subject: [PATCH 3/3] fix ArrayIndexOutOfBoundsException --- .../qrscanner_zxing/QRScannerView.kt | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/android/flutter_plugins/qrscanner_zxing/android/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing/QRScannerView.kt b/android/flutter_plugins/qrscanner_zxing/android/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing/QRScannerView.kt index 9c6b7f17..5da59aa2 100644 --- a/android/flutter_plugins/qrscanner_zxing/android/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing/QRScannerView.kt +++ b/android/flutter_plugins/qrscanner_zxing/android/src/main/kotlin/com/yubico/authenticator/flutter_plugins/qrscanner_zxing/QRScannerView.kt @@ -299,10 +299,13 @@ internal class QRScannerView( var analyzedImagesCount = 0 - private fun ByteBuffer.toByteArray(): ByteArray { + private fun ByteBuffer.toByteArray(lastRowPadding: Int): ByteArray { rewind() - val data = ByteArray(remaining()) - get(data) + val size = remaining() + val paddedSize = size + lastRowPadding + val data = ByteArray(paddedSize) + get(data, 0, size) + data.fill(0, size, paddedSize) return data } @@ -325,9 +328,9 @@ internal class QRScannerView( val plane = indexedPlane.value try { - Log.v(TAG, " plane[$index].rawStride: ${plane.rowStride} ") + Log.v(TAG, " plane[$index].rowStride: ${plane.rowStride} ") } catch (_: UnsupportedOperationException) { - Log.v(TAG, " plane[$index].rawStride: Unsupported Operation") + Log.v(TAG, " plane[$index].rowStride: Unsupported Operation") } try { Log.v(TAG, " plane[$index].pixelStride: ${plane.pixelStride}") @@ -335,18 +338,22 @@ internal class QRScannerView( Log.v(TAG, " plane[$index].pixelStride: Unsupported Operation") } - Log.v(TAG, " plane[$index].buffer.size: ${plane.buffer.toByteArray().size}") + Log.v(TAG, " plane[$index].buffer.size: ${plane.buffer.toByteArray(0).size}") } } val buffer = plane0.buffer - val intArray = buffer.toByteArray().map { it.toInt() }.toIntArray() + val rowStride = plane0.rowStride + + // the new array has to pad extra size for situation when rowStride > image width + val intArray = + buffer.toByteArray(rowStride - imageProxy.width).map { it.toInt() }.toIntArray() val planeLuminanceSource = - RGBLuminanceSource(plane0.rowStride, imageProxy.height, intArray) + RGBLuminanceSource(rowStride, imageProxy.height, intArray) val luminanceSource = - if (plane0.rowStride > imageProxy.width && planeLuminanceSource.isCropSupported) { + if (rowStride > imageProxy.width && planeLuminanceSource.isCropSupported) { if (analyzedImagesCount == 0) { Log.v( TAG, " row stride greater than image -> "+