Merge pull request #232 from herou/zxing_implementation

Implement zxing instead of Google Firebase ML Vision
This commit is contained in:
Kevin Gorham 2021-03-16 14:32:55 -04:00 committed by GitHub
commit bad21eda3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 57 deletions

View File

@ -4,7 +4,6 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.google.gms.google-services'
//apply plugin: 'com.github.ben-manes.versions'
archivesBaseName = 'zcash-android-wallet'
@ -152,7 +151,6 @@ dependencies {
// Google
implementation Deps.Google.GUAVA
implementation Deps.Google.MATERIAL
implementation Deps.Google.ML_VISION // QR Scanner
// Dagger
implementation Deps.Dagger.ANDROID_SUPPORT
@ -173,6 +171,7 @@ dependencies {
// Misc.
implementation Deps.Misc.LOTTIE
implementation Deps.Misc.CHIPS
implementation Deps.Misc.Plugins.QR_SCANNER
// Tests
testImplementation Deps.Test.COROUTINES_TEST
@ -184,6 +183,7 @@ dependencies {
androidTestImplementation Deps.Test.Android.JUNIT
androidTestImplementation Deps.Test.Android.ESPRESSO
}
defaultTasks 'clean', 'assembleZcashmainnetRelease'

View File

@ -2,62 +2,62 @@ package cash.z.ecc.android.ui.scan
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import cash.z.ecc.android.sdk.ext.retrySimple
import cash.z.ecc.android.sdk.ext.retryUpTo
import cash.z.ecc.android.sdk.ext.twig
import com.google.android.gms.tasks.Task
import com.google.firebase.ml.vision.FirebaseVision
import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcode
import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcodeDetector
import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcodeDetectorOptions
import com.google.firebase.ml.vision.common.FirebaseVisionImage
import com.google.firebase.ml.vision.common.FirebaseVisionImageMetadata
import com.google.zxing.*
import com.google.zxing.common.HybridBinarizer
class QrAnalyzer(val scanCallback: (qrContent: String, image: ImageProxy) -> Unit) :
ImageAnalysis.Analyzer {
private val detector: FirebaseVisionBarcodeDetector by lazy {
val options = FirebaseVisionBarcodeDetectorOptions.Builder()
.setBarcodeFormats(FirebaseVisionBarcode.FORMAT_QR_CODE)
.build()
FirebaseVision.getInstance().getVisionBarcodeDetector(options)
}
var pendingTask: Task<out Any>? = null
private val reader = MultiFormatReader()
override fun analyze(image: ImageProxy) {
var rotation = image.imageInfo.rotationDegrees % 360
if (rotation < 0) {
rotation += 360
}
retrySimple {
val mediaImage = FirebaseVisionImage.fromMediaImage(
image.image!!, when (rotation) {
0 -> FirebaseVisionImageMetadata.ROTATION_0
90 -> FirebaseVisionImageMetadata.ROTATION_90
180 -> FirebaseVisionImageMetadata.ROTATION_180
270 -> FirebaseVisionImageMetadata.ROTATION_270
else -> {
FirebaseVisionImageMetadata.ROTATION_0
}
}
)
pendingTask = detector.detectInImage(mediaImage).also {
it.addOnSuccessListener { result ->
onImageScan(result, image)
}
it.addOnFailureListener(::onImageScanFailure)
image.toBinaryBitmap().let { bitmap ->
val qrContent = bitmap.decodeWith(reader) ?: bitmap.flip().decodeWith(reader)
if (qrContent == null) {
image.close()
} else {
onImageScan(qrContent, image)
}
}
}
private fun onImageScan(result: List<FirebaseVisionBarcode>, image: ImageProxy) {
result.firstOrNull()?.rawValue?.let {
scanCallback(it, image)
} ?: runCatching { image.close() }
private fun ImageProxy.toBinaryBitmap(): BinaryBitmap {
return planes[0].buffer.let { buffer ->
ByteArray(buffer.remaining()).also { buffer.get(it) }
}.let { bytes ->
PlanarYUVLuminanceSource(bytes, width, height, 0, 0, width, height, false)
}.let { source ->
BinaryBitmap(HybridBinarizer(source))
}
}
private fun onImageScanFailure(e: Exception) {
twig("Warning: Image scan failed")
private fun BinaryBitmap.decodeWith(reader: Reader): String? {
return try {
reader.decode(this).toString()
} catch (e: NotFoundException) {
// these happen frequently. Whenever no QR code is found in the frame. No need to log.
null
} catch (e: Throwable) {
twig("Error while scanning QR: $e")
null
}
}
private fun BinaryBitmap.flip(): BinaryBitmap {
blackMatrix.apply {
repeat(width) { w ->
repeat(height) { h ->
flip(w, h)
}
}
}
return this
}
private fun onImageScan(result: String, image: ImageProxy) {
scanCallback(result, image)
}
}

View File

@ -96,7 +96,7 @@ class ScanFragment : BaseFragment<FragmentScanBinding>() {
try {
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis)
preview.setSurfaceProvider(binding.preview.createSurfaceProvider())
preview.setSurfaceProvider(binding.preview.surfaceProvider)
} catch (t: Throwable) {
// TODO: consider bubbling this up to the user
mainActivity?.feedback?.report(t)

View File

@ -10,7 +10,6 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'com.google.gms:google-services:4.3.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${Deps.kotlinVersion}"
classpath 'io.fabric.tools:gradle:1.31.2'
classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.7.5'

View File

@ -25,13 +25,13 @@ object Deps {
const val MULTIDEX = "androidx.multidex:multidex:2.0.1"
const val PAGING = "androidx.paging:paging-runtime-ktx:2.1.2"
const val RECYCLER = "androidx.recyclerview:recyclerview:1.2.0-alpha05"
object CameraX : Version("1.0.0-beta04") {
val CAMERA2 = "androidx.camera:camera-camera2:1.0.0-beta04"
val CORE = "androidx.camera:camera-core:1.0.0-beta04"
val LIFECYCLE = "androidx.camera:camera-lifecycle:1.0.0-beta04"
object CameraX : Version("1.0.0-rc02") {
val CAMERA2 = "androidx.camera:camera-camera2:1.0.0-rc02"
val CORE = "androidx.camera:camera-core:1.0.0-rc02"
val LIFECYCLE = "androidx.camera:camera-lifecycle:1.0.0-rc02"
object View : Version("1.0.0-alpha11") {
val EXT = "androidx.camera:camera-extensions:1.0.0-alpha11"
val VIEW = "androidx.camera:camera-view:1.0.0-alpha11"
val EXT = "androidx.camera:camera-extensions:1.0.0-alpha21"
val VIEW = "androidx.camera:camera-view:1.0.0-alpha21"
}
}
object Lifecycle : Version("2.2.0") {
@ -57,8 +57,6 @@ object Deps {
// per this recommendation from Chris Povirk, given guava's decision to split ListenableFuture away from Guava: https://groups.google.com/d/msg/guava-discuss/GghaKwusjcY/bCIAKfzOEwAJ
const val GUAVA = "com.google.guava:guava:27.0.1-android"
const val MATERIAL = "com.google.android.material:material:1.1.0-beta01"
// QR Scanner
const val ML_VISION = "com.google.firebase:firebase-ml-vision:24.0.3"
}
object Grpc : Version("1.25.0") {
val ANDROID = "io.grpc:grpc-android:$version"
@ -95,7 +93,7 @@ object Deps {
const val CHIPS = "com.github.gmale:chips-input-layout:2.3.1"
object Plugins {
const val SECURE_STORAGE = "com.github.gmale:secure-storage-android:0.0.3"//"de.adorsys.android:securestoragelibrary:1.2.2"
const val QR_SCANNER = "com.google.zxing:core:3.2.1"
const val QR_SCANNER = "com.google.zxing:core:3.4.1"
}
}