mirror of
https://github.com/bitwarden/android.git
synced 2025-12-10 08:35:05 -06:00
PM-26594: Move the QrCodeAnalyzer to the UI module (#5980)
This commit is contained in:
parent
cd9c7f98e7
commit
7849bbbb0a
@ -271,7 +271,6 @@ dependencies {
|
||||
implementation(platform(libs.square.retrofit.bom))
|
||||
implementation(libs.square.retrofit)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.zxing.zxing.core)
|
||||
|
||||
// For now we are restricted to running Compose tests for debug builds only
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
|
||||
@ -55,6 +55,8 @@ import com.bitwarden.ui.platform.base.util.annotatedStringResource
|
||||
import com.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.bitwarden.ui.platform.feature.qrcodescan.util.QrCodeAnalyzer
|
||||
import com.bitwarden.ui.platform.feature.qrcodescan.util.QrCodeAnalyzerImpl
|
||||
import com.bitwarden.ui.platform.model.WindowSize
|
||||
import com.bitwarden.ui.platform.resource.BitwardenDrawable
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
@ -62,8 +64,6 @@ import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.bitwarden.ui.platform.theme.LocalBitwardenColorScheme
|
||||
import com.bitwarden.ui.platform.theme.color.darkBitwardenColorScheme
|
||||
import com.bitwarden.ui.platform.util.rememberWindowSize
|
||||
import com.x8bit.bitwarden.ui.vault.feature.qrcodescan.util.QrCodeAnalyzer
|
||||
import com.x8bit.bitwarden.ui.vault.feature.qrcodescan.util.QrCodeAnalyzerImpl
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@ -1,73 +0,0 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.qrcodescan.util
|
||||
|
||||
import androidx.camera.core.ImageProxy
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.BinaryBitmap
|
||||
import com.google.zxing.DecodeHintType
|
||||
import com.google.zxing.MultiFormatReader
|
||||
import com.google.zxing.NotFoundException
|
||||
import com.google.zxing.PlanarYUVLuminanceSource
|
||||
import com.google.zxing.common.HybridBinarizer
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
/**
|
||||
* A class setup to handle image analysis so that we can use the Zxing library
|
||||
* to scan QR codes and convert them to a string.
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
class QrCodeAnalyzerImpl : QrCodeAnalyzer {
|
||||
|
||||
/**
|
||||
* This will ensure the result is only sent once as multiple images with a valid
|
||||
* QR code can be sent for analysis.
|
||||
*/
|
||||
private var qrCodeRead = false
|
||||
|
||||
override lateinit var onQrCodeScanned: (String) -> Unit
|
||||
|
||||
override fun analyze(image: ImageProxy) {
|
||||
if (qrCodeRead) {
|
||||
return
|
||||
}
|
||||
|
||||
val source = PlanarYUVLuminanceSource(
|
||||
image.planes[0].buffer.toByteArray(),
|
||||
image.width,
|
||||
image.height,
|
||||
0,
|
||||
0,
|
||||
image.width,
|
||||
image.height,
|
||||
false,
|
||||
)
|
||||
val binaryBitmap = BinaryBitmap(HybridBinarizer(source))
|
||||
try {
|
||||
val result = MultiFormatReader()
|
||||
.apply {
|
||||
setHints(
|
||||
mapOf(
|
||||
DecodeHintType.POSSIBLE_FORMATS to arrayListOf(
|
||||
BarcodeFormat.QR_CODE,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
.decode(binaryBitmap)
|
||||
|
||||
qrCodeRead = true
|
||||
onQrCodeScanned(result.text)
|
||||
} catch (e: NotFoundException) {
|
||||
return
|
||||
} finally {
|
||||
image.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function helps us prepare the byte buffer to be read.
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
private fun ByteBuffer.toByteArray(): ByteArray =
|
||||
ByteArray(rewind().remaining()).also { get(it) }
|
||||
@ -3,9 +3,9 @@ package com.x8bit.bitwarden.ui.vault.feature.qrcodescan
|
||||
import androidx.camera.core.ImageProxy
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.ui.platform.feature.qrcodescan.util.FakeQrCodeAnalyzer
|
||||
import com.bitwarden.ui.util.performCustomAccessibilityAction
|
||||
import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest
|
||||
import com.x8bit.bitwarden.ui.vault.feature.qrcodescan.util.FakeQrCodeAnalyzer
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
|
||||
@ -224,7 +224,6 @@ dependencies {
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
implementation(libs.kotlinx.coroutines.android)
|
||||
implementation(libs.kotlinx.serialization)
|
||||
implementation(libs.zxing.zxing.core)
|
||||
|
||||
// For now we are restricted to running Compose tests for debug builds only
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
|
||||
@ -50,8 +50,6 @@ import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bitwarden.authenticator.ui.authenticator.feature.qrcodescan.util.QrCodeAnalyzer
|
||||
import com.bitwarden.authenticator.ui.authenticator.feature.qrcodescan.util.QrCodeAnalyzerImpl
|
||||
import com.bitwarden.authenticator.ui.platform.util.isPortrait
|
||||
import com.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.bitwarden.ui.platform.base.util.annotatedStringResource
|
||||
@ -60,6 +58,8 @@ import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.bitwarden.ui.platform.feature.qrcodescan.util.QrCodeAnalyzer
|
||||
import com.bitwarden.ui.platform.feature.qrcodescan.util.QrCodeAnalyzerImpl
|
||||
import com.bitwarden.ui.platform.resource.BitwardenDrawable
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
package com.bitwarden.authenticator.ui.authenticator.feature.qrcodescan.util
|
||||
|
||||
import androidx.camera.core.ImageAnalysis
|
||||
import androidx.compose.runtime.Stable
|
||||
|
||||
/**
|
||||
* An interface that is used to help scan QR codes.
|
||||
*/
|
||||
@Stable
|
||||
interface QrCodeAnalyzer : ImageAnalysis.Analyzer {
|
||||
|
||||
/**
|
||||
* The method that is called once the code is scanned.
|
||||
*/
|
||||
var onQrCodeScanned: (String) -> Unit
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
package com.bitwarden.authenticator.ui.authenticator.feature.qrcodescan
|
||||
|
||||
import androidx.camera.core.ImageProxy
|
||||
import com.bitwarden.authenticator.ui.authenticator.feature.qrcodescan.util.QrCodeAnalyzer
|
||||
|
||||
/**
|
||||
* A helper class that helps test scan outcomes.
|
||||
*/
|
||||
class FakeQrCodeAnalyzer : QrCodeAnalyzer {
|
||||
|
||||
override lateinit var onQrCodeScanned: (String) -> Unit
|
||||
|
||||
/**
|
||||
* The result of the scan that will be sent to the ViewModel (or `null` to indicate a
|
||||
* scanning error.
|
||||
*/
|
||||
var scanResult: String? = null
|
||||
|
||||
override fun analyze(image: ImageProxy) {
|
||||
scanResult?.let { onQrCodeScanned.invoke(it) }
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import com.bitwarden.authenticator.ui.platform.base.AuthenticatorComposeTest
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.ui.platform.feature.qrcodescan.util.FakeQrCodeAnalyzer
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
|
||||
@ -57,6 +57,7 @@ dependencies {
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.browser)
|
||||
implementation(libs.androidx.camera.camera2)
|
||||
implementation(libs.androidx.compose.animation)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.compose.material3.adaptive)
|
||||
@ -72,6 +73,7 @@ dependencies {
|
||||
implementation(libs.kotlinx.serialization)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
implementation(libs.zxing.zxing.core)
|
||||
|
||||
// For now we are restricted to running Compose tests for debug builds only
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
@ -86,6 +88,7 @@ dependencies {
|
||||
testImplementation(libs.androidx.compose.ui.test)
|
||||
|
||||
testFixturesImplementation(libs.androidx.activity.compose)
|
||||
testFixturesImplementation(libs.androidx.camera.camera2)
|
||||
testFixturesImplementation(libs.androidx.compose.ui.test)
|
||||
testFixturesImplementation(libs.androidx.navigation.compose)
|
||||
testFixturesImplementation(libs.google.hilt.android.testing)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.qrcodescan.util
|
||||
package com.bitwarden.ui.platform.feature.qrcodescan.util
|
||||
|
||||
import androidx.camera.core.ImageAnalysis
|
||||
import androidx.compose.runtime.Stable
|
||||
@ -1,6 +1,7 @@
|
||||
package com.bitwarden.authenticator.ui.authenticator.feature.qrcodescan.util
|
||||
package com.bitwarden.ui.platform.feature.qrcodescan.util
|
||||
|
||||
import androidx.camera.core.ImageProxy
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.BinaryBitmap
|
||||
import com.google.zxing.DecodeHintType
|
||||
@ -14,6 +15,7 @@ import java.nio.ByteBuffer
|
||||
* A class setup to handle image analysis so that we can use the Zxing library
|
||||
* to scan QR codes and convert them to a string.
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
class QrCodeAnalyzerImpl : QrCodeAnalyzer {
|
||||
|
||||
/**
|
||||
@ -31,7 +33,7 @@ class QrCodeAnalyzerImpl : QrCodeAnalyzer {
|
||||
|
||||
val source = PlanarYUVLuminanceSource(
|
||||
image.planes[0].buffer.toByteArray(),
|
||||
image.planes[0].rowStride,
|
||||
image.width,
|
||||
image.height,
|
||||
0,
|
||||
0,
|
||||
@ -39,12 +41,10 @@ class QrCodeAnalyzerImpl : QrCodeAnalyzer {
|
||||
image.height,
|
||||
false,
|
||||
)
|
||||
|
||||
val binaryBitmap = BinaryBitmap(HybridBinarizer(source))
|
||||
try {
|
||||
val result = MultiFormatReader().decode(
|
||||
/* image = */ binaryBitmap,
|
||||
/* hints = */
|
||||
binaryBitmap,
|
||||
mapOf(
|
||||
DecodeHintType.POSSIBLE_FORMATS to arrayListOf(BarcodeFormat.QR_CODE),
|
||||
DecodeHintType.ALSO_INVERTED to true,
|
||||
@ -53,7 +53,8 @@ class QrCodeAnalyzerImpl : QrCodeAnalyzer {
|
||||
|
||||
qrCodeRead = true
|
||||
onQrCodeScanned(result.text)
|
||||
} catch (ignored: NotFoundException) {
|
||||
} catch (_: NotFoundException) {
|
||||
return
|
||||
} finally {
|
||||
image.close()
|
||||
}
|
||||
@ -63,5 +64,6 @@ class QrCodeAnalyzerImpl : QrCodeAnalyzer {
|
||||
/**
|
||||
* This function helps us prepare the byte buffer to be read.
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
private fun ByteBuffer.toByteArray(): ByteArray =
|
||||
ByteArray(rewind().remaining()).also { get(it) }
|
||||
@ -1,4 +1,4 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.qrcodescan.util
|
||||
package com.bitwarden.ui.platform.feature.qrcodescan.util
|
||||
|
||||
import androidx.camera.core.ImageProxy
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user