From b3546e6c3a2bcf16bc3a5250c6b1df9e5300ad6d Mon Sep 17 00:00:00 2001 From: septs Date: Sat, 8 Mar 2025 17:05:16 +0800 Subject: [PATCH 1/2] feat: enhance QR code processing from gallery and improve error handling --- .../DownloadWizardMethodSelectFragment.kt | 52 ++++++++++--------- .../java/im/angry/openeuicc/util/Utils.kt | 15 +++--- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardMethodSelectFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardMethodSelectFragment.kt index 6d86f6c..8ebc7ee 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardMethodSelectFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardMethodSelectFragment.kt @@ -2,7 +2,9 @@ package im.angry.openeuicc.ui.wizard import android.app.AlertDialog import android.content.ClipboardManager +import android.graphics.Bitmap import android.graphics.BitmapFactory +import android.net.Uri import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -20,7 +22,9 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.journeyapps.barcodescanner.ScanContract import com.journeyapps.barcodescanner.ScanOptions import im.angry.openeuicc.common.R -import im.angry.openeuicc.util.* +import im.angry.openeuicc.util.ActivationCode +import im.angry.openeuicc.util.decodeQrFromBitmap +import im.angry.openeuicc.util.preferenceRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -28,6 +32,10 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext class DownloadWizardMethodSelectFragment : DownloadWizardActivity.DownloadWizardStepFragment() { + companion object { + const val TAG = "DownloadWizardMethodSelectFragment" + } + data class DownloadMethod( val iconRes: Int, val titleRes: Int, @@ -35,34 +43,30 @@ class DownloadWizardMethodSelectFragment : DownloadWizardActivity.DownloadWizard ) // TODO: Maybe we should find a better barcode scanner (or an external one?) - private val barcodeScannerLauncher = registerForActivityResult(ScanContract()) { result -> - result.contents?.let { content -> - processLpaString(content) + private val barcodeScannerLauncher = + registerForActivityResult(ScanContract()) { + processLpaString(it.contents ?: return@registerForActivityResult) } - } private val gallerySelectorLauncher = - registerForActivityResult(ActivityResultContracts.GetContent()) { result -> - if (result == null) return@registerForActivityResult - - lifecycleScope.launch(Dispatchers.IO) { - runCatching { - requireContext().contentResolver.openInputStream(result)?.let { input -> - val bmp = BitmapFactory.decodeStream(input) - input.close() - - decodeQrFromBitmap(bmp)?.let { - withContext(Dispatchers.Main) { - processLpaString(it) - } - } - - bmp.recycle() - } - } - } + registerForActivityResult(ActivityResultContracts.GetContent()) { + lifecycleScope.launch(Dispatchers.IO) { onGalleryResult(it) } } + private suspend fun onGalleryResult(result: Uri?) { + var bmp: Bitmap? = null + try { + bmp = requireContext().contentResolver.openInputStream(result ?: return) + .use(BitmapFactory::decodeStream) + val decoded = decodeQrFromBitmap(bmp) + withContext(Dispatchers.Main) { processLpaString(decoded) } + } catch (e: Exception) { + Log.e(TAG, "Failed to decode QR code from gallery", e) + } finally { + bmp?.recycle() + } + } + val downloadMethods = arrayOf( DownloadMethod(R.drawable.ic_scan_black, R.string.download_wizard_method_qr_code) { barcodeScannerLauncher.launch(ScanOptions().apply { diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt b/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt index 444c176..fd32eac 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt @@ -86,13 +86,12 @@ suspend fun connectSEService(context: Context): SEService = suspendCoroutine { c } } -fun decodeQrFromBitmap(bmp: Bitmap): String? = - runCatching { - val pixels = IntArray(bmp.width * bmp.height) - bmp.getPixels(pixels, 0, bmp.width, 0, 0, bmp.width, bmp.height) +fun decodeQrFromBitmap(bmp: Bitmap): String { + val pixels = IntArray(bmp.width * bmp.height) + bmp.getPixels(pixels, 0, bmp.width, 0, 0, bmp.width, bmp.height) - val luminanceSource = RGBLuminanceSource(bmp.width, bmp.height, pixels) - val binaryBmp = BinaryBitmap(HybridBinarizer(luminanceSource)) + val luminanceSource = RGBLuminanceSource(bmp.width, bmp.height, pixels) + val binaryBmp = BinaryBitmap(HybridBinarizer(luminanceSource)) - QRCodeReader().decode(binaryBmp).text - }.getOrNull() + return QRCodeReader().decode(binaryBmp).text +} From d490d6f2396495ea4a6e2ae51b8cd14b13ffe1ed Mon Sep 17 00:00:00 2001 From: septs Date: Sat, 8 Mar 2025 17:23:58 +0800 Subject: [PATCH 2/2] feat: improve QR code decoding from gallery with better resource management --- .../DownloadWizardMethodSelectFragment.kt | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardMethodSelectFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardMethodSelectFragment.kt index 8ebc7ee..794109c 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardMethodSelectFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardMethodSelectFragment.kt @@ -50,21 +50,19 @@ class DownloadWizardMethodSelectFragment : DownloadWizardActivity.DownloadWizard private val gallerySelectorLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { - lifecycleScope.launch(Dispatchers.IO) { onGalleryResult(it) } + lifecycleScope.launch(Dispatchers.IO) { + val decoded = onGalleryResult(it ?: return@launch) ?: return@launch + withContext(Dispatchers.Main) { processLpaString(decoded) } + } } - private suspend fun onGalleryResult(result: Uri?) { - var bmp: Bitmap? = null - try { - bmp = requireContext().contentResolver.openInputStream(result ?: return) - .use(BitmapFactory::decodeStream) - val decoded = decodeQrFromBitmap(bmp) - withContext(Dispatchers.Main) { processLpaString(decoded) } - } catch (e: Exception) { - Log.e(TAG, "Failed to decode QR code from gallery", e) - } finally { - bmp?.recycle() - } + private fun onGalleryResult(result: Uri) = try { + requireContext().contentResolver.openInputStream(result) + .use(BitmapFactory::decodeStream) + .use(::decodeQrFromBitmap) + } catch (e: Exception) { + Log.e(TAG, "Failed to decode QR code from gallery", e) + null } val downloadMethods = arrayOf( @@ -192,4 +190,10 @@ class DownloadWizardMethodSelectFragment : DownloadWizardActivity.DownloadWizard } } +} + +private fun Bitmap.use(block: Bitmap.() -> T): T = try { + block() +} finally { + recycle() } \ No newline at end of file