Skip to content

Commit 0dc5b05

Browse files
committed
feat: add gallery button on qr to pick images
1 parent a971f09 commit 0dc5b05

File tree

4 files changed

+96
-1
lines changed

4 files changed

+96
-1
lines changed

quickie/src/main/AndroidManifest.xml

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
android:required="false"/>
88

99
<uses-permission android:name="android.permission.CAMERA"/>
10-
10+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
11+
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
1112
<application>
1213

1314
<activity

quickie/src/main/kotlin/io/github/g00fy2/quickie/QRScannerActivity.kt

+69
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package io.github.g00fy2.quickie
22

3+
import android.Manifest
34
import android.Manifest.permission.CAMERA
45
import android.app.Activity
56
import android.app.Dialog
67
import android.content.Intent
78
import android.content.pm.PackageManager
9+
import android.net.Uri
10+
import android.os.Build
811
import android.os.Bundle
912
import android.util.Size
1013
import android.view.HapticFeedbackConstants
@@ -26,7 +29,10 @@ import androidx.core.content.IntentCompat
2629
import androidx.core.view.ViewCompat
2730
import androidx.core.view.WindowCompat
2831
import androidx.core.view.WindowInsetsCompat
32+
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
33+
import com.google.mlkit.vision.barcode.BarcodeScanning
2934
import com.google.mlkit.vision.barcode.common.Barcode
35+
import com.google.mlkit.vision.common.InputImage
3036
import io.github.g00fy2.quickie.config.ParcelableScannerConfig
3137
import io.github.g00fy2.quickie.databinding.QuickieScannerActivityBinding
3238
import 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
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
4+
5+
<item>
6+
<shape android:shape="rectangle">
7+
<!-- Set the background color -->
8+
<solid android:color="@color/quickie_white" />
9+
10+
<corners android:radius="24dp" />
11+
</shape>
12+
</item>
13+
14+
</layer-list>

quickie/src/main/res/layout/quickie_scanner_activity.xml

+11
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,15 @@
1919
android:visibility="invisible"
2020
tools:visibility="visible"
2121
/>
22+
<Button
23+
android:id="@+id/selectFromGalleryButton"
24+
android:layout_width="wrap_content"
25+
android:layout_height="wrap_content"
26+
android:text="Select from Gallery"
27+
android:background="@drawable/quickie_rounded_rectangle_button"
28+
android:textColor="@android:color/black"
29+
android:padding="16dp"
30+
android:layout_gravity="bottom|center_horizontal"
31+
android:layout_marginBottom="32dp" />
32+
2233
</FrameLayout>

0 commit comments

Comments
 (0)