diff options
18 files changed, 446 insertions, 120 deletions
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 5bc5721..ad9dab9 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 @@ -27,6 +27,7 @@ import android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE import android.text.format.DateUtils.FORMAT_NO_YEAR import android.text.format.DateUtils.FORMAT_SHOW_DATE import android.text.format.DateUtils.FORMAT_SHOW_TIME +import android.text.format.DateUtils.FORMAT_SHOW_YEAR import android.text.format.DateUtils.MINUTE_IN_MILLIS import android.text.format.DateUtils.formatDateTime import android.text.format.DateUtils.getRelativeTimeSpanString @@ -82,3 +83,8 @@ fun Long.toRelativeTime(context: Context): CharSequence { formatDateTime(context, this, flags) } else getRelativeTimeSpanString(this, now, MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE) } + +fun Long.toAbsoluteTime(context: Context): CharSequence { + val flags = FORMAT_SHOW_TIME or FORMAT_SHOW_DATE or FORMAT_SHOW_YEAR + return formatDateTime(context, this, flags) +} diff --git a/wallet/src/main/java/net/taler/wallet/BalanceFragment.kt b/wallet/src/main/java/net/taler/wallet/BalanceFragment.kt index d871cfb..3d5364b 100644 --- a/wallet/src/main/java/net/taler/wallet/BalanceFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/BalanceFragment.kt @@ -31,6 +31,7 @@ import android.widget.TextView 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 @@ -41,13 +42,17 @@ import com.google.zxing.integration.android.IntentIntegrator.QR_CODE import kotlinx.android.synthetic.main.fragment_show_balance.* import net.taler.wallet.BalanceAdapter.BalanceViewHolder -class BalanceFragment : Fragment() { +interface BalanceClickListener { + fun onBalanceClick() +} + +class BalanceFragment : Fragment(), BalanceClickListener { private val model: WalletViewModel by activityViewModels() private val withdrawManager by lazy { model.withdrawManager } private var reloadBalanceMenuItem: MenuItem? = null - private val balancesAdapter = BalanceAdapter() + private val balancesAdapter = BalanceAdapter(this) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -141,9 +146,13 @@ class BalanceFragment : Fragment() { beginDelayedTransition(view as ViewGroup) } + override fun onBalanceClick() { + findNavController().navigate(R.id.walletHistory) + } + } -class BalanceAdapter : Adapter<BalanceViewHolder>() { +class BalanceAdapter(private val listener: BalanceClickListener) : Adapter<BalanceViewHolder>() { private var items = emptyList<BalanceItem>() @@ -169,13 +178,14 @@ class BalanceAdapter : Adapter<BalanceViewHolder>() { this.notifyDataSetChanged() } - class BalanceViewHolder(private val v: View) : ViewHolder(v) { + inner class BalanceViewHolder(private val v: View) : ViewHolder(v) { private val currencyView: TextView = v.findViewById(R.id.balance_currency) private val amountView: TextView = v.findViewById(R.id.balance_amount) private val balanceInboundAmount: TextView = v.findViewById(R.id.balanceInboundAmount) private val balanceInboundLabel: TextView = v.findViewById(R.id.balanceInboundLabel) fun bind(item: BalanceItem) { + v.setOnClickListener { listener.onBalanceClick() } currencyView.text = item.available.currency amountView.text = item.available.amountStr diff --git a/wallet/src/main/java/net/taler/wallet/Utils.kt b/wallet/src/main/java/net/taler/wallet/Utils.kt new file mode 100644 index 0000000..ae8712f --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/Utils.kt @@ -0,0 +1,21 @@ +/* + * 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 + +fun cleanExchange(exchange: String) = exchange.let { + if (it.startsWith("https://")) it.substring(8) else it +}.trimEnd('/') diff --git a/wallet/src/main/java/net/taler/wallet/history/HistoryAdapter.kt b/wallet/src/main/java/net/taler/wallet/history/HistoryAdapter.kt index 43b7bd7..881ada5 100644 --- a/wallet/src/main/java/net/taler/wallet/history/HistoryAdapter.kt +++ b/wallet/src/main/java/net/taler/wallet/history/HistoryAdapter.kt @@ -16,23 +16,19 @@ package net.taler.wallet.history -import android.annotation.SuppressLint +import android.content.Context import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG import android.view.LayoutInflater import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.annotation.CallSuper -import androidx.core.net.toUri import androidx.recyclerview.widget.RecyclerView.Adapter import androidx.recyclerview.widget.RecyclerView.ViewHolder -import net.taler.common.Amount import net.taler.common.toRelativeTime -import net.taler.wallet.BuildConfig import net.taler.wallet.R +import net.taler.wallet.cleanExchange import net.taler.wallet.history.HistoryAdapter.HistoryEventViewHolder @@ -68,23 +64,20 @@ internal class HistoryAdapter( this.notifyDataSetChanged() } - internal abstract inner class HistoryEventViewHolder(protected val v: View) : ViewHolder(v) { + internal abstract inner class HistoryEventViewHolder(private val v: View) : ViewHolder(v) { + protected val context: Context = v.context private val icon: ImageView = v.findViewById(R.id.icon) protected val title: TextView = v.findViewById(R.id.title) private val time: TextView = v.findViewById(R.id.time) @CallSuper open fun bind(event: HistoryEvent) { - if (BuildConfig.DEBUG) { // doesn't produce recycling issues, no need to cover all cases - v.setOnClickListener { listener.onEventClicked(event) } - } else { - v.background = null - } + v.setOnClickListener { listener.onEventClicked(event) } icon.setImageResource(event.icon) if (event.title == 0) title.text = event::class.java.simpleName else title.setText(event.title) - time.text = event.timestamp.ms.toRelativeTime(v.context) + time.text = event.timestamp.ms.toRelativeTime(context) } } @@ -96,8 +89,8 @@ internal class HistoryAdapter( override fun bind(event: HistoryEvent) { super.bind(event) info.text = when (event) { - is ExchangeAddedEvent -> event.exchangeBaseUrl - is ExchangeUpdatedEvent -> event.exchangeBaseUrl + is ExchangeAddedEvent -> cleanExchange(event.exchangeBaseUrl) + is ExchangeUpdatedEvent -> cleanExchange(event.exchangeBaseUrl) is ReserveBalanceUpdatedEvent -> event.amountReserveBalance.toString() is HistoryPaymentSentEvent -> event.orderShortInfo.summary is HistoryOrderAcceptedEvent -> event.orderShortInfo.summary @@ -113,8 +106,7 @@ internal class HistoryAdapter( private val summary: TextView = v.findViewById(R.id.summary) private val amountWithdrawn: TextView = v.findViewById(R.id.amountWithdrawn) - private val feeLabel: TextView = v.findViewById(R.id.feeLabel) - private val fee: TextView = v.findViewById(R.id.fee) + private val paintFlags = amountWithdrawn.paintFlags override fun bind(event: HistoryEvent) { super.bind(event) @@ -127,52 +119,31 @@ internal class HistoryAdapter( } private fun bind(event: HistoryWithdrawnEvent) { - title.text = getHostname(event.exchangeBaseUrl) - summary.setText(event.title) - - showAmounts(event.amountWithdrawnEffective, event.amountWithdrawnRaw) + summary.text = cleanExchange(event.exchangeBaseUrl) + amountWithdrawn.text = + context.getString(R.string.amount_positive, event.amountWithdrawnEffective) + amountWithdrawn.paintFlags = paintFlags } private fun bind(event: HistoryRefundedEvent) { - title.text = event.orderShortInfo.summary - summary.setText(event.title) - - showAmounts(event.amountRefundedEffective, event.amountRefundedRaw) + summary.text = event.orderShortInfo.summary + amountWithdrawn.text = + context.getString(R.string.amount_positive, event.amountRefundedEffective) + amountWithdrawn.paintFlags = paintFlags } private fun bind(event: HistoryTipAcceptedEvent) { - title.setText(event.title) summary.text = null - showAmounts(event.tipRaw, event.tipRaw) + amountWithdrawn.text = context.getString(R.string.amount_positive, event.tipRaw) + amountWithdrawn.paintFlags = paintFlags } private fun bind(event: HistoryTipDeclinedEvent) { - title.setText(event.title) summary.text = null - showAmounts(event.tipAmount, event.tipAmount) + amountWithdrawn.text = context.getString(R.string.amount_positive, event.tipAmount) amountWithdrawn.paintFlags = amountWithdrawn.paintFlags or STRIKE_THRU_TEXT_FLAG } - private fun showAmounts(effective: Amount, raw: Amount) { - @SuppressLint("SetTextI18n") - amountWithdrawn.text = "+$raw" - val calculatedFee = raw - effective - if (calculatedFee.isZero()) { - fee.visibility = GONE - feeLabel.visibility = GONE - } else { - @SuppressLint("SetTextI18n") - fee.text = "-$calculatedFee" - fee.visibility = VISIBLE - feeLabel.visibility = VISIBLE - } - amountWithdrawn.paintFlags = fee.paintFlags - } - - private fun getHostname(url: String): String { - return url.toUri().host!! - } - } internal inner class HistoryPaymentViewHolder(v: View) : HistoryEventViewHolder(v) { @@ -182,7 +153,6 @@ internal class HistoryAdapter( override fun bind(event: HistoryEvent) { super.bind(event) - summary.setText(event.title) when (event) { is HistoryPaymentSentEvent -> bind(event) is HistoryPaymentAbortedEvent -> bind(event) @@ -191,23 +161,29 @@ internal class HistoryAdapter( } private fun bind(event: HistoryPaymentSentEvent) { - title.text = event.orderShortInfo.summary - @SuppressLint("SetTextI18n") - amountPaidWithFees.text = "-${event.amountPaidWithFees}" + summary.text = event.orderShortInfo.summary + amountPaidWithFees.text = + context.getString(R.string.amount_negative, event.amountPaidWithFees) } private fun bind(event: HistoryPaymentAbortedEvent) { - title.text = event.orderShortInfo.summary - @SuppressLint("SetTextI18n") - amountPaidWithFees.text = "-${event.amountLost}" + summary.text = event.orderShortInfo.summary + amountPaidWithFees.text = context.getString(R.string.amount_negative, event.amountLost) } private fun bind(event: HistoryRefreshedEvent) { - title.text = "" + val res = when (event.refreshReason) { + RefreshReason.MANUAL -> R.string.history_event_refresh_reason_manual + RefreshReason.PAY -> R.string.history_event_refresh_reason_pay + RefreshReason.REFUND -> R.string.history_event_refresh_reason_refund + RefreshReason.ABORT_PAY -> R.string.history_event_refresh_reason_abort_pay + RefreshReason.RECOUP -> R.string.history_event_refresh_reason_recoup + RefreshReason.BACKUP_RESTORED -> R.string.history_event_refresh_reason_backup_restored + } + summary.text = context.getString(res) val fee = event.amountRefreshedRaw - event.amountRefreshedEffective - @SuppressLint("SetTextI18n") if (fee.isZero()) amountPaidWithFees.text = null - else amountPaidWithFees.text = "-$fee" + else amountPaidWithFees.text = context.getString(R.string.amount_negative, fee) } } diff --git a/wallet/src/main/java/net/taler/wallet/history/HistoryEventFragment.kt b/wallet/src/main/java/net/taler/wallet/history/HistoryEventFragment.kt new file mode 100644 index 0000000..f0dec75 --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/history/HistoryEventFragment.kt @@ -0,0 +1,80 @@ +/* + * 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.history + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import kotlinx.android.synthetic.main.fragment_history_event.* +import net.taler.common.toAbsoluteTime +import net.taler.wallet.R +import net.taler.wallet.WalletViewModel +import net.taler.wallet.cleanExchange + +class HistoryEventFragment : Fragment() { + + private val model: WalletViewModel by activityViewModels() + private val historyManager by lazy { model.historyManager } + private val event by lazy { requireNotNull(historyManager.selectedEvent) } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (model.devMode.value == true) setHasOptionsMenu(true) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_history_event, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val event = event as HistoryWithdrawnEvent + + timeView.text = event.timestamp.ms.toAbsoluteTime(requireContext()) + effectiveAmountLabel.text = getString(R.string.withdraw_total) + effectiveAmountView.text = event.amountWithdrawnEffective.toString() + chosenAmountLabel.text = getString(R.string.amount_chosen) + chosenAmountView.text = + getString(R.string.amount_positive, event.amountWithdrawnRaw.toString()) + val fee = event.amountWithdrawnRaw - event.amountWithdrawnEffective + feeView.text = getString(R.string.amount_negative, fee.toString()) + exchangeView.text = cleanExchange(event.exchangeBaseUrl) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.history_event, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.show_json -> { + JsonDialogFragment.new(event.json.toString(2)).show(parentFragmentManager, null) + true + } + else -> super.onOptionsItemSelected(item) + } + } + +} diff --git a/wallet/src/main/java/net/taler/wallet/history/HistoryFragment.kt b/wallet/src/main/java/net/taler/wallet/history/HistoryFragment.kt index 2586ef8..b0f6728 100644 --- a/wallet/src/main/java/net/taler/wallet/history/HistoryFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/history/HistoryFragment.kt @@ -28,10 +28,14 @@ import android.view.ViewGroup 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_show_history.* +import net.taler.common.exhaustive +import net.taler.common.fadeIn +import net.taler.common.fadeOut import net.taler.wallet.R import net.taler.wallet.WalletViewModel @@ -73,8 +77,7 @@ class HistoryFragment : Fragment(), OnEventClickListener { historyProgressBar.visibility = if (show) VISIBLE else INVISIBLE }) historyManager.history.observe(viewLifecycleOwner, Observer { history -> - historyEmptyState.visibility = if (history.isEmpty()) VISIBLE else INVISIBLE - historyAdapter.update(history) + onHistoryResult(history) }) // kicks off initial load, needs to be adapted if showAll state is ever saved @@ -106,9 +109,29 @@ class HistoryFragment : Fragment(), OnEventClickListener { } override fun onEventClicked(event: HistoryEvent) { - if (model.devMode.value != true) return - JsonDialogFragment.new(event.json.toString(4)) - .show(parentFragmentManager, null) + when (event) { + is HistoryWithdrawnEvent -> { + historyManager.selectedEvent = event + findNavController().navigate(R.id.action_walletHistory_to_historyEventFragment) + } + else -> { + if (model.devMode.value != true) return + JsonDialogFragment.new(event.json.toString(2)) + .show(parentFragmentManager, null) + } + }.exhaustive + } + + private fun onHistoryResult(result: HistoryResult) = when (result) { + HistoryResult.Error -> { + historyList.fadeOut() + historyEmptyState.text = getString(R.string.history_error) + historyEmptyState.fadeIn() + } + is HistoryResult.Success -> { + historyEmptyState.visibility = if (result.history.isEmpty()) VISIBLE else INVISIBLE + historyAdapter.update(result.history) + } } } diff --git a/wallet/src/main/java/net/taler/wallet/history/HistoryManager.kt b/wallet/src/main/java/net/taler/wallet/history/HistoryManager.kt index c350daa..7ce4f5b 100644 --- a/wallet/src/main/java/net/taler/wallet/history/HistoryManager.kt +++ b/wallet/src/main/java/net/taler/wallet/history/HistoryManager.kt @@ -29,6 +29,11 @@ import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onStart import net.taler.wallet.backend.WalletBackendApi +sealed class HistoryResult { + object Error : HistoryResult() + class Success(val history: History) : HistoryResult() +} + @Suppress("EXPERIMENTAL_API_USAGE") class HistoryManager( private val walletBackendApi: WalletBackendApi, @@ -40,7 +45,9 @@ class HistoryManager( val showAll = MutableLiveData<Boolean>() - val history: LiveData<History> = showAll.switchMap { showAll -> + var selectedEvent: HistoryEvent? = null + + val history: LiveData<HistoryResult> = showAll.switchMap { showAll -> loadHistory(showAll) .onStart { mProgress.postValue(true) } .onCompletion { mProgress.postValue(false) } @@ -50,7 +57,7 @@ class HistoryManager( private fun loadHistory(showAll: Boolean) = callbackFlow { walletBackendApi.sendRequest("getHistory", null) { isError, result -> if (isError) { - // TODO show error message in [WalletHistory] fragment + offer(HistoryResult.Error) close() return@sendRequest } @@ -62,7 +69,8 @@ class HistoryManager( history.add(event) } history.reverse() // show latest first - offer(if (showAll) history else history.filter { it.showToUser } as History) + val filtered = if (showAll) history else history.filter { it.showToUser } as History + offer(HistoryResult.Success(filtered)) close() } awaitClose() diff --git a/wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt b/wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt index f51dba9..5421db3 100644 --- a/wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt @@ -20,6 +20,8 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.fragment.app.DialogFragment import kotlinx.android.synthetic.main.fragment_json.* import net.taler.wallet.R @@ -47,4 +49,9 @@ class JsonDialogFragment : DialogFragment() { jsonView.text = json } + override fun onStart() { + super.onStart() + dialog?.window?.setLayout(MATCH_PARENT, WRAP_CONTENT) + } + } 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 ea04e24..5d0fe63 100644 --- a/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt @@ -30,6 +30,7 @@ import net.taler.common.fadeIn import net.taler.common.fadeOut import net.taler.wallet.R import net.taler.wallet.WalletViewModel +import net.taler.wallet.cleanExchange import net.taler.wallet.withdraw.WithdrawStatus.Loading import net.taler.wallet.withdraw.WithdrawStatus.TermsOfServiceReviewRequired import net.taler.wallet.withdraw.WithdrawStatus.Withdrawing @@ -115,9 +116,7 @@ class PromptWithdrawFragment : Fragment() { feeView.fadeIn() exchangeIntroView.fadeIn() - withdrawExchangeUrl.text = exchange.let { - if (it.startsWith("https://")) it.substring(8) else it - }.trimEnd('/') + withdrawExchangeUrl.text = cleanExchange(exchange) withdrawExchangeUrl.fadeIn() withdrawCard.fadeIn() diff --git a/wallet/src/main/res/layout/fragment_history_event.xml b/wallet/src/main/res/layout/fragment_history_event.xml new file mode 100644 index 0000000..4224093 --- /dev/null +++ b/wallet/src/main/res/layout/fragment_history_event.xml @@ -0,0 +1,160 @@ +<?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" + tools:context=".history.HistoryEventFragment"> + + <TextView + android:id="@+id/timeView" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_margin="16dp" + android:gravity="center" + android:textAppearance="@style/TextAppearance.AppCompat.Medium" + app:layout_constraintBottom_toTopOf="@+id/effectiveAmountLabel" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_chainStyle="packed" + tools:text="23 March 2020 23:42pm" /> + + <TextView + android:id="@+id/effectiveAmountLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="16dp" + android:gravity="center" + app:layout_constraintBottom_toTopOf="@+id/effectiveAmountView" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/timeView" + tools:text="@string/withdraw_total" /> + + <TextView + android:id="@+id/effectiveAmountView" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="16dp" + android:gravity="center" + android:textColor="@color/green" + android:textSize="24sp" + app:layout_constraintBottom_toTopOf="@+id/chosenAmountLabel" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/effectiveAmountLabel" + tools:text="23.42 TESTKUDOS" /> + + <TextView + android:id="@+id/chosenAmountLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="16dp" + android:gravity="center" + app:layout_constraintBottom_toTopOf="@+id/chosenAmountView" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/effectiveAmountView" + tools:text="@string/amount_chosen" /> + + <TextView + android:id="@+id/chosenAmountView" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="16dp" + android:gravity="center" + android:textSize="24sp" + app:layout_constraintBottom_toTopOf="@+id/feeLabel" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/chosenAmountLabel" + tools:text="24 TESTKUDOS" /> + + <TextView + android:id="@+id/feeLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="16dp" + android:gravity="center" + android:text="@string/withdraw_fees" + app:layout_constraintBottom_toTopOf="@+id/feeView" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/chosenAmountView" /> + + <TextView + android:id="@+id/feeView" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="16dp" + android:gravity="center" + android:textColor="@color/red" + android:textSize="24sp" + app:layout_constraintBottom_toTopOf="@+id/exchangeLabel" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/feeLabel" + tools:text="-0.38 TESTKUDOS" /> + + <TextView + android:id="@+id/exchangeLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="16dp" + android:gravity="center" + android:text="@string/withdraw_exchange" + app:layout_constraintBottom_toTopOf="@+id/exchangeView" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/feeView" /> + + <TextView + android:id="@+id/exchangeView" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + android:gravity="center" + android:textSize="24sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/exchangeLabel" + tools:text="exchange.demo.taler.net" /> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/wallet/src/main/res/layout/history_payment.xml b/wallet/src/main/res/layout/history_payment.xml index 3839038..33cb676 100644 --- a/wallet/src/main/res/layout/history_payment.xml +++ b/wallet/src/main/res/layout/history_payment.xml @@ -19,11 +19,11 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginStart="16dp" - android:layout_marginTop="8dp" - android:layout_marginEnd="16dp" - android:layout_marginBottom="8dp" - android:background="?attr/selectableItemBackground"> + android:background="?attr/selectableItemBackground" + android:paddingStart="16dp" + android:paddingTop="8dp" + android:paddingEnd="16dp" + android:paddingBottom="8dp"> <ImageView android:id="@+id/icon" @@ -32,36 +32,36 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:srcCompat="@drawable/history_withdrawn" app:tint="?android:colorControlNormal" - tools:ignore="ContentDescription" /> + tools:ignore="ContentDescription" + tools:src="@drawable/ic_cash_usd_outline" /> <TextView android:id="@+id/title" style="@style/HistoryTitle" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="8dp" + android:layout_marginStart="16dp" android:layout_marginEnd="8dp" app:layout_constraintEnd_toStartOf="@+id/amountPaidWithFees" app:layout_constraintStart_toEndOf="@+id/icon" app:layout_constraintTop_toTopOf="parent" - tools:text="Lots of books with very long titles" /> + tools:text="@string/history_event_payment_sent" /> <TextView android:id="@+id/summary" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="8dp" + android:layout_marginStart="16dp" android:layout_marginTop="8dp" app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/amountPaidWithFees" + app:layout_constraintEnd_toStartOf="@+id/time" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toEndOf="@+id/icon" app:layout_constraintTop_toBottomOf="@+id/title" app:layout_constraintVertical_bias="0.0" - tools:text="@string/history_event_payment_sent" /> + tools:text="Lots of books with very long titles" /> <TextView android:id="@+id/amountPaidWithFees" diff --git a/wallet/src/main/res/layout/history_receive.xml b/wallet/src/main/res/layout/history_receive.xml index def97a2..5f386a2 100644 --- a/wallet/src/main/res/layout/history_receive.xml +++ b/wallet/src/main/res/layout/history_receive.xml @@ -19,11 +19,11 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginStart="16dp" - android:layout_marginTop="8dp" - android:layout_marginEnd="16dp" - android:layout_marginBottom="8dp" - android:background="?attr/selectableItemBackground"> + android:background="?attr/selectableItemBackground" + android:paddingStart="16dp" + android:paddingTop="8dp" + android:paddingEnd="16dp" + android:paddingBottom="8dp"> <ImageView android:id="@+id/icon" @@ -41,7 +41,7 @@ style="@style/HistoryTitle" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="8dp" + android:layout_marginStart="16dp" android:layout_marginEnd="8dp" android:text="@string/history_event_withdrawn" app:layout_constraintEnd_toStartOf="@+id/amountWithdrawn" @@ -52,34 +52,18 @@ android:id="@+id/summary" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="8dp" + android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/feeLabel" + app:layout_constraintEnd_toStartOf="@+id/time" + app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toEndOf="@+id/icon" app:layout_constraintTop_toBottomOf="@+id/title" - tools:text="http://taler.quite-long-exchange.url" /> - - <TextView - android:id="@+id/feeLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="2dp" - android:text="@string/history_fee_label" - app:layout_constraintEnd_toStartOf="@+id/fee" - app:layout_constraintTop_toTopOf="@+id/fee" /> - - <TextView - android:id="@+id/fee" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textColor="@color/red" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@+id/amountWithdrawn" - tools:text="0.2 TESTKUDOS" /> + app:layout_constraintVertical_bias="0.0" + tools:text="exchange.taler.quite-long-domain-name.org" /> <TextView android:id="@+id/amountWithdrawn" @@ -96,10 +80,13 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" + android:layout_marginBottom="8dp" android:textSize="14sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@+id/fee" + app:layout_constraintStart_toEndOf="@+id/summary" + app:layout_constraintTop_toBottomOf="@+id/amountWithdrawn" + app:layout_constraintVertical_bias="1.0" tools:text="23 min. ago" /> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/wallet/src/main/res/layout/history_row.xml b/wallet/src/main/res/layout/history_row.xml index 2982008..5eac44b 100644 --- a/wallet/src/main/res/layout/history_row.xml +++ b/wallet/src/main/res/layout/history_row.xml @@ -19,8 +19,11 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="15dp" - android:background="?attr/selectableItemBackground"> + android:background="?attr/selectableItemBackground" + android:paddingStart="16dp" + android:paddingTop="8dp" + android:paddingEnd="16dp" + android:paddingBottom="8dp"> <ImageView android:id="@+id/icon" @@ -38,7 +41,7 @@ style="@style/HistoryTitle" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="8dp" + android:layout_marginStart="16dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/icon" app:layout_constraintTop_toTopOf="parent" @@ -49,7 +52,7 @@ android:id="@+id/info" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="8dp" + android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" app:layout_constraintBottom_toBottomOf="parent" diff --git a/wallet/src/main/res/layout/list_item_balance.xml b/wallet/src/main/res/layout/list_item_balance.xml index dc24232..a9e15df 100644 --- a/wallet/src/main/res/layout/list_item_balance.xml +++ b/wallet/src/main/res/layout/list_item_balance.xml @@ -19,6 +19,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" android:padding="16dp"> <TextView diff --git a/wallet/src/main/res/menu/history_event.xml b/wallet/src/main/res/menu/history_event.xml new file mode 100644 index 0000000..45a1e0e --- /dev/null +++ b/wallet/src/main/res/menu/history_event.xml @@ -0,0 +1,23 @@ +<?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" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item + android:id="@+id/show_json" + android:title="@string/history_detail_json" + app:showAsAction="never" /> +</menu> diff --git a/wallet/src/main/res/navigation/nav_graph.xml b/wallet/src/main/res/navigation/nav_graph.xml index 9875b8a..1ab9666 100644 --- a/wallet/src/main/res/navigation/nav_graph.xml +++ b/wallet/src/main/res/navigation/nav_graph.xml @@ -57,11 +57,23 @@ android:name="net.taler.wallet.Settings" android:label="Settings" tools:layout="@layout/fragment_settings" /> + <fragment android:id="@+id/walletHistory" android:name="net.taler.wallet.history.HistoryFragment" android:label="@string/history_title" - tools:layout="@layout/fragment_show_history" /> + tools:layout="@layout/fragment_show_history"> + <action + android:id="@+id/action_walletHistory_to_historyEventFragment" + app:destination="@id/historyEventFragment" /> + </fragment> + + <fragment + android:id="@+id/historyEventFragment" + android:name="net.taler.wallet.history.HistoryEventFragment" + android:label="@string/history_detail_title" + tools:layout="@layout/fragment_history_event" /> + <fragment android:id="@+id/alreadyPaid" android:name="net.taler.wallet.payment.AlreadyPaidFragment" diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml index bcd173f..1f028c2 100644 --- a/wallet/src/main/res/values/strings.xml +++ b/wallet/src/main/res/values/strings.xml @@ -41,6 +41,7 @@ <string name="balances_title">Balances</string> <string name="amount_positive">+%s</string> <string name="amount_negative">-%s</string> + <string name="amount_chosen">Chosen Amount</string> <string name="balances_inbound_label">inbound</string> <string name="balances_empty_state">There is no digital cash in your wallet.\n\nYou can get test money from the demo bank:\n\nhttps://bank.demo.taler.net</string> @@ -49,6 +50,9 @@ <string name="history_show_all">Show All</string> <string name="history_reload">Reload History</string> <string name="history_empty">The wallet history is empty</string> + <string name="history_error">Could not load history</string> + <string name="history_detail_title">Transaction</string> + <string name="history_detail_json">Show JSON</string> <!-- HistoryEvents --> <string name="history_event_exchange_added">Exchange Added</string> @@ -64,6 +68,12 @@ <string name="history_event_order_redirected">Purchase Redirected</string> <string name="history_event_refund">Refund</string> <string name="history_event_refreshed">Obtained change</string> + <string name="history_event_refresh_reason_manual">because of manual request</string> + <string name="history_event_refresh_reason_pay">for payment</string> + <string name="history_event_refresh_reason_refund">for refund</string> + <string name="history_event_refresh_reason_abort_pay">to abort payment</string> + <string name="history_event_refresh_reason_recoup">to recoup funds</string> + <string name="history_event_refresh_reason_backup_restored">because of restoring from backup</string> <string name="history_event_unknown">Unknown Event</string> <string name="payment_fee">+%s payment fee</string> diff --git a/wallet/src/main/res/values/styles.xml b/wallet/src/main/res/values/styles.xml index 83f3e3a..c3e18a6 100644 --- a/wallet/src/main/res/values/styles.xml +++ b/wallet/src/main/res/values/styles.xml @@ -34,7 +34,7 @@ <style name="AppTheme.Toolbar" parent="Widget.MaterialComponents.Toolbar.Primary" /> <style name="HistoryTitle"> - <item name="android:textSize">17sp</item> + <item name="android:textSize">16sp</item> <item name="android:textColor">?android:textColorPrimary</item> </style> |