diff options
author | Torsten Grote <t@grobox.de> | 2020-02-14 12:10:10 -0300 |
---|---|---|
committer | Torsten Grote <t@grobox.de> | 2020-02-14 12:10:10 -0300 |
commit | b2db61af90cde2c686bd75b7e8b8e69cb736a3b9 (patch) | |
tree | efcb221e87edd33b70415b9241c0dedc4a064fe9 /app/src/main/java/net/taler/wallet | |
parent | ada382885e9c103fe0795817a8585270a3079302 (diff) | |
download | wallet-android-b2db61af90cde2c686bd75b7e8b8e69cb736a3b9.tar.gz wallet-android-b2db61af90cde2c686bd75b7e8b8e69cb736a3b9.tar.bz2 wallet-android-b2db61af90cde2c686bd75b7e8b8e69cb736a3b9.zip |
Show products included in contract terms when paying
Diffstat (limited to 'app/src/main/java/net/taler/wallet')
6 files changed, 141 insertions, 8 deletions
diff --git a/app/src/main/java/net/taler/wallet/Amount.kt b/app/src/main/java/net/taler/wallet/Amount.kt index 2b41be1..f8b48e3 100644 --- a/app/src/main/java/net/taler/wallet/Amount.kt +++ b/app/src/main/java/net/taler/wallet/Amount.kt @@ -18,11 +18,16 @@ package net.taler.wallet +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.deser.std.StdDeserializer import org.json.JSONObject import kotlin.math.round private const val FRACTIONAL_BASE = 1e8 +@JsonDeserialize(using = AmountDeserializer::class) data class Amount(val currency: String, val amount: String) { fun isZero(): Boolean { return amount.toDouble() == 0.0 @@ -46,6 +51,17 @@ data class Amount(val currency: String, val amount: String) { return Amount(components[0], components[1]) } } + + override fun toString(): String { + return String.format("%.2f $currency", amount.toDouble()) + } +} + +class AmountDeserializer : StdDeserializer<Amount>(Amount::class.java) { + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Amount { + val node = p.codec.readValue(p, String::class.java) + return Amount.fromString(node) + } } class ParsedAmount( diff --git a/app/src/main/java/net/taler/wallet/WalletViewModel.kt b/app/src/main/java/net/taler/wallet/WalletViewModel.kt index af1037e..3ed271b 100644 --- a/app/src/main/java/net/taler/wallet/WalletViewModel.kt +++ b/app/src/main/java/net/taler/wallet/WalletViewModel.kt @@ -116,12 +116,13 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) { private var currentWithdrawRequestId = 0 private val walletBackendApi = WalletBackendApi(app) - val paymentManager = PaymentManager(walletBackendApi) private val mapper = ObjectMapper() .registerModule(KotlinModule()) .configure(FAIL_ON_UNKNOWN_PROPERTIES, false) + val paymentManager = PaymentManager(walletBackendApi, mapper) + fun init() { if (initialized) { Log.e(TAG, "WalletViewModel already initialized") diff --git a/app/src/main/java/net/taler/wallet/payment/ContractTerms.kt b/app/src/main/java/net/taler/wallet/payment/ContractTerms.kt new file mode 100644 index 0000000..f577600 --- /dev/null +++ b/app/src/main/java/net/taler/wallet/payment/ContractTerms.kt @@ -0,0 +1,38 @@ +package net.taler.wallet.payment + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonProperty +import net.taler.wallet.Amount + + +@JsonIgnoreProperties(ignoreUnknown = true) +data class ContractTerms( + val summary: String, + val products: List<ContractProduct>, + val amount: Amount +) + +interface Product { + val id: String + val description: String + val price: Amount + val location: String? +} + +@JsonIgnoreProperties("totalPrice") +data class ContractProduct( + @JsonProperty("product_id") + override val id: String, + override val description: String, + override val price: Amount, + @JsonProperty("delivery_location") + override val location: String?, + val quantity: Int +) : Product { + + val totalPrice: Amount by lazy { + val amount = price.amount.toDouble() * quantity + Amount(price.currency, amount.toString()) + } + +} 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 2e40250..489940f 100644 --- a/app/src/main/java/net/taler/wallet/payment/PaymentManager.kt +++ b/app/src/main/java/net/taler/wallet/payment/PaymentManager.kt @@ -4,21 +4,30 @@ import android.util.Log import androidx.annotation.UiThread import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue import net.taler.wallet.Amount import net.taler.wallet.TAG import net.taler.wallet.backend.WalletBackendApi import org.json.JSONObject -class PaymentManager(private val walletBackendApi: WalletBackendApi) { +class PaymentManager( + private val walletBackendApi: WalletBackendApi, + private val mapper: ObjectMapper +) { private val mPayStatus = MutableLiveData<PayStatus>(PayStatus.None) internal val payStatus: LiveData<PayStatus> = mPayStatus + private val mDetailsShown = MutableLiveData<Boolean>() + internal val detailsShown: LiveData<Boolean> = mDetailsShown + private var currentPayRequestId = 0 @UiThread fun preparePay(url: String) { mPayStatus.value = PayStatus.Loading + mDetailsShown.value = false val args = JSONObject(mapOf("url" to url)) @@ -55,10 +64,13 @@ class PaymentManager(private val walletBackendApi: WalletBackendApi) { } private fun getContractTerms(json: JSONObject): ContractTerms { - val ctJson = JSONObject(json.getString("contractTermsRaw")) - val amount = Amount.fromString(ctJson.getString("amount")) - val summary = ctJson.getString("summary") - return ContractTerms(summary, amount) + return mapper.readValue(json.getString("contractTermsRaw")) + } + + @UiThread + fun toggleDetailsShown() { + val oldValue = mDetailsShown.value ?: false + mDetailsShown.value = !oldValue } fun confirmPay(proposalId: String) { @@ -104,5 +116,3 @@ sealed class PayStatus { data class Error(val error: String) : PayStatus() object Success : PayStatus() } - -data class ContractTerms(val summary: String, val amount: Amount) diff --git a/app/src/main/java/net/taler/wallet/payment/ProductAdapter.kt b/app/src/main/java/net/taler/wallet/payment/ProductAdapter.kt new file mode 100644 index 0000000..f23e1cb --- /dev/null +++ b/app/src/main/java/net/taler/wallet/payment/ProductAdapter.kt @@ -0,0 +1,47 @@ +package net.taler.wallet.payment + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import net.taler.wallet.R +import net.taler.wallet.payment.ProductAdapter.ProductViewHolder + + +internal class ProductAdapter : RecyclerView.Adapter<ProductViewHolder>() { + + private val items = ArrayList<ContractProduct>() + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder { + val view = + LayoutInflater.from(parent.context).inflate(R.layout.list_item_product, parent, false) + return ProductViewHolder(view) + } + + override fun onBindViewHolder(holder: ProductViewHolder, position: Int) { + holder.bind(items[position]) + } + + fun setItems(items: List<ContractProduct>) { + this.items.clear() + this.items.addAll(items) + notifyDataSetChanged() + } + + internal inner class ProductViewHolder(v: View) : ViewHolder(v) { + private val quantity: TextView = v.findViewById(R.id.quantity) + 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() + name.text = product.description + price.text = product.totalPrice.toString() + } + } + +} diff --git a/app/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt b/app/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt index 4b4bf01..d623788 100644 --- a/app/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt +++ b/app/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt @@ -20,13 +20,17 @@ import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View +import android.view.View.GONE import android.view.View.INVISIBLE import android.view.View.VISIBLE import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Observer import androidx.lifecycle.observe import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.transition.TransitionManager.beginDelayedTransition import kotlinx.android.synthetic.main.fragment_prompt_payment.* import net.taler.wallet.Amount import net.taler.wallet.R @@ -39,6 +43,7 @@ class PromptPaymentFragment : Fragment() { private val model: WalletViewModel by activityViewModels() private val paymentManager by lazy { model.paymentManager } + private val adapter = ProductAdapter() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -49,6 +54,20 @@ class PromptPaymentFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { paymentManager.payStatus.observe(viewLifecycleOwner, this::onPaymentStatusChanged) + paymentManager.detailsShown.observe(viewLifecycleOwner, Observer { shown -> + beginDelayedTransition(view as ViewGroup) + val res = if (shown) R.string.payment_hide_details else R.string.payment_show_details + detailsButton.setText(res) + productsList.visibility = if (shown) VISIBLE else GONE + }) + + detailsButton.setOnClickListener { + paymentManager.toggleDetailsShown() + } + productsList.apply { + adapter = this@PromptPaymentFragment.adapter + layoutManager = LinearLayoutManager(requireContext()) + } button_abort_payment.setOnClickListener { when (val ps = paymentManager.payStatus.value) { @@ -111,6 +130,7 @@ class PromptPaymentFragment : Fragment() { private fun showOrder(contractTerms: ContractTerms, totalFees: Amount?) { order_summary.text = contractTerms.summary + adapter.setItems(contractTerms.products) val amount = contractTerms.amount @SuppressLint("SetTextI18n") order_amount.text = "${amount.amount} ${amount.currency}" @@ -123,6 +143,7 @@ class PromptPaymentFragment : Fragment() { } fadeInView(order_summary_label) fadeInView(order_summary) + if (contractTerms.products.isNotEmpty()) fadeInView(detailsButton) fadeInView(order_amount_label) fadeInView(order_amount) } |