From 6b7b8070355d44841a9602e7790e22a0a86b37ca Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 10 Mar 2020 15:01:20 -0300 Subject: Support base64 encoded images in rfc2397 data URLs for product images --- .../java/net/taler/wallet/payment/ContractTerms.kt | 6 +++-- .../net/taler/wallet/payment/PaymentManager.kt | 21 +++++++++++++-- .../net/taler/wallet/payment/ProductAdapter.kt | 16 +++++++++++ app/src/main/res/layout/list_item_product.xml | 31 +++++++++++++++++----- 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/net/taler/wallet/payment/ContractTerms.kt b/app/src/main/java/net/taler/wallet/payment/ContractTerms.kt index a9f75ed..da91dea 100644 --- a/app/src/main/java/net/taler/wallet/payment/ContractTerms.kt +++ b/app/src/main/java/net/taler/wallet/payment/ContractTerms.kt @@ -29,20 +29,22 @@ data class ContractTerms( ) interface Product { - val id: String + val id: String? val description: String val price: Amount val location: String? + val image: String? } @JsonIgnoreProperties("totalPrice") data class ContractProduct( @JsonProperty("product_id") - override val id: String, + override val id: String?, override val description: String, override val price: Amount, @JsonProperty("delivery_location") override val location: String?, + override val image: String?, val quantity: Int ) : Product { diff --git a/app/src/main/java/net/taler/wallet/payment/PaymentManager.kt b/app/src/main/java/net/taler/wallet/payment/PaymentManager.kt index a00a686..ee0edaf 100644 --- a/app/src/main/java/net/taler/wallet/payment/PaymentManager.kt +++ b/app/src/main/java/net/taler/wallet/payment/PaymentManager.kt @@ -26,6 +26,9 @@ import net.taler.wallet.Amount import net.taler.wallet.TAG import net.taler.wallet.backend.WalletBackendApi import org.json.JSONObject +import java.net.MalformedURLException + +val REGEX_PRODUCT_IMAGE = Regex("^data:image/(jpeg|png);base64,([A-Za-z0-9+/=]+)$") class PaymentManager( private val walletBackendApi: WalletBackendApi, @@ -61,7 +64,12 @@ class PaymentManager( } else -> { val status = result.getString("status") - mPayStatus.postValue(getPayStatusUpdate(status, result)) + try { + mPayStatus.postValue(getPayStatusUpdate(status, result)) + } catch (e: Exception) { + Log.e(TAG, "Error getting PayStatusUpdate", e) + mPayStatus.postValue(PayStatus.Error(e.message ?: "unknown error")) + } } } } @@ -80,7 +88,16 @@ class PaymentManager( } private fun getContractTerms(json: JSONObject): ContractTerms { - return mapper.readValue(json.getString("contractTermsRaw")) + val terms: ContractTerms = mapper.readValue(json.getString("contractTermsRaw")) + // validate product images + terms.products.forEach { product -> + product.image?.let { image -> + if (REGEX_PRODUCT_IMAGE.matchEntire(image) == null) { + throw MalformedURLException("Invalid image data URL for ${product.description}") + } + } + } + return terms } @UiThread diff --git a/app/src/main/java/net/taler/wallet/payment/ProductAdapter.kt b/app/src/main/java/net/taler/wallet/payment/ProductAdapter.kt index 3a0dca6..8519dcb 100644 --- a/app/src/main/java/net/taler/wallet/payment/ProductAdapter.kt +++ b/app/src/main/java/net/taler/wallet/payment/ProductAdapter.kt @@ -16,9 +16,14 @@ package net.taler.wallet.payment +import android.graphics.BitmapFactory.decodeByteArray +import android.util.Base64 import android.view.LayoutInflater import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE import android.view.ViewGroup +import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ViewHolder @@ -50,11 +55,22 @@ internal class ProductAdapter : RecyclerView.Adapter() { internal inner class ProductViewHolder(v: View) : ViewHolder(v) { private val quantity: TextView = v.findViewById(R.id.quantity) + private val image: ImageView = v.findViewById(R.id.image) private val name: TextView = v.findViewById(R.id.name) private val price: TextView = v.findViewById(R.id.price) fun bind(product: ContractProduct) { quantity.text = product.quantity.toString() + if (product.image == null) { + image.visibility = GONE + } else { + image.visibility = VISIBLE + // product.image was validated before, so non-null below + val match = REGEX_PRODUCT_IMAGE.matchEntire(product.image)!! + val decodedString = Base64.decode(match.groups[2]!!.value, Base64.DEFAULT) + val bitmap = decodeByteArray(decodedString, 0, decodedString.size) + image.setImageBitmap(bitmap) + } name.text = product.description price.text = product.totalPrice.toString() } diff --git a/app/src/main/res/layout/list_item_product.xml b/app/src/main/res/layout/list_item_product.xml index 606022e..fe6ba23 100644 --- a/app/src/main/res/layout/list_item_product.xml +++ b/app/src/main/res/layout/list_item_product.xml @@ -1,5 +1,4 @@ - -