gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-android] branch master updated (9400d76 -> 64cd56e)


From: gnunet
Subject: [taler-taler-android] branch master updated (9400d76 -> 64cd56e)
Date: Fri, 17 Jul 2020 21:55:37 +0200

This is an automated email from the git hooks/post-receive script.

torsten-grote pushed a change to branch master
in repository taler-android.

    from 9400d76  [wallet] Try to connect to open Wi-Fi
     new 1c8da5a  [wallet] show list of known exchanges in settings
     new aa4472c  [wallet] Allow to add an exchange manually by providing its 
base URL
     new 4f665e6  [wallet] add UI for making manual withdrawal via exchange
     new 64cd56e  [wallet] adapt code to new TransactionWithdrawal API

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .idea/dictionaries/user.xml                        |   1 +
 build.gradle                                       |   4 +-
 .../src/main/java/net/taler/common/Amount.kt       |   1 +
 .../src/main/java/net/taler/common/AndroidUtils.kt |   7 ++
 wallet/build.gradle                                |   4 +-
 .../src/main/java/net/taler/wallet/MainActivity.kt |   4 +-
 .../main/java/net/taler/wallet/MainViewModel.kt    |   2 +
 .../wallet/exchanges/AddExchangeDialogFragment.kt  |  49 +++++++++
 .../net/taler/wallet/exchanges/ExchangeAdapter.kt  |  93 ++++++++++++++++
 .../ExchangeListFragment.kt}                       |  70 ++++++------
 .../net/taler/wallet/exchanges/ExchangeManager.kt  |  91 ++++++++++++++++
 .../ManualWithdrawFragment.kt}                     |  47 ++++----
 .../net/taler/wallet/settings/SettingsFragment.kt  |   2 +-
 .../transactions/TransactionDetailFragment.kt      |   5 +-
 .../wallet/transactions/TransactionManager.kt      |   4 +-
 .../net/taler/wallet/transactions/Transactions.kt  |  40 ++++++-
 ...{ic_account_balance.xml => ic_baseline_add.xml} |   8 +-
 ...h_usd_outline.xml => ic_baseline_more_vert.xml} |   4 +-
 .../src/main/res/layout/dialog_exchange_add.xml    |  24 ++---
 ...transactions.xml => fragment_exchange_list.xml} |  18 +++-
 wallet/src/main/res/layout/fragment_main.xml       |   9 +-
 .../main/res/layout/fragment_manual_withdraw.xml   | 118 +++++++++++++++++++++
 ...ist_item_history.xml => list_item_exchange.xml} |  59 ++++-------
 .../menu/{transactions_detail.xml => exchange.xml} |   6 +-
 wallet/src/main/res/navigation/nav_graph.xml       |  24 ++++-
 wallet/src/main/res/values/strings.xml             |  17 +++
 wallet/src/main/res/values/styles.xml              |  11 ++
 wallet/src/main/res/xml/settings_main.xml          |   7 ++
 28 files changed, 587 insertions(+), 142 deletions(-)
 create mode 100644 
wallet/src/main/java/net/taler/wallet/exchanges/AddExchangeDialogFragment.kt
 create mode 100644 
wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt
 copy wallet/src/main/java/net/taler/wallet/{history/DevHistoryFragment.kt => 
exchanges/ExchangeListFragment.kt} (51%)
 create mode 100644 
wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt
 copy wallet/src/main/java/net/taler/wallet/{withdraw/ErrorFragment.kt => 
exchanges/ManualWithdrawFragment.kt} (52%)
 copy wallet/src/main/res/drawable/{ic_account_balance.xml => 
ic_baseline_add.xml} (79%)
 copy wallet/src/main/res/drawable/{ic_cash_usd_outline.xml => 
ic_baseline_more_vert.xml} (77%)
 copy anastasis-ui/src/main/res/layout/fragment_sms.xml => 
wallet/src/main/res/layout/dialog_exchange_add.xml (74%)
 copy wallet/src/main/res/layout/{fragment_transactions.xml => 
fragment_exchange_list.xml} (71%)
 create mode 100644 wallet/src/main/res/layout/fragment_manual_withdraw.xml
 copy wallet/src/main/res/layout/{list_item_history.xml => 
list_item_exchange.xml} (59%)
 copy wallet/src/main/res/menu/{transactions_detail.xml => exchange.xml} (86%)

diff --git a/.idea/dictionaries/user.xml b/.idea/dictionaries/user.xml
index 1bcab63..0028bad 100644
--- a/.idea/dictionaries/user.xml
+++ b/.idea/dictionaries/user.xml
@@ -7,6 +7,7 @@
       <w>anastasis</w>
       <w>apdu</w>
       <w>markwon</w>
+      <w>payto</w>
       <w>servicedesc</w>
       <w>snackbar</w>
       <w>ssid</w>
diff --git a/build.gradle b/build.gradle
index 4f08021..3067d22 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
 buildscript {
     ext.kotlin_version = '1.3.72'
-    ext.nav_version = "2.2.2"
+    ext.nav_version = "2.3.0"
     ext.lifecycle_version = "2.2.0"
     // check https://android-rebuilds.beuc.net/ for availability of free build 
tools
     ext.build_tools_version = "29.0.2"
@@ -9,7 +9,7 @@ buildscript {
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:4.0.0'
+        classpath 'com.android.tools.build:gradle:4.0.1'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         classpath 
"androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
     }
diff --git a/taler-kotlin-common/src/main/java/net/taler/common/Amount.kt 
b/taler-kotlin-common/src/main/java/net/taler/common/Amount.kt
index bd12a40..76cd294 100644
--- a/taler-kotlin-common/src/main/java/net/taler/common/Amount.kt
+++ b/taler-kotlin-common/src/main/java/net/taler/common/Amount.kt
@@ -159,6 +159,7 @@ data class Amount(
 
     @Throws(AmountOverflowException::class)
     operator fun times(factor: Int): Amount {
+        if (factor == 0) return zero(currency)
         var result = this
         for (i in 1 until factor) result += this
         return result
diff --git a/taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt 
b/taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt
index fda537b..ba6ee1c 100644
--- a/taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt
+++ b/taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt
@@ -38,6 +38,8 @@ import android.text.format.DateUtils.getRelativeTimeSpanString
 import android.view.View
 import android.view.View.INVISIBLE
 import android.view.View.VISIBLE
+import android.view.inputmethod.InputMethodManager
+import androidx.core.content.ContextCompat.getSystemService
 import androidx.fragment.app.Fragment
 import androidx.navigation.NavDirections
 import androidx.navigation.fragment.findNavController
@@ -61,6 +63,11 @@ fun View.fadeOut(endAction: () -> Unit = {}) {
     }.start()
 }
 
+fun View.hideKeyboard() {
+    getSystemService(context, InputMethodManager::class.java)
+        ?.hideSoftInputFromWindow(windowToken, 0)
+}
+
 fun assertUiThread() {
     check(Looper.getMainLooper().thread == Thread.currentThread())
 }
diff --git a/wallet/build.gradle b/wallet/build.gradle
index 192b454..d93b8b9 100644
--- a/wallet/build.gradle
+++ b/wallet/build.gradle
@@ -23,7 +23,7 @@ plugins {
     id "de.undercouch.download"
 }
 
-def walletCoreVersion = "v0.7.1-dev.6"
+def walletCoreVersion = "v0.7.1-dev.10"
 
 android {
     compileSdkVersion 29
@@ -35,7 +35,7 @@ android {
         minSdkVersion 24
         targetSdkVersion 29
         versionCode 6
-        versionName "0.7.1.dev.6"
+        versionName "0.7.1.dev.10"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         buildConfigField "String", "WALLET_CORE_VERSION", 
"\"$walletCoreVersion\""
     }
diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt 
b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
index 786e40e..fdb8cf8 100644
--- a/wallet/src/main/java/net/taler/wallet/MainActivity.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
@@ -158,7 +158,8 @@ class MainActivity : AppCompatActivity(), 
OnNavigationItemSelectedListener,
             }
             url.toLowerCase(ROOT).startsWith("taler://withdraw/") -> {
                 Log.v(TAG, "navigating!")
-                nav.navigate(R.id.action_nav_main_to_promptWithdraw)
+                // there's more than one entry point, so use global action
+                nav.navigate(R.id.action_global_promptWithdraw)
                 model.withdrawManager.getWithdrawalInfo(url)
             }
             url.toLowerCase(ROOT).startsWith("taler://refund/") -> {
@@ -222,6 +223,7 @@ class MainActivity : AppCompatActivity(), 
OnNavigationItemSelectedListener,
     ): Boolean {
         when (pref.key) {
             "pref_backup" -> 
nav.navigate(R.id.action_nav_settings_to_nav_settings_backup)
+            "pref_exchanges" -> 
nav.navigate(R.id.action_nav_settings_to_nav_settings_exchanges)
         }
         return true
     }
diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt 
b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
index c69c31c..46f5021 100644
--- a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
@@ -33,6 +33,7 @@ import net.taler.common.assertUiThread
 import net.taler.common.toEvent
 import net.taler.wallet.backend.WalletBackendApi
 import net.taler.wallet.balances.BalanceItem
+import net.taler.wallet.exchanges.ExchangeManager
 import net.taler.wallet.history.DevHistoryManager
 import net.taler.wallet.payment.PaymentManager
 import net.taler.wallet.pending.PendingOperationsManager
@@ -102,6 +103,7 @@ class MainViewModel(val app: Application) : 
AndroidViewModel(app) {
     val transactionManager: TransactionManager =
         TransactionManager(walletBackendApi, viewModelScope, mapper)
     val refundManager = RefundManager(walletBackendApi)
+    val exchangeManager: ExchangeManager = ExchangeManager(walletBackendApi, 
mapper)
 
     private val mTransactionsEvent = MutableLiveData<Event<String>>()
     val transactionsEvent: LiveData<Event<String>> = mTransactionsEvent
diff --git 
a/wallet/src/main/java/net/taler/wallet/exchanges/AddExchangeDialogFragment.kt 
b/wallet/src/main/java/net/taler/wallet/exchanges/AddExchangeDialogFragment.kt
new file mode 100644
index 0000000..5ea763a
--- /dev/null
+++ 
b/wallet/src/main/java/net/taler/wallet/exchanges/AddExchangeDialogFragment.kt
@@ -0,0 +1,49 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under 
the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+ * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.wallet.exchanges
+
+import android.app.Dialog
+import android.os.Bundle
+import android.widget.TextView
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.activityViewModels
+import net.taler.wallet.MainViewModel
+import net.taler.wallet.R
+
+
+class AddExchangeDialogFragment : DialogFragment() {
+
+    private val model: MainViewModel by activityViewModels()
+    private val exchangeManager by lazy { model.exchangeManager }
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        return AlertDialog.Builder(requireContext(), R.style.DialogTheme)
+            .setIcon(R.drawable.ic_account_balance)
+            .setTitle(R.string.exchange_list_add)
+            .setView(R.layout.dialog_exchange_add)
+            .setPositiveButton(R.string.ok) { dialog, _ ->
+                val urlView: TextView = (dialog as 
AlertDialog).findViewById(R.id.urlView)!!
+                exchangeManager.add(urlView.text.toString())
+            }
+            .setNegativeButton(R.string.cancel) { _, _ ->
+                dismiss()
+            }
+            .create()
+    }
+
+}
diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt 
b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt
new file mode 100644
index 0000000..189f444
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt
@@ -0,0 +1,93 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under 
the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+ * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.wallet.exchanges
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageButton
+import android.widget.TextView
+import androidx.appcompat.widget.PopupMenu
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.Adapter
+import net.taler.wallet.R
+import net.taler.wallet.cleanExchange
+import net.taler.wallet.exchanges.ExchangeAdapter.ExchangeItemViewHolder
+
+data class ExchangeItem(
+    val exchangeBaseUrl: String,
+    val currency: String,
+    val paytoUris: List<String>
+) {
+    val name: String get() = cleanExchange(exchangeBaseUrl)
+}
+
+interface ExchangeClickListener {
+    fun onManualWithdraw(item: ExchangeItem)
+}
+
+internal class ExchangeAdapter(private val listener: ExchangeClickListener) :
+    Adapter<ExchangeItemViewHolder>() {
+
+    private val items = ArrayList<ExchangeItem>()
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): 
ExchangeItemViewHolder {
+        val view = LayoutInflater.from(parent.context)
+            .inflate(R.layout.list_item_exchange, parent, false)
+        return ExchangeItemViewHolder(view)
+    }
+
+    override fun getItemCount() = items.size
+
+    override fun onBindViewHolder(holder: ExchangeItemViewHolder, position: 
Int) {
+        holder.bind(items[position])
+    }
+
+    fun update(newItems: List<ExchangeItem>) {
+        items.clear()
+        items.addAll(newItems)
+        notifyDataSetChanged()
+    }
+
+    internal inner class ExchangeItemViewHolder(v: View) : 
RecyclerView.ViewHolder(v) {
+        private val context = v.context
+        private val urlView: TextView = v.findViewById(R.id.urlView)
+        private val currencyView: TextView = v.findViewById(R.id.currencyView)
+        private val overflowIcon: ImageButton = 
v.findViewById(R.id.overflowIcon)
+
+        fun bind(item: ExchangeItem) {
+            urlView.text = item.name
+            currencyView.text = 
context.getString(R.string.exchange_list_currency, item.currency)
+            overflowIcon.setOnClickListener { openMenu(overflowIcon, item) }
+        }
+
+        private fun openMenu(anchor: View, item: ExchangeItem) = 
PopupMenu(context, anchor).apply {
+            inflate(R.menu.exchange)
+            setOnMenuItemClickListener { menuItem ->
+                when (menuItem.itemId) {
+                    R.id.action_manual_withdrawal -> {
+                        listener.onManualWithdraw(item)
+                        true
+                    }
+                    else -> false
+                }
+            }
+            show()
+        }
+    }
+
+}
diff --git 
a/wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt 
b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt
similarity index 51%
copy from wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt
copy to wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt
index c3c07a3..c7da205 100644
--- a/wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt
@@ -14,74 +14,78 @@
  * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-package net.taler.wallet.history
+package net.taler.wallet.exchanges
 
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
-import android.view.View.INVISIBLE
-import android.view.View.VISIBLE
 import android.view.ViewGroup
+import android.widget.Toast
+import android.widget.Toast.LENGTH_LONG
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
 import androidx.lifecycle.Observer
+import androidx.navigation.fragment.findNavController
 import androidx.recyclerview.widget.DividerItemDecoration
 import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL
-import kotlinx.android.synthetic.main.fragment_transactions.*
+import kotlinx.android.synthetic.main.fragment_exchange_list.*
+import net.taler.common.EventObserver
 import net.taler.common.fadeIn
 import net.taler.common.fadeOut
 import net.taler.wallet.MainViewModel
 import net.taler.wallet.R
 
-internal interface OnEventClickListener {
-    fun onTransactionClicked(historyEvent: HistoryEvent)
-}
-
-class DevHistoryFragment : Fragment(),
-    OnEventClickListener {
+class ExchangeListFragment : Fragment(), ExchangeClickListener {
 
     private val model: MainViewModel by activityViewModels()
-    private val historyManager by lazy { model.historyManager }
-    private val historyAdapter by lazy { DevHistoryAdapter(this) }
+    private val exchangeManager by lazy { model.exchangeManager }
+    private val exchangeAdapter by lazy { ExchangeAdapter(this) }
 
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
     ): View? {
-        return inflater.inflate(R.layout.fragment_transactions, container, 
false)
+        return inflater.inflate(R.layout.fragment_exchange_list, container, 
false)
     }
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        if (savedInstanceState == null) historyManager.loadHistory()
-
         list.apply {
-            adapter = historyAdapter
+            adapter = exchangeAdapter
             addItemDecoration(DividerItemDecoration(context, VERTICAL))
         }
-        historyManager.progress.observe(viewLifecycleOwner, Observer { show ->
-            progressBar.visibility = if (show) VISIBLE else INVISIBLE
+        addExchangeFab.setOnClickListener {
+            AddExchangeDialogFragment().show(parentFragmentManager, 
"ADD_EXCHANGE")
+        }
+
+        exchangeManager.progress.observe(viewLifecycleOwner, Observer { show ->
+            if (show) progressBar.fadeIn() else progressBar.fadeOut()
         })
-        historyManager.history.observe(viewLifecycleOwner, Observer { result ->
-            onHistoryResult(result)
+        exchangeManager.exchanges.observe(viewLifecycleOwner, Observer { 
exchanges ->
+            onExchangeUpdate(exchanges)
+        })
+        exchangeManager.addError.observe(viewLifecycleOwner, EventObserver { 
error ->
+            if (error) onAddExchangeFailed()
         })
     }
 
-    override fun onTransactionClicked(historyEvent: HistoryEvent) {
-        JsonDialogFragment.new(historyEvent.json.toString(2))
-            .show(parentFragmentManager, null)
-    }
-
-    private fun onHistoryResult(result: HistoryResult) = when (result) {
-        HistoryResult.Error -> {
-            list.fadeOut()
-            emptyState.text = getString(R.string.transactions_error)
+    private fun onExchangeUpdate(exchanges: List<ExchangeItem>) {
+        exchangeAdapter.update(exchanges)
+        if (exchanges.isEmpty()) {
             emptyState.fadeIn()
-        }
-        is HistoryResult.Success -> {
-            emptyState.visibility = if (result.history.isEmpty()) VISIBLE else 
INVISIBLE
-            historyAdapter.update(result.history)
+            list.fadeOut()
+        } else {
+            emptyState.fadeOut()
             list.fadeIn()
         }
     }
 
+    private fun onAddExchangeFailed() {
+        Toast.makeText(requireContext(), R.string.exchange_add_error, 
LENGTH_LONG).show()
+    }
+
+    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/ExchangeManager.kt 
b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt
new file mode 100644
index 0000000..cdd5590
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt
@@ -0,0 +1,91 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under 
the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+ * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.wallet.exchanges
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.kotlin.readValue
+import net.taler.common.Amount
+import net.taler.common.Event
+import net.taler.common.toEvent
+import net.taler.wallet.TAG
+import net.taler.wallet.backend.WalletBackendApi
+import org.json.JSONObject
+
+class ExchangeManager(
+    private val walletBackendApi: WalletBackendApi,
+    private val mapper: ObjectMapper
+) {
+
+    private val mProgress = MutableLiveData<Boolean>()
+    val progress: LiveData<Boolean> = mProgress
+
+    private val mExchanges = MutableLiveData<List<ExchangeItem>>()
+    val exchanges: LiveData<List<ExchangeItem>> get() = list()
+
+    private val mAddError = MutableLiveData<Event<Boolean>>()
+    val addError: LiveData<Event<Boolean>> = mAddError
+
+    var withdrawalExchange: ExchangeItem? = null
+
+    private fun list(): LiveData<List<ExchangeItem>> {
+        mProgress.value = true
+        walletBackendApi.sendRequest("listExchanges", JSONObject()) { isError, 
result ->
+            if (isError) {
+                throw AssertionError("Wallet core failed to return exchanges!")
+            } else {
+                val exchanges: List<ExchangeItem> = 
mapper.readValue(result.getString("exchanges"))
+                Log.d(TAG, "Exchange list: $exchanges")
+                mProgress.value = false
+                mExchanges.value = exchanges
+            }
+        }
+        return mExchanges
+    }
+
+    fun add(exchangeUrl: String) {
+        mProgress.value = true
+        val args = JSONObject().apply { put("exchangeBaseUrl", exchangeUrl) }
+        walletBackendApi.sendRequest("addExchange", args) { isError, result ->
+            mProgress.value = false
+            if (isError) {
+                Log.e(TAG, "$result")
+                mAddError.value = true.toEvent()
+            } else {
+                Log.d(TAG, "Exchange $exchangeUrl added")
+                list()
+            }
+        }
+    }
+
+    fun getWithdrawalDetails(exchangeItem: ExchangeItem, amount: Amount) {
+        val args = JSONObject().apply {
+            put("exchangeBaseUrl", exchangeItem.exchangeBaseUrl)
+            put("amount", amount.toJSONString())
+        }
+        walletBackendApi.sendRequest("getWithdrawalDetailsForAmount", args) { 
isError, result ->
+            if (isError) {
+                Log.e(TAG, "$result")
+            } else {
+                Log.e(TAG, "$result")
+            }
+        }
+    }
+
+}
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/ErrorFragment.kt 
b/wallet/src/main/java/net/taler/wallet/exchanges/ManualWithdrawFragment.kt
similarity index 52%
copy from wallet/src/main/java/net/taler/wallet/withdraw/ErrorFragment.kt
copy to 
wallet/src/main/java/net/taler/wallet/exchanges/ManualWithdrawFragment.kt
index fa5ab2f..c3f201d 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/ErrorFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ManualWithdrawFragment.kt
@@ -14,50 +14,47 @@
  * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-package net.taler.wallet.withdraw
+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.View.VISIBLE
 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 kotlinx.android.synthetic.main.fragment_error.*
-import net.taler.wallet.R
+import kotlinx.android.synthetic.main.fragment_manual_withdraw.*
+import net.taler.common.Amount
+import net.taler.common.hideKeyboard
 import net.taler.wallet.MainViewModel
+import net.taler.wallet.R
+import net.taler.wallet.scanQrCode
 
-class ErrorFragment : Fragment() {
+class ManualWithdrawFragment : Fragment() {
 
     private val model: MainViewModel by activityViewModels()
-    private val withdrawManager by lazy { model.withdrawManager }
+    private val exchangeManager by lazy { model.exchangeManager }
+    private val exchangeItem by lazy { 
requireNotNull(exchangeManager.withdrawalExchange) }
 
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
     ): View? {
-        return inflater.inflate(R.layout.fragment_error, container, false)
+        return inflater.inflate(R.layout.fragment_manual_withdraw, container, 
false)
     }
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        super.onViewCreated(view, savedInstanceState)
-
-        errorTitle.setText(R.string.withdraw_error_title)
-        errorMessage.setText(R.string.withdraw_error_message)
-
-        // show dev error message if dev mode is on
-        val status = withdrawManager.withdrawStatus.value
-        if (model.devMode.value == true && status is WithdrawStatus.Error) {
-            errorDevMessage.visibility = VISIBLE
-            errorDevMessage.text = status.message
-        } else {
-            errorDevMessage.visibility = GONE
-        }
-
-        backButton.setOnClickListener {
-            findNavController().navigateUp()
+        qrCodeButton.setOnClickListener { scanQrCode(requireActivity()) }
+        currencyView.text = exchangeItem.currency
+        paymentOptionsLabel.text =
+            getString(R.string.withdraw_manual_payment_options, 
exchangeItem.name)
+        checkFeesButton.setOnClickListener {
+            val value = amountView.text.toString().toLong()
+            val amount = Amount(exchangeItem.currency, value, 0)
+            amountView.hideKeyboard()
+            Toast.makeText(view.context, "Not implemented: $amount", 
LENGTH_SHORT).show()
+            exchangeManager.getWithdrawalDetails(exchangeItem, amount)
         }
     }
 
diff --git a/wallet/src/main/java/net/taler/wallet/settings/SettingsFragment.kt 
b/wallet/src/main/java/net/taler/wallet/settings/SettingsFragment.kt
index 31295d6..d9b1def 100644
--- a/wallet/src/main/java/net/taler/wallet/settings/SettingsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/settings/SettingsFragment.kt
@@ -110,7 +110,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
     }
 
     private fun showResetDialog() {
-        AlertDialog.Builder(requireContext())
+        AlertDialog.Builder(requireContext(), R.style.DialogTheme)
             .setMessage("Do you really want to reset the wallet and lose all 
coins and purchases?")
             .setPositiveButton("Reset") { _, _ ->
                 model.dangerouslyReset()
diff --git 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
index f78d98f..0142978 100644
--- 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
+++ 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
@@ -41,6 +41,7 @@ import net.taler.common.toAbsoluteTime
 import net.taler.wallet.MainViewModel
 import net.taler.wallet.R
 import net.taler.wallet.cleanExchange
+import net.taler.wallet.transactions.WithdrawalDetails.TalerBankIntegrationApi
 
 class TransactionDetailFragment : Fragment() {
 
@@ -95,9 +96,9 @@ class TransactionDetailFragment : Fragment() {
     private fun bind(t: TransactionWithdrawal) {
         effectiveAmountLabel.text = getString(R.string.withdraw_total)
         effectiveAmountView.text = t.amountEffective.toString()
-        if (t.pending && !t.confirmed && t.bankConfirmationUrl != null) {
+        if (t.pending && t.withdrawalDetails is TalerBankIntegrationApi && 
!t.confirmed && t.withdrawalDetails.bankConfirmationUrl != null) {
             val i = Intent().apply {
-                data = Uri.parse(t.bankConfirmationUrl)
+                data = Uri.parse(t.withdrawalDetails.bankConfirmationUrl)
             }
             if (i.isSafe(requireContext())) {
                 confirmWithdrawalButton.setOnClickListener { startActivity(i) }
diff --git 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
index 882b29b..d8204b6 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
@@ -28,7 +28,8 @@ import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import net.taler.wallet.backend.WalletBackendApi
 import org.json.JSONObject
-import java.util.*
+import java.util.HashMap
+import java.util.LinkedList
 
 sealed class TransactionsResult {
     object Error : TransactionsResult()
@@ -72,6 +73,7 @@ class TransactionManager(
         walletBackendApi.sendRequest("getTransactions", request) { isError, 
result ->
             if (isError) {
                 liveData.postValue(TransactionsResult.Error)
+                mProgress.postValue(false)
             } else {
                 val currencyToUpdate = if (searchQuery == null) currency else 
null
                 scope.launch(Dispatchers.Default) {
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt 
b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
index 27384d8..ccc40eb 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
@@ -33,6 +33,8 @@ import net.taler.common.ContractProduct
 import net.taler.common.Timestamp
 import net.taler.wallet.R
 import net.taler.wallet.cleanExchange
+import net.taler.wallet.transactions.WithdrawalDetails.ManualTransfer
+import net.taler.wallet.transactions.WithdrawalDetails.TalerBankIntegrationApi
 
 @JsonTypeInfo(use = NAME, include = PROPERTY, property = "type")
 @JsonSubTypes(
@@ -80,8 +82,7 @@ class TransactionWithdrawal(
     timestamp: Timestamp,
     pending: Boolean,
     val exchangeBaseUrl: String,
-    val confirmed: Boolean,
-    val bankConfirmationUrl: String?,
+    val withdrawalDetails: WithdrawalDetails,
     error: TransactionError? = null,
     amountRaw: Amount,
     amountEffective: Amount
@@ -91,6 +92,41 @@ class TransactionWithdrawal(
     override val amountType = AmountType.Positive
     override fun getTitle(context: Context) = cleanExchange(exchangeBaseUrl)
     override val generalTitleRes = R.string.withdraw_title
+    val confirmed: Boolean =
+        withdrawalDetails is TalerBankIntegrationApi && 
withdrawalDetails.confirmed
+}
+
+@JsonTypeInfo(use = NAME, include = PROPERTY, property = "type")
+@JsonSubTypes(
+    Type(value = TalerBankIntegrationApi::class, name = 
"taler-bank-integration-api"),
+    Type(value = ManualTransfer::class, name = "manual-transfer")
+)
+sealed class WithdrawalDetails {
+    @JsonTypeName("manual-transfer")
+    class ManualTransfer(
+        /**
+         * Payto URIs that the exchange supports.
+         *
+         * Already contains the amount and message.
+         */
+        val exchangePaytoUris: List<String>
+    ) : WithdrawalDetails()
+
+    @JsonTypeName("taler-bank-integration-api")
+    class TalerBankIntegrationApi(
+        /**
+         * Set to true if the bank has confirmed the withdrawal, false if not.
+         * An unconfirmed withdrawal usually requires user-input
+         * and should be highlighted in the UI.
+         * See also bankConfirmationUrl below.
+         */
+        val confirmed: Boolean,
+
+        /**
+         * If the withdrawal is unconfirmed, this can include a URL for 
user-initiated confirmation.
+         */
+        val bankConfirmationUrl: String?
+    ) : WithdrawalDetails()
 }
 
 @JsonTypeName("payment")
diff --git a/wallet/src/main/res/drawable/ic_account_balance.xml 
b/wallet/src/main/res/drawable/ic_baseline_add.xml
similarity index 79%
copy from wallet/src/main/res/drawable/ic_account_balance.xml
copy to wallet/src/main/res/drawable/ic_baseline_add.xml
index 78836a0..d123476 100644
--- a/wallet/src/main/res/drawable/ic_account_balance.xml
+++ b/wallet/src/main/res/drawable/ic_baseline_add.xml
@@ -18,9 +18,9 @@
     android:width="24dp"
     android:height="24dp"
     android:tint="?attr/colorControlNormal"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+    android:viewportWidth="24"
+    android:viewportHeight="24">
     <path
-        android:fillColor="#FF000000"
-        
android:pathData="M4,10v7h3v-7L4,10zM10,10v7h3v-7h-3zM2,22h19v-3L2,19v3zM16,10v7h3v-7h-3zM11.5,1L2,6v2h19L21,6l-9.5,-5z"
 />
+        android:fillColor="@android:color/white"
+        android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
 </vector>
diff --git a/wallet/src/main/res/drawable/ic_cash_usd_outline.xml 
b/wallet/src/main/res/drawable/ic_baseline_more_vert.xml
similarity index 77%
copy from wallet/src/main/res/drawable/ic_cash_usd_outline.xml
copy to wallet/src/main/res/drawable/ic_baseline_more_vert.xml
index aa5b85f..b19e0f1 100644
--- a/wallet/src/main/res/drawable/ic_cash_usd_outline.xml
+++ b/wallet/src/main/res/drawable/ic_baseline_more_vert.xml
@@ -21,6 +21,6 @@
     android:viewportWidth="24"
     android:viewportHeight="24">
     <path
-        android:fillColor="#000"
-        android:pathData="M20,18H4V6H20M20,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 
4,20H20A2,2 0 0,0 22,18V6C22,4.89 21.1,4 20,4M11,17H13V16H14A1,1 0 0,0 
15,15V12A1,1 0 0,0 14,11H11V10H15V8H13V7H11V8H10A1,1 0 0,0 9,9V12A1,1 0 0,0 
10,13H13V14H9V16H11V17Z" />
+        android:fillColor="@android:color/white"
+        android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 
0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 
-2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
 </vector>
diff --git a/anastasis-ui/src/main/res/layout/fragment_sms.xml 
b/wallet/src/main/res/layout/dialog_exchange_add.xml
similarity index 74%
copy from anastasis-ui/src/main/res/layout/fragment_sms.xml
copy to wallet/src/main/res/layout/dialog_exchange_add.xml
index f855d38..dfa0f70 100644
--- a/anastasis-ui/src/main/res/layout/fragment_sms.xml
+++ b/wallet/src/main/res/layout/dialog_exchange_add.xml
@@ -16,36 +16,30 @@
 
 <androidx.constraintlayout.widget.ConstraintLayout 
xmlns:android="http://schemas.android.com/apk/res/android";
     xmlns:app="http://schemas.android.com/apk/res-auto";
+    xmlns:tools="http://schemas.android.com/tools";
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
     <com.google.android.material.textfield.TextInputLayout
-        android:id="@+id/smsView"
+        android:id="@+id/urlLayout"
         
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
-        android:layout_width="match_parent"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_margin="16dp"
+        android:hint="@string/exchange_add_url"
+        app:boxBackgroundMode="outline"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent">
 
         <com.google.android.material.textfield.TextInputEditText
+            android:id="@+id/urlView"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:hint="Phone Number"
-            android:inputType="phone" />
+            android:inputType="textUri"
+            android:text="https://";
+            tools:ignore="HardcodedText" />
 
     </com.google.android.material.textfield.TextInputLayout>
 
-    <Button
-        android:id="@+id/saveSmsButton"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_margin="16dp"
-        android:backgroundTint="@color/green"
-        android:drawableTint="?attr/colorOnPrimarySurface"
-        android:text="Save"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent" />
-
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/wallet/src/main/res/layout/fragment_transactions.xml 
b/wallet/src/main/res/layout/fragment_exchange_list.xml
similarity index 71%
copy from wallet/src/main/res/layout/fragment_transactions.xml
copy to wallet/src/main/res/layout/fragment_exchange_list.xml
index 547da24..29d88c7 100644
--- a/wallet/src/main/res/layout/fragment_transactions.xml
+++ b/wallet/src/main/res/layout/fragment_exchange_list.xml
@@ -14,7 +14,7 @@
   ~ GNU Taler; see the file COPYING.  If not, see 
<http://www.gnu.org/licenses/>
   -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android";
+<androidx.coordinatorlayout.widget.CoordinatorLayout 
xmlns:android="http://schemas.android.com/apk/res/android";
     xmlns:app="http://schemas.android.com/apk/res-auto";
     xmlns:tools="http://schemas.android.com/tools";
     android:layout_width="match_parent"
@@ -27,7 +27,7 @@
         android:scrollbars="vertical"
         android:visibility="invisible"
         app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
-        tools:listitem="@layout/list_item_history"
+        tools:listitem="@layout/list_item_exchange"
         tools:visibility="visible" />
 
     <TextView
@@ -35,7 +35,8 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:text="@string/transactions_empty"
+        android:gravity="center"
+        android:text="@string/exchange_list_empty"
         android:visibility="invisible"
         tools:visibility="visible" />
 
@@ -48,4 +49,13 @@
         android:visibility="invisible"
         tools:visibility="visible" />
 
-</FrameLayout>
+    <com.google.android.material.floatingactionbutton.FloatingActionButton
+        android:id="@+id/addExchangeFab"
+        style="@style/FabStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:contentDescription="@string/exchange_list_add"
+        android:src="@drawable/ic_baseline_add"
+        
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
 />
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/wallet/src/main/res/layout/fragment_main.xml 
b/wallet/src/main/res/layout/fragment_main.xml
index 81121b5..3f680ba 100644
--- a/wallet/src/main/res/layout/fragment_main.xml
+++ b/wallet/src/main/res/layout/fragment_main.xml
@@ -25,16 +25,11 @@
 
     <com.google.android.material.floatingactionbutton.FloatingActionButton
         android:id="@+id/mainFab"
+        style="@style/FabStyle"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="bottom|end"
-        android:layout_marginEnd="16dp"
-        android:layout_marginBottom="16dp"
         android:contentDescription="@string/button_scan_qr_code"
         android:src="@drawable/ic_scan_qr"
-        app:backgroundTint="@color/colorPrimary"
-        
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
-        app:layout_dodgeInsetEdges="bottom"
-        app:tint="?attr/colorOnPrimary" />
+        
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
 />
 
 </androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/wallet/src/main/res/layout/fragment_manual_withdraw.xml 
b/wallet/src/main/res/layout/fragment_manual_withdraw.xml
new file mode 100644
index 0000000..5b37d2a
--- /dev/null
+++ b/wallet/src/main/res/layout/fragment_manual_withdraw.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ This file is part of GNU Taler
+  ~ (C) 2020 Taler Systems S.A.
+  ~
+  ~ GNU Taler is free software; you can redistribute it and/or modify it under 
the
+  ~ terms of the GNU General Public License as published by the Free Software
+  ~ Foundation; either version 3, or (at your option) any later version.
+  ~
+  ~ GNU Taler is distributed in the hope that it will be useful, but WITHOUT 
ANY
+  ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+  ~ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License along 
with
+  ~ GNU Taler; see the file COPYING.  If not, see 
<http://www.gnu.org/licenses/>
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout 
xmlns:android="http://schemas.android.com/apk/res/android";
+    xmlns:app="http://schemas.android.com/apk/res-auto";
+    xmlns:tools="http://schemas.android.com/tools";
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <Button
+        android:id="@+id/qrCodeButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="32dp"
+        android:drawableLeft="@drawable/ic_scan_qr"
+        android:text="@string/button_scan_qr_code"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_chainStyle="packed"
+        tools:ignore="RtlHardcoded" />
+
+    <TextView
+        android:id="@+id/orView"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="32dp"
+        android:text="@string/or"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/qrCodeButton" />
+
+    <TextView
+        android:id="@+id/manualWithdrawIntro"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="32dp"
+        android:text="@string/withdraw_manual_title"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/orView" />
+
+    <com.google.android.material.textfield.TextInputLayout
+        android:id="@+id/amountLayout"
+        
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="16dp"
+        android:hint="@string/withdraw_amount"
+        app:boxBackgroundMode="outline"
+        app:endIconDrawable="@drawable/ic_cancel"
+        app:endIconMode="clear_text"
+        app:layout_constraintEnd_toStartOf="@+id/currencyView"
+        app:layout_constraintHorizontal_chainStyle="packed"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/manualWithdrawIntro">
+
+        <com.google.android.material.textfield.TextInputEditText
+            android:id="@+id/amountView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ems="10"
+            android:inputType="number" />
+
+    </com.google.android.material.textfield.TextInputLayout>
+
+    <TextView
+        android:id="@+id/currencyView"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintBottom_toBottomOf="@+id/amountLayout"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/amountLayout"
+        app:layout_constraintTop_toTopOf="@+id/amountLayout"
+        tools:text="TESTKUDOS123" />
+
+    <TextView
+        android:id="@+id/paymentOptionsLabel"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginTop="32dp"
+        android:layout_marginEnd="16dp"
+        android:text="@string/withdraw_manual_payment_options"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/amountLayout" />
+
+    <Button
+        android:id="@+id/checkFeesButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="32dp"
+        android:layout_marginEnd="16dp"
+        android:text="@string/withdraw_manual_check_fees"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/paymentOptionsLabel"
+        app:layout_constraintVertical_bias="0.0" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/wallet/src/main/res/layout/list_item_history.xml 
b/wallet/src/main/res/layout/list_item_exchange.xml
similarity index 59%
copy from wallet/src/main/res/layout/list_item_history.xml
copy to wallet/src/main/res/layout/list_item_exchange.xml
index bc94738..c9d1df4 100644
--- a/wallet/src/main/res/layout/list_item_history.xml
+++ b/wallet/src/main/res/layout/list_item_exchange.xml
@@ -21,55 +21,42 @@
     android:layout_height="wrap_content"
     android:background="@drawable/selectable_background"
     android:foreground="?attr/selectableItemBackground"
-    android:paddingStart="16dp"
-    android:paddingTop="8dp"
-    android:paddingEnd="16dp"
-    android:paddingBottom="8dp">
-
-    <ImageView
-        android:id="@+id/icon"
-        android:layout_width="32dp"
-        android:layout_height="32dp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:tint="?android:colorControlNormal"
-        tools:ignore="ContentDescription"
-        tools:src="@drawable/ic_cash_usd_outline" />
+    android:paddingTop="16dp"
+    android:paddingBottom="16dp">
 
     <TextView
-        android:id="@+id/title"
+        android:id="@+id/urlView"
         style="@style/TransactionTitle"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginStart="16dp"
-        android:layout_marginEnd="8dp"
-        app:layout_constraintEnd_toStartOf="@+id/amount"
-        app:layout_constraintStart_toEndOf="@+id/icon"
-        app:layout_constraintTop_toTopOf="parent"
-        tools:text="@string/payment_title" />
-
-    <TextView
-        android:id="@+id/amount"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="24sp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
+        android:textSize="18sp"
+        app:layout_constraintEnd_toStartOf="@+id/overflowIcon"
+        app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        tools:text="- 1337.23" />
+        tools:text="exchange.test.taler.net" />
 
     <TextView
-        android:id="@+id/time"
+        android:id="@+id/currencyView"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginTop="8dp"
-        android:layout_marginEnd="8dp"
         android:textSize="14sp"
+        app:layout_constraintEnd_toStartOf="@+id/overflowIcon"
+        app:layout_constraintStart_toStartOf="@+id/urlView"
+        app:layout_constraintTop_toBottomOf="@+id/urlView"
+        tools:text="@string/exchange_list_currency" />
+
+    <ImageButton
+        android:id="@+id/overflowIcon"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:contentDescription="@string/menu"
+        android:scaleType="centerInside"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@+id/amount"
-        app:layout_constraintStart_toStartOf="@+id/title"
-        app:layout_constraintTop_toBottomOf="@+id/title"
-        tools:text="23 min ago" />
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:srcCompat="@drawable/ic_baseline_more_vert" />
 
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/wallet/src/main/res/menu/transactions_detail.xml 
b/wallet/src/main/res/menu/exchange.xml
similarity index 86%
copy from wallet/src/main/res/menu/transactions_detail.xml
copy to wallet/src/main/res/menu/exchange.xml
index d4568d4..85ec08f 100644
--- a/wallet/src/main/res/menu/transactions_detail.xml
+++ b/wallet/src/main/res/menu/exchange.xml
@@ -14,6 +14,8 @@
   ~ GNU Taler; see the file COPYING.  If not, see 
<http://www.gnu.org/licenses/>
   -->
 
-<menu xmlns:android="http://schemas.android.com/apk/res/android";
-    xmlns:app="http://schemas.android.com/apk/res-auto";>
+<menu xmlns:android="http://schemas.android.com/apk/res/android";>
+    <item
+        android:id="@+id/action_manual_withdrawal"
+        android:title="@string/exchange_menu_manual_withdraw" />
 </menu>
diff --git a/wallet/src/main/res/navigation/nav_graph.xml 
b/wallet/src/main/res/navigation/nav_graph.xml
index 51dcaba..c9a98f0 100644
--- a/wallet/src/main/res/navigation/nav_graph.xml
+++ b/wallet/src/main/res/navigation/nav_graph.xml
@@ -28,9 +28,6 @@
         <action
             android:id="@+id/action_nav_main_to_promptPayment"
             app:destination="@id/promptPayment" />
-        <action
-            android:id="@+id/action_nav_main_to_promptWithdraw"
-            app:destination="@id/promptWithdraw" />
         <action
             android:id="@+id/action_nav_main_to_nav_transactions"
             app:destination="@id/nav_transactions" />
@@ -58,11 +55,28 @@
         android:id="@+id/nav_settings"
         android:name="net.taler.wallet.settings.SettingsFragment"
         android:label="@string/menu_settings">
+        <action
+            android:id="@+id/action_nav_settings_to_nav_settings_exchanges"
+            app:destination="@id/nav_settings_exchanges" />
         <action
             android:id="@+id/action_nav_settings_to_nav_settings_backup"
             app:destination="@id/nav_settings_backup" />
     </fragment>
 
+    <fragment
+        android:id="@+id/nav_settings_exchanges"
+        android:name="net.taler.wallet.exchanges.ExchangeListFragment"
+        android:label="@string/exchange_list_title">
+        <action
+            
android:id="@+id/action_nav_settings_exchanges_to_nav_exchange_manual_withdrawal"
+            app:destination="@id/nav_exchange_manual_withdrawal" />
+    </fragment>
+
+    <fragment
+        android:id="@+id/nav_exchange_manual_withdrawal"
+        android:name="net.taler.wallet.exchanges.ManualWithdrawFragment"
+        android:label="@string/withdraw_title"/>
+
     <fragment
         android:id="@+id/nav_settings_backup"
         android:name="net.taler.wallet.settings.BackupSettingsFragment"
@@ -147,6 +161,10 @@
         android:label="@string/nav_error"
         tools:layout="@layout/fragment_error" />
 
+    <action
+        android:id="@+id/action_global_promptWithdraw"
+        app:destination="@id/promptWithdraw" />
+
     <action
         android:id="@+id/action_global_promptPayment"
         app:destination="@id/promptPayment" />
diff --git a/wallet/src/main/res/values/strings.xml 
b/wallet/src/main/res/values/strings.xml
index fec5948..7e8024f 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -50,7 +50,10 @@ GNU Taler is immune against many types of fraud, such as 
phishing of credit card
     <string name="paste_invalid">Clipboard contains an invalid data 
type</string>
     <string name="uri_invalid">Not a valid Taler URI</string>
     <string name="ok">OK</string>
+    <string name="cancel">Cancel</string>
     <string name="search">Search</string>
+    <string name="menu">Menu</string>
+    <string name="or">or</string>
 
     <string name="menu_settings">Settings</string>
     <string name="menu_retry_pending_operations">Retry Pending 
Operations</string>
@@ -109,9 +112,23 @@ GNU Taler is immune against many types of fraud, such as 
phishing of credit card
     <string name="withdraw_button_confirm_bank">Confirm with bank</string>
     <string name="withdraw_button_tos">Review Terms</string>
     <string name="withdraw_waiting_confirm">Waiting for confirmation</string>
+    <string name="withdraw_manual_title">Make a manual transfer to the 
exchange</string>
+    <string name="withdraw_amount">How much to withdraw?</string>
+    <string name="withdraw_manual_payment_options">Payment options supported 
by %s:</string>
+    <string name="withdraw_manual_check_fees">Check fees</string>
     <string name="withdraw_error_title">Withdrawal Error</string>
     <string name="withdraw_error_message">Withdrawing is currently not 
possible. Please try again later!</string>
 
+    <string name="exchange_settings_title">Exchanges</string>
+    <string name="exchange_settings_summary">Manage list of exchanges known to 
this wallet</string>
+    <string name="exchange_list_title">Exchanges</string>
+    <string name="exchange_list_empty">No exchanges known\n\nAdd one manually 
or withdraw digital cash!</string>
+    <string name="exchange_list_currency">Currency: %s</string>
+    <string name="exchange_list_add">Add exchange</string>
+    <string name="exchange_add_url">Enter address of exchange</string>
+    <string name="exchange_add_error">Could not add exchange</string>
+    <string name="exchange_menu_manual_withdraw">Withdraw</string>
+
     <string name="exchange_fee_withdrawal_fee_label">Withdrawal Fee:</string>
     <string name="exchange_fee_overhead_label">Rounding Loss:</string>
     <string name="exchange_fee_coin_expiration_label">Earliest Coin 
Expiry:</string>
diff --git a/wallet/src/main/res/values/styles.xml 
b/wallet/src/main/res/values/styles.xml
index 093f43f..33e31a3 100644
--- a/wallet/src/main/res/values/styles.xml
+++ b/wallet/src/main/res/values/styles.xml
@@ -35,6 +35,8 @@
 
     <style name="AppTheme.Toolbar" 
parent="Widget.MaterialComponents.Toolbar.Primary" />
 
+    <style name="DialogTheme" 
parent="Theme.MaterialComponents.DayNight.Dialog.Alert" />
+
     <style name="TransactionTitle">
         <item name="android:textSize">16sp</item>
         <item name="android:textColor">?android:textColorPrimary</item>
@@ -70,4 +72,13 @@
         <item name="cardElevation">8dp</item>
     </style>
 
+    <style name="FabStyle" 
parent="Widget.MaterialComponents.FloatingActionButton">
+        <item name="android:layout_gravity">bottom|end</item>
+        <item name="android:layout_marginEnd">16dp</item>
+        <item name="android:layout_marginBottom">16dp</item>
+        <item name="backgroundTint">@color/colorPrimary</item>
+        <item name="layout_dodgeInsetEdges">bottom</item>
+        <item name="tint">?attr/colorOnPrimary</item>
+    </style>
+
 </resources>
diff --git a/wallet/src/main/res/xml/settings_main.xml 
b/wallet/src/main/res/xml/settings_main.xml
index 4defc08..4c2f149 100644
--- a/wallet/src/main/res/xml/settings_main.xml
+++ b/wallet/src/main/res/xml/settings_main.xml
@@ -17,6 +17,13 @@
 <PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto";
     xmlns:tools="http://schemas.android.com/tools";>
 
+    <Preference
+        app:fragment="net.taler.wallet.exchanges.ExchangeListFragment"
+        app:icon="@drawable/ic_account_balance"
+        app:key="pref_exchanges"
+        app:summary="@string/exchange_settings_summary"
+        app:title="@string/exchange_settings_title" />
+
     <Preference
         app:fragment="net.taler.wallet.settings.BackupSettingsFragment"
         app:icon="@drawable/ic_baseline_backup"

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]