From 855f47e793a1142d216efe7a3723d882ccaa5e61 Mon Sep 17 00:00:00 2001 From: Severiano Jaramillo Date: Tue, 27 Aug 2019 13:47:25 -0500 Subject: [PATCH] Generate QR in background thread. - Moved the logic to generate the QR Code image from ReceiveTransactionFragment to its ViewModel (ReceiveTransactionViewModel), and used coroutines to send that process to a background thread to make sure this does not freeze the UI. --- .../fragments/ReceiveTransactionFragment.kt | 68 ++----------------- .../viewmodels/ReceiveTransactionViewModel.kt | 62 +++++++++++++++++ 2 files changed, 69 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/ReceiveTransactionFragment.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/ReceiveTransactionFragment.kt index 95b6f8d..a010e8e 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/ReceiveTransactionFragment.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/ReceiveTransactionFragment.kt @@ -1,10 +1,7 @@ package cy.agorise.bitsybitshareswallet.fragments -import android.Manifest import android.content.Intent import android.content.pm.PackageManager -import android.graphics.Bitmap -import android.graphics.Color import android.os.Bundle import android.preference.PreferenceManager import android.util.Log @@ -17,11 +14,6 @@ import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import com.crashlytics.android.Crashlytics import com.google.common.primitives.UnsignedLong -import com.google.zxing.BarcodeFormat -import com.google.zxing.EncodeHintType -import com.google.zxing.MultiFormatWriter -import com.google.zxing.WriterException -import com.google.zxing.common.BitMatrix import com.jakewharton.rxbinding3.widget.textChanges import cy.agorise.bitsybitshareswallet.R import cy.agorise.bitsybitshareswallet.adapters.AssetsAdapter @@ -43,6 +35,7 @@ import java.text.DecimalFormatSymbols import java.util.* import java.util.concurrent.TimeUnit import kotlin.collections.ArrayList +import kotlin.math.min class ReceiveTransactionFragment : ConnectedFragment() { @@ -144,6 +137,10 @@ class ReceiveTransactionFragment : ConnectedFragment() { } }) + mViewModel.qrCodeBitmap.observe(this, Observer { bitmap -> + ivQR.setImageBitmap(bitmap) + }) + spAsset.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{ override fun onNothingSelected(parent: AdapterView<*>?) { } @@ -251,7 +248,6 @@ class ReceiveTransactionFragment : ConnectedFragment() { } } - // TODO use coroutines to move this process to a background thread private fun updateQR() { if (mAsset == null) { ivQR.setImageDrawable(null) @@ -276,64 +272,14 @@ class ReceiveTransactionFragment : ConnectedFragment() { asset.symbol.replaceFirst("bit", ""), items, "", "") Log.d(TAG, "invoice: " + invoice.toJsonString()) try { - val bitmap = encodeAsBitmap(Invoice.toQrCode(invoice), "#139657") // PalmPay green - ivQR.setImageBitmap(bitmap) + mViewModel.updateInvoice(invoice, min(ivQR.width, ivQR.height)) updateAmountAddressUI(amount, asset.symbol, asset.precision, mUserAccount!!.name) - } catch (e: WriterException) { - Log.e(TAG, "WriterException. Msg: " + e.message) - Crashlytics.logException(e) } catch (e: NullPointerException) { Log.e(TAG, "NullPointerException. Msg: " + e.message) Crashlytics.logException(e) } } - /** - * Encodes the provided data as a QR-code. Used to provide payment requests. - * @param data: Data containing payment request data as the recipient's address and the requested amount. - * @param color: The color used for the QR-code - * @return Bitmap with the QR-code encoded data - * @throws WriterException if QR Code cannot be generated - */ - @Throws(WriterException::class) - internal fun encodeAsBitmap(data: String, color: String): Bitmap? { - val result: BitMatrix - - // Get measured width and height of the ImageView where the QR code will be placed and make sure - // the final QR code has a squared shape - val w = Math.min(ivQR.width, ivQR.height) - val h = w - - try { - val hints = HashMap() - hints[EncodeHintType.MARGIN] = 0 - result = MultiFormatWriter().encode( - data, - BarcodeFormat.QR_CODE, w, h, hints - ) - } catch (iae: IllegalArgumentException) { - // Unsupported format - return null - } - - val pixels = IntArray(w * h) - for (y in 0 until h) { - val offset = y * w - for (x in 0 until w) { - pixels[offset + x] = if (result.get(x, y)) Color.parseColor(color) else Color.WHITE - } - } - - return try { - val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888) - bitmap.setPixels(pixels, 0, w, 0, 0, w, h) - bitmap - } catch (e: IllegalArgumentException) { - Crashlytics.logException(e) - null - } - } - /** * Updates the UI to show the amount and account to send the payment */ @@ -369,7 +315,7 @@ class ReceiveTransactionFragment : ConnectedFragment() { } private fun verifyStoragePermission() { - if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE) + if (ContextCompat.checkSelfPermission(activity!!, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // Permission is not already granted requestPermissions(arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE), diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/viewmodels/ReceiveTransactionViewModel.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/viewmodels/ReceiveTransactionViewModel.kt index 822457e..9c977e8 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/viewmodels/ReceiveTransactionViewModel.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/viewmodels/ReceiveTransactionViewModel.kt @@ -1,18 +1,36 @@ package cy.agorise.bitsybitshareswallet.viewmodels import android.app.Application +import android.graphics.Bitmap +import android.graphics.Color +import android.util.Log import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import com.google.zxing.BarcodeFormat +import com.google.zxing.EncodeHintType +import com.google.zxing.MultiFormatWriter +import com.google.zxing.WriterException import cy.agorise.bitsybitshareswallet.database.entities.Asset import cy.agorise.bitsybitshareswallet.database.entities.UserAccount import cy.agorise.bitsybitshareswallet.repositories.AssetRepository import cy.agorise.bitsybitshareswallet.repositories.UserAccountRepository +import cy.agorise.graphenej.Invoice +import kotlinx.coroutines.Dispatchers.Default +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.util.HashMap class ReceiveTransactionViewModel(application: Application) : AndroidViewModel(application) { private var mUserAccountRepository = UserAccountRepository(application) private var mAssetRepository = AssetRepository(application) + private val _qrCodeBitmap = MutableLiveData() + val qrCodeBitmap: LiveData + get() = _qrCodeBitmap + internal fun getUserAccount(id: String): LiveData { return mUserAccountRepository.getUserAccount(id) } @@ -20,4 +38,48 @@ class ReceiveTransactionViewModel(application: Application) : AndroidViewModel(a internal fun getAllNonZero(): LiveData> { return mAssetRepository.getAllNonZero() } + + internal fun updateInvoice(invoice: Invoice, size: Int) { + viewModelScope.launch { + try { + _qrCodeBitmap.value = encodeAsBitmap(Invoice.toQrCode(invoice), "#139657", size) // PalmPay green + } catch (e: Exception) { + Log.d("ReceiveTransactionVM", e.message) + } + } + } + + /** + * Encodes the provided data as a QR-code. Used to provide payment requests. + * @param data: Data containing payment request data as the recipient's address and the requested amount. + * @param color: The color used for the QR-code + * @param size: The size in pixels of the QR-code to generate + * @return Bitmap with the QR-code encoded data + * @throws WriterException if QR Code cannot be generated + * @throws IllegalArgumentException IllegalArgumentException + */ + @Throws(WriterException::class, IllegalArgumentException::class) + private suspend fun encodeAsBitmap(data: String, color: String, size: Int): Bitmap? = + withContext(Default) { + + val hints = HashMap() + hints[EncodeHintType.MARGIN] = 0 + val result = MultiFormatWriter().encode( + data, + BarcodeFormat.QR_CODE, size, size, hints + ) + + val pixels = IntArray(size * size) + for (y in 0 until size) { + val offset = y * size + for (x in 0 until size) { + pixels[offset + x] = + if (result.get(x, y)) Color.parseColor(color) else Color.WHITE + } + } + + val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) + bitmap.setPixels(pixels, 0, size, 0, 0, size, size) + bitmap + } } \ No newline at end of file