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.
This commit is contained in:
parent
2a88821710
commit
855f47e793
2 changed files with 69 additions and 61 deletions
|
@ -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<EncodeHintType, Any>()
|
||||
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),
|
||||
|
|
|
@ -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<Bitmap>()
|
||||
val qrCodeBitmap: LiveData<Bitmap>
|
||||
get() = _qrCodeBitmap
|
||||
|
||||
internal fun getUserAccount(id: String): LiveData<UserAccount> {
|
||||
return mUserAccountRepository.getUserAccount(id)
|
||||
}
|
||||
|
@ -20,4 +38,48 @@ class ReceiveTransactionViewModel(application: Application) : AndroidViewModel(a
|
|||
internal fun getAllNonZero(): LiveData<List<Asset>> {
|
||||
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<EncodeHintType, Any>()
|
||||
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
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue