11package io.github.g00fy2.quickie
22
3+ import android.Manifest
34import android.Manifest.permission.CAMERA
45import android.app.Activity
56import android.app.Dialog
67import android.content.Intent
78import android.content.pm.PackageManager
9+ import android.net.Uri
10+ import android.os.Build
811import android.os.Bundle
912import android.util.Size
1013import android.view.HapticFeedbackConstants
@@ -26,7 +29,10 @@ import androidx.core.content.IntentCompat
2629import androidx.core.view.ViewCompat
2730import androidx.core.view.WindowCompat
2831import androidx.core.view.WindowInsetsCompat
32+ import com.google.mlkit.vision.barcode.BarcodeScannerOptions
33+ import com.google.mlkit.vision.barcode.BarcodeScanning
2934import com.google.mlkit.vision.barcode.common.Barcode
35+ import com.google.mlkit.vision.common.InputImage
3036import io.github.g00fy2.quickie.config.ParcelableScannerConfig
3137import io.github.g00fy2.quickie.databinding.QuickieScannerActivityBinding
3238import io.github.g00fy2.quickie.extensions.toParcelableContentType
@@ -43,6 +49,9 @@ internal class QRScannerActivity : AppCompatActivity() {
4349 private var showTorchToggle = false
4450 private var showCloseButton = false
4551 private var useFrontCamera = false
52+ private val galleryLauncher= registerForActivityResult(ActivityResultContracts .GetContent ()) { uri: Uri ? ->
53+ uri?.let { analyzeImageFromGallery(it) }
54+ }
4655 internal var errorDialog: Dialog ? = null
4756 set(value) {
4857 field = value
@@ -68,6 +77,7 @@ internal class QRScannerActivity : AppCompatActivity() {
6877
6978 setupEdgeToEdgeUI()
7079 applyScannerConfig()
80+ setupGalleryButton()
7181
7282 analysisExecutor = Executors .newSingleThreadExecutor()
7383
@@ -81,6 +91,64 @@ internal class QRScannerActivity : AppCompatActivity() {
8191 }
8292 }
8393
94+ private fun setupGalleryButton () {
95+ binding.selectFromGalleryButton.setOnClickListener {
96+ launchGalleryPicker()
97+ }
98+ }
99+ private val storagePermissionLauncher = registerForActivityResult(ActivityResultContracts .RequestPermission ()) { granted ->
100+ if (granted) {
101+ galleryLauncher.launch(IMAGE_MIME_TYPE )
102+ } else {
103+ onFailure(Exception (" Storage permission required to select images" ))
104+ }
105+ }
106+ private fun launchGalleryPicker () {
107+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
108+ if (ContextCompat .checkSelfPermission(this , Manifest .permission.READ_MEDIA_IMAGES ) == PackageManager .PERMISSION_GRANTED ) {
109+ galleryLauncher.launch(IMAGE_MIME_TYPE )
110+ } else {
111+ storagePermissionLauncher.launch(Manifest .permission.READ_MEDIA_IMAGES )
112+ }
113+ } else {
114+ if (ContextCompat .checkSelfPermission(this , Manifest .permission.READ_EXTERNAL_STORAGE ) == PackageManager .PERMISSION_GRANTED ) {
115+ galleryLauncher.launch(IMAGE_MIME_TYPE )
116+ } else {
117+ storagePermissionLauncher.launch(Manifest .permission.READ_EXTERNAL_STORAGE )
118+ }
119+ }
120+ }
121+
122+ private fun analyzeImageFromGallery (imageUri : Uri ) {
123+ try {
124+ val image = InputImage .fromFilePath(this , imageUri)
125+ val options = BarcodeScannerOptions .Builder ()
126+ .setBarcodeFormats(barcodeFormats.sum())
127+ .build()
128+
129+ val scanner = BarcodeScanning .getClient(options)
130+
131+ binding.overlayView.isLoading = true
132+
133+ scanner.process(image)
134+ .addOnSuccessListener { barcodes ->
135+ binding.overlayView.isLoading = false
136+ if (barcodes.isNullOrEmpty()) {
137+ onFailure(Exception (" No QR code found in the image" ))
138+ } else {
139+ onSuccess(barcodes.first())
140+ }
141+ }
142+ .addOnFailureListener { e ->
143+ binding.overlayView.isLoading = false
144+ onFailure(e)
145+ }
146+ } catch (e: Exception ) {
147+ binding.overlayView.isLoading = false
148+ onFailure(e)
149+ }
150+ }
151+
84152 override fun onDestroy () {
85153 super .onDestroy()
86154 analysisExecutor.shutdown()
@@ -216,6 +284,7 @@ internal class QRScannerActivity : AppCompatActivity() {
216284 const val EXTRA_RESULT_TYPE = " quickie-type"
217285 const val EXTRA_RESULT_PARCELABLE = " quickie-parcelable"
218286 const val EXTRA_RESULT_EXCEPTION = " quickie-exception"
287+ const val IMAGE_MIME_TYPE = " image/*"
219288 const val RESULT_MISSING_PERMISSION = RESULT_FIRST_USER + 1
220289 const val RESULT_ERROR = RESULT_FIRST_USER + 2
221290 }
0 commit comments