Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2513938

Browse files
committed
Add Google ML Kit-based QR code scanner with ZXing fallback
Integrate Google ML Kit's Play Services-backed code scanner for enhanced QR scanning, including support for dense/stylized codes. Update QR importing flow to prioritize the new scanner while retaining ZXing as a fallback. Add dependencies, implement scanner logic in `MainActivity`, and update manifest to pre-download scanner module on installation.
1 parent e36a078 commit 2513938

3 files changed

Lines changed: 54 additions & 6 deletions

File tree

app/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,11 @@ dependencies {
7272
implementation("androidx.appcompat:appcompat:1.7.0")
7373
implementation("com.google.android.material:material:1.12.0")
7474
implementation("androidx.constraintlayout:constraintlayout:2.2.0")
75+
// Google Code Scanner (ML Kit) — primary QR scanner. Works on stylized /
76+
// dense codes that ZXing's CameraX-less BarcodeView misses. No CAMERA
77+
// permission needed — Play Services owns the camera pipeline.
78+
implementation("com.google.android.gms:play-services-code-scanner:16.1.0")
79+
// ZXing kept as fallback: gallery image decoding + offline scanner on
80+
// devices without Play Services.
7581
implementation("com.journeyapps:zxing-android-embedded:4.3.0")
7682
}

app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
android:theme="@style/Theme.AndroidProxyApp"
3030
android:networkSecurityConfig="@xml/network_security_config">
3131

32+
<!-- Hint to Play Store to pre-download the ML Kit code-scanner module
33+
after install, so first scan doesn't have to wait for a download. -->
34+
<meta-data
35+
android:name="com.google.mlkit.vision.DEPENDENCIES"
36+
android:value="barcode_ui" />
37+
3238
<activity
3339
android:name=".MainActivity"
3440
android:exported="true">

app/src/main/java/com/proxyagent/app/MainActivity.kt

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ import androidx.appcompat.app.AppCompatActivity
3636
import androidx.core.app.ActivityCompat
3737
import androidx.core.content.FileProvider
3838
import androidx.core.view.WindowCompat
39+
import com.google.mlkit.vision.barcode.common.Barcode
40+
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
41+
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
3942
import com.google.zxing.BarcodeFormat
4043
import com.google.zxing.BinaryBitmap
4144
import com.google.zxing.DecodeHintType
@@ -530,17 +533,17 @@ class MainActivity : AppCompatActivity() {
530533
.show()
531534
}
532535

533-
// Three independent ways to ingest a QR config: live camera, picking an
534-
// image from gallery (decoded offline), or pasting the plain text payload
535-
// shown next to the QR in the dashboard. Camera fails on dense QRs +
536-
// weak autofocus, so the alternatives matter for real-world use.
536+
// Three independent ways to ingest a QR config: live camera (Google ML
537+
// Kit code scanner — way more robust than ZXing on stylized/dense QRs),
538+
// picking an image from gallery (decoded via ZXing offline), or pasting
539+
// the plain text payload shown next to the QR in the dashboard.
537540
private fun showQrSourceChooser() {
538-
val items = arrayOf("Camera (live scan)", "Pick QR image from gallery", "Paste from clipboard")
541+
val items = arrayOf("Camera (Google scanner)", "Pick QR image from gallery", "Paste from clipboard")
539542
AlertDialog.Builder(this)
540543
.setTitle("Import tunnel QR")
541544
.setItems(items) { _, which ->
542545
when (which) {
543-
0 -> qrLauncher.launch(buildScanOptions())
546+
0 -> launchGoogleCodeScanner()
544547
1 -> qrImageLauncher.launch("image/*")
545548
2 -> applyClipboardQr()
546549
}
@@ -549,6 +552,39 @@ class MainActivity : AppCompatActivity() {
549552
.show()
550553
}
551554

555+
// Google ML Kit code scanner — Play-Services-backed, no CAMERA permission
556+
// needed, handles stylized/dense QRs that ZXing struggles with. Falls back
557+
// to ZXing when Play Services is missing or the scan client errors.
558+
private fun launchGoogleCodeScanner() {
559+
try {
560+
val options = GmsBarcodeScannerOptions.Builder()
561+
.setBarcodeFormats(Barcode.FORMAT_QR_CODE)
562+
.build()
563+
val scanner = GmsBarcodeScanning.getClient(this, options)
564+
scanner.startScan()
565+
.addOnSuccessListener { barcode ->
566+
val text = barcode.rawValue
567+
if (text.isNullOrBlank()) {
568+
Toast.makeText(this, "QR: empty payload", Toast.LENGTH_SHORT).show()
569+
} else {
570+
applyQrPayload(text)
571+
}
572+
}
573+
.addOnCanceledListener {
574+
Toast.makeText(this, "QR scan cancelled", Toast.LENGTH_SHORT).show()
575+
}
576+
.addOnFailureListener { e ->
577+
Toast.makeText(this, "Google scanner failed (${e.message}); falling back to ZXing",
578+
Toast.LENGTH_LONG).show()
579+
qrLauncher.launch(buildScanOptions())
580+
}
581+
} catch (e: Throwable) {
582+
Toast.makeText(this, "No Play Services scanner; falling back to ZXing",
583+
Toast.LENGTH_SHORT).show()
584+
qrLauncher.launch(buildScanOptions())
585+
}
586+
}
587+
552588
// setOrientationLocked(true) + custom portrait CaptureActivity prevents the
553589
// library's default sensorLandscape activity from showing the "rotate the
554590
// phone" overlay on portrait-only apps.

0 commit comments

Comments
 (0)