diff options
Diffstat (limited to 'wallet/src')
11 files changed, 327 insertions, 23 deletions
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 f53ce46..189f444 100644 --- a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt +++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt @@ -19,8 +19,11 @@ 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 @@ -29,9 +32,16 @@ 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 : RecyclerView.Adapter<ExchangeItemViewHolder>() { +internal class ExchangeAdapter(private val listener: ExchangeClickListener) : + Adapter<ExchangeItemViewHolder>() { private val items = ArrayList<ExchangeItem>() @@ -57,9 +67,26 @@ internal class ExchangeAdapter : RecyclerView.Adapter<ExchangeItemViewHolder>() 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 = cleanExchange(item.exchangeBaseUrl) + 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/exchanges/ExchangeListFragment.kt b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt index c844042..c7da205 100644 --- a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt @@ -25,8 +25,9 @@ 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 +import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL import kotlinx.android.synthetic.main.fragment_exchange_list.* import net.taler.common.EventObserver import net.taler.common.fadeIn @@ -34,11 +35,11 @@ import net.taler.common.fadeOut import net.taler.wallet.MainViewModel import net.taler.wallet.R -class ExchangeListFragment : Fragment() { +class ExchangeListFragment : Fragment(), ExchangeClickListener { private val model: MainViewModel by activityViewModels() private val exchangeManager by lazy { model.exchangeManager } - private val exchangeAdapter by lazy { ExchangeAdapter() } + private val exchangeAdapter by lazy { ExchangeAdapter(this) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -50,7 +51,7 @@ class ExchangeListFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { list.apply { adapter = exchangeAdapter - addItemDecoration(DividerItemDecoration(context, LinearLayoutManager.VERTICAL)) + addItemDecoration(DividerItemDecoration(context, VERTICAL)) } addExchangeFab.setOnClickListener { AddExchangeDialogFragment().show(parentFragmentManager, "ADD_EXCHANGE") @@ -82,4 +83,9 @@ class ExchangeListFragment : Fragment() { 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 index 4b93c40..cdd5590 100644 --- a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt +++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt @@ -21,6 +21,7 @@ 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 @@ -41,6 +42,23 @@ class ExchangeManager( 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) } @@ -56,19 +74,18 @@ class ExchangeManager( } } - private fun list(): LiveData<List<ExchangeItem>> { - mProgress.value = true - walletBackendApi.sendRequest("listExchanges", JSONObject()) { isError, result -> + 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) { - throw AssertionError("Wallet core failed to return exchanges!") + Log.e(TAG, "$result") } else { - val exchanges: List<ExchangeItem> = mapper.readValue(result.getString("exchanges")) - Log.d(TAG, "Exchange list: $exchanges") - mProgress.value = false - mExchanges.value = exchanges + Log.e(TAG, "$result") } } - return mExchanges } } diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ManualWithdrawFragment.kt b/wallet/src/main/java/net/taler/wallet/exchanges/ManualWithdrawFragment.kt new file mode 100644 index 0000000..c3f201d --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/exchanges/ManualWithdrawFragment.kt @@ -0,0 +1,61 @@ +/* + * 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.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 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 ManualWithdrawFragment : Fragment() { + + private val model: MainViewModel by activityViewModels() + 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_manual_withdraw, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + 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/res/drawable/ic_baseline_more_vert.xml b/wallet/src/main/res/drawable/ic_baseline_more_vert.xml new file mode 100644 index 0000000..b19e0f1 --- /dev/null +++ b/wallet/src/main/res/drawable/ic_baseline_more_vert.xml @@ -0,0 +1,26 @@ +<!-- + ~ 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/> + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="?attr/colorControlNormal" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + 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/wallet/src/main/res/layout/fragment_exchange_list.xml b/wallet/src/main/res/layout/fragment_exchange_list.xml index c7404ae..29d88c7 100644 --- a/wallet/src/main/res/layout/fragment_exchange_list.xml +++ b/wallet/src/main/res/layout/fragment_exchange_list.xml @@ -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 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_exchange.xml b/wallet/src/main/res/layout/list_item_exchange.xml index 4c646fe..c9d1df4 100644 --- a/wallet/src/main/res/layout/list_item_exchange.xml +++ b/wallet/src/main/res/layout/list_item_exchange.xml @@ -21,16 +21,17 @@ android:layout_height="wrap_content" android:background="@drawable/selectable_background" android:foreground="?attr/selectableItemBackground" - android:padding="16dp"> + android:paddingTop="16dp" + android:paddingBottom="16dp"> <TextView android:id="@+id/urlView" style="@style/TransactionTitle" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginEnd="8dp" + android:layout_marginStart="16dp" android:textSize="18sp" - app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintEnd_toStartOf="@+id/overflowIcon" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="exchange.test.taler.net" /> @@ -40,11 +41,22 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:layout_marginEnd="8dp" android:textSize="14sp" - app:layout_constraintEnd_toEndOf="parent" + 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_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/exchange.xml b/wallet/src/main/res/menu/exchange.xml new file mode 100644 index 0000000..85ec08f --- /dev/null +++ b/wallet/src/main/res/menu/exchange.xml @@ -0,0 +1,21 @@ +<?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/> + --> + +<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 7019597..1242857 100644 --- a/wallet/src/main/res/navigation/nav_graph.xml +++ b/wallet/src/main/res/navigation/nav_graph.xml @@ -69,7 +69,16 @@ <fragment android:id="@+id/nav_settings_exchanges" android:name="net.taler.wallet.exchanges.ExchangeListFragment" - android:label="@string/exchange_list_title"/> + 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" diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml index 226c7d1..7e8024f 100644 --- a/wallet/src/main/res/values/strings.xml +++ b/wallet/src/main/res/values/strings.xml @@ -52,6 +52,8 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card <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> @@ -110,6 +112,10 @@ 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> @@ -121,6 +127,7 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card <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> |