[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-taler-android] 01/02: [wallet] implement exchange selection for w
From: |
gnunet |
Subject: |
[taler-taler-android] 01/02: [wallet] implement exchange selection for withdrawals |
Date: |
Mon, 28 Sep 2020 19:46:58 +0200 |
This is an automated email from the git hooks/post-receive script.
torsten-grote pushed a commit to branch master
in repository taler-android.
commit 0936fc851232c8c6c41ce4a0c479ba7a1e452137
Author: Torsten Grote <t@grobox.de>
AuthorDate: Mon Sep 28 12:12:20 2020 -0300
[wallet] implement exchange selection for withdrawals
---
.idea/codeStyles/Project.xml | 1 +
build.gradle | 2 +-
.../src/main/java/net/taler/common/Event.kt | 4 +
wallet/build.gradle | 2 +-
.../net/taler/wallet/exchanges/ExchangeAdapter.kt | 17 ++-
...ExchangeFragment.kt => ExchangeFeesFragment.kt} | 11 +-
.../taler/wallet/exchanges/ExchangeListFragment.kt | 20 +--
.../wallet/exchanges/SelectExchangeFragment.kt | 135 +++------------------
.../wallet/withdraw/PromptWithdrawFragment.kt | 88 +++++++++-----
.../net/taler/wallet/withdraw/WithdrawManager.kt | 52 +++++---
...ect_exchange.xml => fragment_exchange_fees.xml} | 0
.../main/res/layout/fragment_prompt_withdraw.xml | 2 +-
wallet/src/main/res/navigation/nav_graph.xml | 4 +-
wallet/src/main/res/values/strings.xml | 1 +
14 files changed, 158 insertions(+), 181 deletions(-)
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index b23c749..587f132 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -17,6 +17,7 @@
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS"
value="2147483647" />
+ <option name="ALLOW_TRAILING_COMMA" value="true" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
diff --git a/build.gradle b/build.gradle
index 8973530..7f12ed4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,5 @@
buildscript {
- ext.kotlin_version = '1.4.0'
+ ext.kotlin_version = '1.4.10'
ext.ktor_version = "1.4.0"
ext.nav_version = "2.3.0"
ext.material_version = "1.2.1"
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/Event.kt
b/taler-kotlin-android/src/main/java/net/taler/common/Event.kt
index 779247f..752e20e 100644
--- a/taler-kotlin-android/src/main/java/net/taler/common/Event.kt
+++ b/taler-kotlin-android/src/main/java/net/taler/common/Event.kt
@@ -34,6 +34,10 @@ open class Event<out T>(private val content: T) {
return if (isConsumed.compareAndSet(false, true)) content else null
}
+ fun getEvenIfConsumedAlready(): T {
+ return content
+ }
+
}
fun <T> T.toEvent() = Event(this)
diff --git a/wallet/build.gradle b/wallet/build.gradle
index 02123ee..48e1749 100644
--- a/wallet/build.gradle
+++ b/wallet/build.gradle
@@ -139,7 +139,7 @@ dependencies {
implementation 'me.zhanghai.android.materialprogressbar:library:1.6.1'
// Markdown rendering
- final def markwon_version = '4.5.1'
+ final def markwon_version = '4.6.0'
implementation "io.noties.markwon:core:$markwon_version"
implementation "io.noties.markwon:ext-tables:$markwon_version"
implementation "io.noties.markwon:recycler:$markwon_version"
diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt
b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt
index 17ac50f..e315632 100644
--- a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt
@@ -18,6 +18,8 @@ package net.taler.wallet.exchanges
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.ImageButton
import android.widget.TextView
@@ -39,11 +41,14 @@ data class ExchangeItem(
}
interface ExchangeClickListener {
+ fun onExchangeSelected(item: ExchangeItem)
fun onManualWithdraw(item: ExchangeItem)
}
-internal class ExchangeAdapter(private val listener: ExchangeClickListener) :
- Adapter<ExchangeItemViewHolder>() {
+internal class ExchangeAdapter(
+ private val selectOnly: Boolean,
+ private val listener: ExchangeClickListener,
+) : Adapter<ExchangeItemViewHolder>() {
private val items = ArrayList<ExchangeItem>()
@@ -74,6 +79,14 @@ internal class ExchangeAdapter(private val listener:
ExchangeClickListener) :
fun bind(item: ExchangeItem) {
urlView.text = item.name
currencyView.text =
context.getString(R.string.exchange_list_currency, item.currency)
+ if (selectOnly) {
+ itemView.setOnClickListener {
listener.onExchangeSelected(item) }
+ overflowIcon.visibility = GONE
+ } else {
+ itemView.setOnClickListener(null)
+ itemView.isClickable = false
+ overflowIcon.visibility = VISIBLE
+ }
overflowIcon.setOnClickListener { openMenu(overflowIcon, item) }
}
diff --git
a/wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt
b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFeesFragment.kt
similarity index 95%
copy from
wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt
copy to wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFeesFragment.kt
index a95a51c..c59fffe 100644
--- a/wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFeesFragment.kt
@@ -32,22 +32,23 @@ import net.taler.common.toShortDate
import net.taler.lib.common.Amount
import net.taler.wallet.MainViewModel
import net.taler.wallet.R
-import net.taler.wallet.databinding.FragmentSelectExchangeBinding
+import net.taler.wallet.databinding.FragmentExchangeFeesBinding
import net.taler.wallet.exchanges.CoinFeeAdapter.CoinFeeViewHolder
import net.taler.wallet.exchanges.WireFeeAdapter.WireFeeViewHolder
-class SelectExchangeFragment : Fragment() {
+class ExchangeFeesFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
private val withdrawManager by lazy { model.withdrawManager }
- private lateinit var ui: FragmentSelectExchangeBinding
+ private lateinit var ui: FragmentExchangeFeesBinding
override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
+ inflater: LayoutInflater,
+ container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
- ui = FragmentSelectExchangeBinding.inflate(inflater, container, false)
+ ui = FragmentExchangeFeesBinding.inflate(inflater, container, false)
return ui.root
}
diff --git
a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt
b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt
index 86b2519..9a96b59 100644
--- a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt
@@ -34,17 +34,19 @@ import net.taler.wallet.MainViewModel
import net.taler.wallet.R
import net.taler.wallet.databinding.FragmentExchangeListBinding
-class ExchangeListFragment : Fragment(), ExchangeClickListener {
+open class ExchangeListFragment : Fragment(), ExchangeClickListener {
- private val model: MainViewModel by activityViewModels()
+ protected val model: MainViewModel by activityViewModels()
private val exchangeManager by lazy { model.exchangeManager }
- private lateinit var ui: FragmentExchangeListBinding
- private val exchangeAdapter by lazy { ExchangeAdapter(this) }
+ protected lateinit var ui: FragmentExchangeListBinding
+ protected open val isSelectOnly = false
+ private val exchangeAdapter by lazy { ExchangeAdapter(isSelectOnly, this) }
override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
): View? {
ui = FragmentExchangeListBinding.inflate(inflater, container, false)
return ui.root
@@ -70,7 +72,7 @@ class ExchangeListFragment : Fragment(),
ExchangeClickListener {
})
}
- private fun onExchangeUpdate(exchanges: List<ExchangeItem>) {
+ protected open fun onExchangeUpdate(exchanges: List<ExchangeItem>) {
exchangeAdapter.update(exchanges)
if (exchanges.isEmpty()) {
ui.emptyState.fadeIn()
@@ -85,6 +87,10 @@ class ExchangeListFragment : Fragment(),
ExchangeClickListener {
Toast.makeText(requireContext(), R.string.exchange_add_error,
LENGTH_LONG).show()
}
+ override fun onExchangeSelected(item: ExchangeItem) {
+ throw AssertionError("must not get triggered here")
+ }
+
override fun onManualWithdraw(item: ExchangeItem) {
exchangeManager.withdrawalExchange = item
findNavController().navigate(R.id.action_nav_settings_exchanges_to_nav_exchange_manual_withdrawal)
diff --git
a/wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt
b/wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt
index a95a51c..61e0db5 100644
--- a/wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt
@@ -16,130 +16,33 @@
package net.taler.wallet.exchanges
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.View.GONE
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.core.content.ContextCompat.getColor
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
-import androidx.recyclerview.widget.RecyclerView.Adapter
-import androidx.recyclerview.widget.RecyclerView.ViewHolder
-import net.taler.common.toRelativeTime
-import net.taler.common.toShortDate
-import net.taler.lib.common.Amount
-import net.taler.wallet.MainViewModel
-import net.taler.wallet.R
-import net.taler.wallet.databinding.FragmentSelectExchangeBinding
-import net.taler.wallet.exchanges.CoinFeeAdapter.CoinFeeViewHolder
-import net.taler.wallet.exchanges.WireFeeAdapter.WireFeeViewHolder
+import androidx.navigation.fragment.findNavController
+import net.taler.common.fadeOut
-class SelectExchangeFragment : Fragment() {
+class SelectExchangeFragment : ExchangeListFragment() {
- private val model: MainViewModel by activityViewModels()
private val withdrawManager by lazy { model.withdrawManager }
- private lateinit var ui: FragmentSelectExchangeBinding
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- ui = FragmentSelectExchangeBinding.inflate(inflater, container, false)
- return ui.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- val fees = withdrawManager.exchangeFees ?: throw
IllegalStateException()
- if (fees.withdrawFee.isZero()) {
- ui.withdrawFeeLabel.visibility = GONE
- ui.withdrawFeeView.visibility = GONE
- } else ui.withdrawFeeView.setAmount(fees.withdrawFee)
- if (fees.overhead.isZero()) {
- ui.overheadLabel.visibility = GONE
- ui.overheadView.visibility = GONE
- } else ui.overheadView.setAmount(fees.overhead)
- ui.expirationView.text =
fees.earliestDepositExpiration.ms.toRelativeTime(requireContext())
- ui.coinFeesList.adapter = CoinFeeAdapter(fees.coinFees)
- ui.wireFeesList.adapter = WireFeeAdapter(fees.wireFees)
- }
-
- private fun TextView.setAmount(amount: Amount) {
- if (amount.isZero()) text = amount.toString()
- else {
- text = getString(R.string.amount_negative, amount)
- setTextColor(getColor(context, R.color.red))
- }
+ override val isSelectOnly = true
+ private val exchangeSelection by lazy {
+
requireNotNull(withdrawManager.exchangeSelection.value?.getEvenIfConsumedAlready())
}
-}
-
-private class CoinFeeAdapter(private val items: List<CoinFee>) :
Adapter<CoinFeeViewHolder>() {
- override fun getItemCount() = items.size
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
CoinFeeViewHolder {
- val v =
-
LayoutInflater.from(parent.context).inflate(R.layout.list_item_coin_fee,
parent, false)
- return CoinFeeViewHolder(v)
+ override fun onExchangeUpdate(exchanges: List<ExchangeItem>) {
+ ui.progressBar.fadeOut()
+ super.onExchangeUpdate(exchanges.filter { exchangeItem ->
+ exchangeItem.currency == exchangeSelection.amount.currency
+ })
}
- override fun onBindViewHolder(holder: CoinFeeViewHolder, position: Int) {
- holder.bind(items[position])
+ override fun onExchangeSelected(item: ExchangeItem) {
+ withdrawManager.getWithdrawalDetails(
+ exchangeBaseUrl = item.exchangeBaseUrl,
+ amount = exchangeSelection.amount,
+ showTosImmediately = true,
+ uri = exchangeSelection.talerWithdrawUri,
+ )
+ findNavController().navigateUp()
}
- private class CoinFeeViewHolder(private val v: View) : ViewHolder(v) {
- private val res = v.context.resources
- private val coinView: TextView = v.findViewById(R.id.coinView)
- private val withdrawFeeView: TextView =
v.findViewById(R.id.withdrawFeeView)
- private val depositFeeView: TextView =
v.findViewById(R.id.depositFeeView)
- private val refreshFeeView: TextView =
v.findViewById(R.id.refreshFeeView)
- private val refundFeeView: TextView =
v.findViewById(R.id.refundFeeView)
- fun bind(item: CoinFee) {
- coinView.text = res.getQuantityString(
- R.plurals.exchange_fee_coin,
- item.quantity,
- item.coin,
- item.quantity
- )
- withdrawFeeView.text =
- v.context.getString(R.string.exchange_fee_withdraw_fee,
item.feeWithdraw)
- depositFeeView.text =
- v.context.getString(R.string.exchange_fee_deposit_fee,
item.feeDeposit)
- refreshFeeView.text =
- v.context.getString(R.string.exchange_fee_refresh_fee,
item.feeRefresh)
- refundFeeView.text =
- v.context.getString(R.string.exchange_fee_refund_fee,
item.feeRefresh)
- }
- }
-}
-
-private class WireFeeAdapter(private val items: List<WireFee>) :
Adapter<WireFeeViewHolder>() {
- override fun getItemCount() = items.size
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
WireFeeViewHolder {
- val v =
-
LayoutInflater.from(parent.context).inflate(R.layout.list_item_wire_fee,
parent, false)
- return WireFeeViewHolder(v)
- }
-
- override fun onBindViewHolder(holder: WireFeeViewHolder, position: Int) {
- holder.bind(items[position])
- }
-
- private class WireFeeViewHolder(private val v: View) : ViewHolder(v) {
- private val validityView: TextView = v.findViewById(R.id.validityView)
- private val wireFeeView: TextView = v.findViewById(R.id.wireFeeView)
- private val closingFeeView: TextView =
v.findViewById(R.id.closingFeeView)
- fun bind(item: WireFee) {
- validityView.text = v.context.getString(
- R.string.exchange_fee_wire_fee_timespan,
- item.start.ms.toShortDate(v.context),
- item.end.ms.toShortDate(v.context)
- )
- wireFeeView.text =
- v.context.getString(R.string.exchange_fee_wire_fee_wire_fee,
item.wireFee)
- closingFeeView.text =
-
v.context.getString(R.string.exchange_fee_wire_fee_closing_fee, item.closingFee)
- }
- }
}
diff --git
a/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt
b/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt
index 0c7687c..38e09fa 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt
@@ -20,13 +20,12 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.Toast
-import android.widget.Toast.LENGTH_SHORT
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
+import net.taler.common.EventObserver
import net.taler.common.fadeIn
import net.taler.common.fadeOut
import net.taler.lib.common.Amount
@@ -35,6 +34,7 @@ import net.taler.wallet.R
import net.taler.wallet.cleanExchange
import net.taler.wallet.databinding.FragmentPromptWithdrawBinding
import net.taler.wallet.withdraw.WithdrawStatus.Loading
+import net.taler.wallet.withdraw.WithdrawStatus.ReceivedDetails
import net.taler.wallet.withdraw.WithdrawStatus.TosReviewRequired
import net.taler.wallet.withdraw.WithdrawStatus.Withdrawing
@@ -46,8 +46,9 @@ class PromptWithdrawFragment : Fragment() {
private lateinit var ui: FragmentPromptWithdrawBinding
override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
): View? {
ui = FragmentPromptWithdrawBinding.inflate(inflater, container, false)
return ui.root
@@ -59,21 +60,26 @@ class PromptWithdrawFragment : Fragment() {
withdrawManager.withdrawStatus.observe(viewLifecycleOwner, {
showWithdrawStatus(it)
})
+ withdrawManager.exchangeSelection.observe(viewLifecycleOwner,
EventObserver {
+
findNavController().navigate(R.id.action_promptWithdraw_to_selectExchangeFragment)
+ })
}
private fun showWithdrawStatus(status: WithdrawStatus?): Any = when
(status) {
- is WithdrawStatus.ReceivedDetails -> {
- showContent(status.amountRaw, status.amountEffective,
status.exchangeBaseUrl)
- ui.confirmWithdrawButton.apply {
- text = getString(R.string.withdraw_button_confirm)
- setOnClickListener {
- it.fadeOut()
- ui.confirmProgressBar.fadeIn()
- withdrawManager.acceptWithdrawal()
- }
- isEnabled = true
+ null -> model.showProgressBar.value = false
+ is Loading -> model.showProgressBar.value = true
+ is WithdrawStatus.NeedsExchange -> {
+ model.showProgressBar.value = false
+ val exchangeSelection = status.exchangeSelection.getIfNotConsumed()
+ if (exchangeSelection == null) { // already consumed
+ findNavController().popBackStack()
+ } else {
+ withdrawManager.selectExchange(exchangeSelection)
}
}
+ is TosReviewRequired -> onTosReviewRequired(status)
+ is ReceivedDetails -> onReceivedDetails(status)
+ is Withdrawing -> model.showProgressBar.value = true
is WithdrawStatus.Success -> {
model.showProgressBar.value = false
withdrawManager.withdrawStatus.value = null
@@ -81,14 +87,18 @@ class PromptWithdrawFragment : Fragment() {
model.showTransactions(status.currency)
Snackbar.make(requireView(), R.string.withdraw_initiated,
LENGTH_LONG).show()
}
- is Loading -> {
- model.showProgressBar.value = true
- }
- is Withdrawing -> {
- model.showProgressBar.value = true
+ is WithdrawStatus.Error -> {
+ model.showProgressBar.value = false
+
findNavController().navigate(R.id.action_promptWithdraw_to_errorFragment)
}
- is TosReviewRequired -> {
- showContent(status.amountRaw, status.amountEffective,
status.exchangeBaseUrl)
+ }
+
+ private fun onTosReviewRequired(s: TosReviewRequired) {
+ model.showProgressBar.value = false
+ if (s.showImmediately.getIfNotConsumed() == true) {
+
findNavController().navigate(R.id.action_promptWithdraw_to_reviewExchangeTOS)
+ } else {
+ showContent(s.amountRaw, s.amountEffective, s.exchangeBaseUrl,
s.talerWithdrawUri)
ui.confirmWithdrawButton.apply {
text = getString(R.string.withdraw_button_tos)
setOnClickListener {
@@ -97,14 +107,27 @@ class PromptWithdrawFragment : Fragment() {
isEnabled = true
}
}
- is WithdrawStatus.Error -> {
- model.showProgressBar.value = false
-
findNavController().navigate(R.id.action_promptWithdraw_to_errorFragment)
+ }
+
+ private fun onReceivedDetails(s: ReceivedDetails) {
+ showContent(s.amountRaw, s.amountEffective, s.exchangeBaseUrl,
s.talerWithdrawUri)
+ ui.confirmWithdrawButton.apply {
+ text = getString(R.string.withdraw_button_confirm)
+ setOnClickListener {
+ it.fadeOut()
+ ui.confirmProgressBar.fadeIn()
+ withdrawManager.acceptWithdrawal()
+ }
+ isEnabled = true
}
- null -> model.showProgressBar.value = false
}
- private fun showContent(amountRaw: Amount, amountEffective: Amount,
exchange: String) {
+ private fun showContent(
+ amountRaw: Amount,
+ amountEffective: Amount,
+ exchange: String,
+ uri: String?,
+ ) {
model.showProgressBar.value = false
ui.progressBar.fadeOut()
@@ -117,15 +140,20 @@ class PromptWithdrawFragment : Fragment() {
ui.chosenAmountView.fadeIn()
ui.feeLabel.fadeIn()
- ui.feeView.text = getString(R.string.amount_negative, (amountRaw -
amountEffective).toString())
+ ui.feeView.text =
+ getString(R.string.amount_negative, (amountRaw -
amountEffective).toString())
ui.feeView.fadeIn()
ui.exchangeIntroView.fadeIn()
ui.withdrawExchangeUrl.text = cleanExchange(exchange)
ui.withdrawExchangeUrl.fadeIn()
- ui.selectExchangeButton.fadeIn()
- ui.selectExchangeButton.setOnClickListener {
- Toast.makeText(context, "Not yet implemented", LENGTH_SHORT).show()
+
+ if (uri != null) { // no Uri for manual withdrawals
+ ui.selectExchangeButton.fadeIn()
+ ui.selectExchangeButton.setOnClickListener {
+ val exchangeSelection = ExchangeSelection(amountRaw, uri)
+ withdrawManager.selectExchange(exchangeSelection)
+ }
}
ui.withdrawCard.fadeIn()
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
index 25c5b72..5e11c04 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
@@ -18,10 +18,13 @@ package net.taler.wallet.withdraw
import android.util.Log
import androidx.annotation.UiThread
+import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
+import net.taler.common.Event
+import net.taler.common.toEvent
import net.taler.lib.common.Amount
import net.taler.wallet.TAG
import net.taler.wallet.backend.TalerErrorInfo
@@ -32,20 +35,23 @@ import
net.taler.wallet.withdraw.WithdrawStatus.ReceivedDetails
sealed class WithdrawStatus {
data class Loading(val talerWithdrawUri: String? = null) : WithdrawStatus()
+ data class NeedsExchange(val exchangeSelection: Event<ExchangeSelection>)
: WithdrawStatus()
+
data class TosReviewRequired(
val talerWithdrawUri: String? = null,
val exchangeBaseUrl: String,
val amountRaw: Amount,
val amountEffective: Amount,
val tosText: String,
- val tosEtag: String
+ val tosEtag: String,
+ val showImmediately: Event<Boolean>,
) : WithdrawStatus()
data class ReceivedDetails(
val talerWithdrawUri: String? = null,
val exchangeBaseUrl: String,
val amountRaw: Amount,
- val amountEffective: Amount
+ val amountEffective: Amount,
) : WithdrawStatus()
object Withdrawing : WithdrawStatus()
@@ -57,24 +63,31 @@ sealed class WithdrawStatus {
data class WithdrawalDetailsForUri(
val amount: Amount,
val defaultExchangeBaseUrl: String?,
- val possibleExchanges: List<ExchangeItem>
+ val possibleExchanges: List<ExchangeItem>,
)
@Serializable
data class WithdrawalDetails(
val tosAccepted: Boolean,
val amountRaw: Amount,
- val amountEffective: Amount
+ val amountEffective: Amount,
+)
+
+data class ExchangeSelection(
+ val amount: Amount,
+ val talerWithdrawUri: String,
)
class WithdrawManager(
private val api: WalletBackendApi,
- private val scope: CoroutineScope
+ private val scope: CoroutineScope,
) {
val withdrawStatus = MutableLiveData<WithdrawStatus>()
val testWithdrawalInProgress = MutableLiveData(false)
+ private val _exchangeSelection =
MutableLiveData<Event<ExchangeSelection>>()
+ val exchangeSelection: LiveData<Event<ExchangeSelection>> =
_exchangeSelection
var exchangeFees: ExchangeFees? = null
private set
@@ -87,6 +100,11 @@ class WithdrawManager(
}
}
+ @UiThread
+ fun selectExchange(selection: ExchangeSelection) {
+ _exchangeSelection.value = selection.toEvent()
+ }
+
fun getWithdrawalDetails(uri: String) = scope.launch {
withdrawStatus.value = WithdrawStatus.Loading(uri)
api.request("getWithdrawalDetailsForUri",
WithdrawalDetailsForUri.serializer()) {
@@ -95,11 +113,10 @@ class WithdrawManager(
handleError("getWithdrawalDetailsForUri", error)
}.onSuccess { details ->
if (details.defaultExchangeBaseUrl == null) {
- // TODO go to exchange selection screen instead
- val chosenExchange =
details.possibleExchanges[0].exchangeBaseUrl
- getWithdrawalDetails(chosenExchange, details.amount, uri)
+ val exchangeSelection = ExchangeSelection(details.amount, uri)
+ withdrawStatus.value =
WithdrawStatus.NeedsExchange(exchangeSelection.toEvent())
} else {
- getWithdrawalDetails(details.defaultExchangeBaseUrl,
details.amount, uri)
+ getWithdrawalDetails(details.defaultExchangeBaseUrl,
details.amount, false, uri)
}
}
}
@@ -107,7 +124,8 @@ class WithdrawManager(
fun getWithdrawalDetails(
exchangeBaseUrl: String,
amount: Amount,
- uri: String? = null
+ showTosImmediately: Boolean = false,
+ uri: String? = null,
) = scope.launch {
withdrawStatus.value = WithdrawStatus.Loading(uri)
api.request("getWithdrawalDetailsForAmount",
WithdrawalDetails.serializer()) {
@@ -121,16 +139,17 @@ class WithdrawManager(
talerWithdrawUri = uri,
exchangeBaseUrl = exchangeBaseUrl,
amountRaw = details.amountRaw,
- amountEffective = details.amountEffective
+ amountEffective = details.amountEffective,
)
- } else getExchangeTos(exchangeBaseUrl, details, uri)
+ } else getExchangeTos(exchangeBaseUrl, details,
showTosImmediately, uri)
}
}
private fun getExchangeTos(
exchangeBaseUrl: String,
details: WithdrawalDetails,
- uri: String?
+ showImmediately: Boolean,
+ uri: String?,
) = scope.launch {
api.request("getExchangeTos", TosResponse.serializer()) {
put("exchangeBaseUrl", exchangeBaseUrl)
@@ -143,7 +162,8 @@ class WithdrawManager(
amountRaw = details.amountRaw,
amountEffective = details.amountEffective,
tosText = it.tos,
- tosEtag = it.currentEtag
+ tosEtag = it.currentEtag,
+ showImmediately = showImmediately.toEvent(),
)
}
}
@@ -163,7 +183,7 @@ class WithdrawManager(
talerWithdrawUri = s.talerWithdrawUri,
exchangeBaseUrl = s.exchangeBaseUrl,
amountRaw = s.amountRaw,
- amountEffective = s.amountEffective
+ amountEffective = s.amountEffective,
)
}
}
@@ -181,7 +201,7 @@ class WithdrawManager(
api.request<Unit>(operation) {
put("exchangeBaseUrl", status.exchangeBaseUrl)
if (status.talerWithdrawUri == null) {
- put("amount", status.amountRaw)
+ put("amount", status.amountRaw.toJSONString())
} else {
put("talerWithdrawUri", status.talerWithdrawUri)
}
diff --git a/wallet/src/main/res/layout/fragment_select_exchange.xml
b/wallet/src/main/res/layout/fragment_exchange_fees.xml
similarity index 100%
rename from wallet/src/main/res/layout/fragment_select_exchange.xml
rename to wallet/src/main/res/layout/fragment_exchange_fees.xml
diff --git a/wallet/src/main/res/layout/fragment_prompt_withdraw.xml
b/wallet/src/main/res/layout/fragment_prompt_withdraw.xml
index 6bca4ef..421911a 100644
--- a/wallet/src/main/res/layout/fragment_prompt_withdraw.xml
+++ b/wallet/src/main/res/layout/fragment_prompt_withdraw.xml
@@ -169,7 +169,7 @@
android:contentDescription="@string/nav_exchange_fees"
android:src="@drawable/ic_edit"
android:tint="?attr/colorOnPrimary"
- android:visibility="invisible"
+ android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/withdrawExchangeUrl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/withdrawExchangeUrl"
diff --git a/wallet/src/main/res/navigation/nav_graph.xml
b/wallet/src/main/res/navigation/nav_graph.xml
index d8ce5b2..cf98b95 100644
--- a/wallet/src/main/res/navigation/nav_graph.xml
+++ b/wallet/src/main/res/navigation/nav_graph.xml
@@ -152,8 +152,8 @@
<fragment
android:id="@+id/selectExchangeFragment"
android:name="net.taler.wallet.exchanges.SelectExchangeFragment"
- android:label="@string/nav_exchange_fees"
- tools:layout="@layout/fragment_select_exchange" />
+ android:label="@string/nav_exchange_select"
+ tools:layout="@layout/fragment_exchange_list" />
<fragment
android:id="@+id/nav_pending_operations"
diff --git a/wallet/src/main/res/values/strings.xml
b/wallet/src/main/res/values/strings.xml
index 24db2b0..2e32c88 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -39,6 +39,7 @@ GNU Taler is immune against many types of fraud, such as
phishing of credit card
<string name="nav_prompt_withdraw">Withdraw Digital Cash</string>
<string name="nav_exchange_tos">Exchange\'s Terms of Service</string>
+ <string name="nav_exchange_select">Select Exchange</string>
<string name="nav_exchange_fees">Exchange Fees</string>
<string name="nav_error">Error</string>
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.