From 2371ad0e4492e31648c0451b0b3fa799f7a99a42 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 22 Jan 2020 09:20:25 -0300 Subject: Show history event JSON in debug builds --- .../main/java/net/taler/wallet/WalletViewModel.kt | 9 +- .../java/net/taler/wallet/history/HistoryEvent.kt | 5 +- .../net/taler/wallet/history/JsonDialogFragment.kt | 50 ++++ .../java/net/taler/wallet/history/WalletHistory.kt | 13 +- .../taler/wallet/history/WalletHistoryAdapter.kt | 254 +++++++++++---------- app/src/main/res/layout/fragment_json.xml | 25 ++ app/src/main/res/layout/history_payment.xml | 1 + app/src/main/res/layout/history_receive.xml | 1 + app/src/main/res/layout/history_row.xml | 1 + .../net/taler/wallet/history/HistoryEventTest.kt | 29 --- 10 files changed, 232 insertions(+), 156 deletions(-) create mode 100644 app/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt create mode 100644 app/src/main/res/layout/fragment_json.xml (limited to 'app/src') diff --git a/app/src/main/java/net/taler/wallet/WalletViewModel.kt b/app/src/main/java/net/taler/wallet/WalletViewModel.kt index f932cff..bc8c7e2 100644 --- a/app/src/main/java/net/taler/wallet/WalletViewModel.kt +++ b/app/src/main/java/net/taler/wallet/WalletViewModel.kt @@ -29,6 +29,7 @@ import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onStart import net.taler.wallet.backend.WalletBackendApi import net.taler.wallet.history.History +import net.taler.wallet.history.HistoryEvent import org.json.JSONObject const val TAG = "taler-wallet" @@ -214,7 +215,13 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) { close() return@sendRequest } - val history: History = mapper.readValue(result.getString("history")) + val history = History() + val json = result.getJSONArray("history") + for (i in 0 until json.length()) { + val event: HistoryEvent = mapper.readValue(json.getString(i)) + event.json = json.getJSONObject(i) + history.add(event) + } history.reverse() // show latest first mHistoryProgress.postValue(false) offer(if (showAll) history else history.filter { it.showToUser } as History) diff --git a/app/src/main/java/net/taler/wallet/history/HistoryEvent.kt b/app/src/main/java/net/taler/wallet/history/HistoryEvent.kt index d866c72..e2a7c7e 100644 --- a/app/src/main/java/net/taler/wallet/history/HistoryEvent.kt +++ b/app/src/main/java/net/taler/wallet/history/HistoryEvent.kt @@ -26,6 +26,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME import net.taler.wallet.ParsedAmount.Companion.parseAmount import net.taler.wallet.R +import org.json.JSONObject enum class ReserveType { /** @@ -130,7 +131,9 @@ abstract class HistoryEvent( @get:DrawableRes open val icon: Int = R.drawable.ic_account_balance, open val showToUser: Boolean = false -) +) { + open lateinit var json: JSONObject +} @JsonTypeName("exchange-added") diff --git a/app/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt b/app/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt new file mode 100644 index 0000000..a9ed514 --- /dev/null +++ b/app/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt @@ -0,0 +1,50 @@ +/* + This file is part of GNU Taler + (C) 2019 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 + */ + +package net.taler.wallet.history + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import kotlinx.android.synthetic.main.fragment_json.* +import net.taler.wallet.R + +class JsonDialogFragment : DialogFragment() { + + companion object { + fun new(json: String): JsonDialogFragment { + return JsonDialogFragment().apply { + arguments = Bundle().apply { putString("json", json) } + } + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_json, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val json = arguments!!.getString("json") + jsonView.text = json + } + +} diff --git a/app/src/main/java/net/taler/wallet/history/WalletHistory.kt b/app/src/main/java/net/taler/wallet/history/WalletHistory.kt index 0b2e214..5652e66 100644 --- a/app/src/main/java/net/taler/wallet/history/WalletHistory.kt +++ b/app/src/main/java/net/taler/wallet/history/WalletHistory.kt @@ -30,15 +30,19 @@ import kotlinx.android.synthetic.main.fragment_show_history.* import net.taler.wallet.R import net.taler.wallet.WalletViewModel +interface OnEventClickListener { + fun onEventClicked(event: HistoryEvent) +} + /** * Wallet history. * */ -class WalletHistory : Fragment() { +class WalletHistory : Fragment(), OnEventClickListener { lateinit var model: WalletViewModel private lateinit var showAllItem: MenuItem - private val historyAdapter = WalletHistoryAdapter() + private val historyAdapter = WalletHistoryAdapter(this) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -98,6 +102,11 @@ class WalletHistory : Fragment() { if (savedInstanceState == null) model.historyShowAll.value = false } + override fun onEventClicked(event: HistoryEvent) { + JsonDialogFragment.new(event.json.toString(4)) + .show(parentFragmentManager, null) + } + companion object { const val TAG = "taler-wallet" } diff --git a/app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt b/app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt index 7bb8b33..7df8d0c 100644 --- a/app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt +++ b/app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt @@ -29,13 +29,16 @@ import androidx.annotation.CallSuper import androidx.core.net.toUri import androidx.recyclerview.widget.RecyclerView.Adapter import androidx.recyclerview.widget.RecyclerView.ViewHolder +import net.taler.wallet.BuildConfig import net.taler.wallet.ParsedAmount import net.taler.wallet.ParsedAmount.Companion.parseAmount import net.taler.wallet.R -internal class WalletHistoryAdapter(private var history: History = History()) : - Adapter() { +internal class WalletHistoryAdapter( + private val listener: OnEventClickListener, + private var history: History = History() +) : Adapter() { init { setHasStableIds(false) @@ -64,158 +67,163 @@ internal class WalletHistoryAdapter(private var history: History = History()) : this.notifyDataSetChanged() } -} - -internal abstract class HistoryEventViewHolder(protected val v: View) : ViewHolder(v) { + internal abstract inner class HistoryEventViewHolder(protected val v: View) : ViewHolder(v) { - 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) + 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) { - icon.setImageResource(event.icon) - if (event.title == 0) title.text = event::class.java.simpleName - else title.setText(event.title) - time.text = getRelativeTime(event.timestamp.ms) - } + @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 + } + icon.setImageResource(event.icon) + if (event.title == 0) title.text = event::class.java.simpleName + else title.setText(event.title) + time.text = getRelativeTime(event.timestamp.ms) + } - private fun getRelativeTime(timestamp: Long): CharSequence { - val now = System.currentTimeMillis() - return if (now - timestamp > DAY_IN_MILLIS * 2) { - formatDateTime( - v.context, - timestamp, - FORMAT_SHOW_TIME or FORMAT_SHOW_DATE or FORMAT_ABBREV_MONTH or FORMAT_NO_YEAR - ) - } else { - getRelativeTimeSpanString(timestamp, now, MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE) + private fun getRelativeTime(timestamp: Long): CharSequence { + val now = System.currentTimeMillis() + return if (now - timestamp > DAY_IN_MILLIS * 2) { + formatDateTime( + v.context, + timestamp, + FORMAT_SHOW_TIME or FORMAT_SHOW_DATE or FORMAT_ABBREV_MONTH or FORMAT_NO_YEAR + ) + } else { + getRelativeTimeSpanString(timestamp, now, MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE) + } } + } -} + internal inner class GenericHistoryEventViewHolder(v: View) : HistoryEventViewHolder(v) { -internal class GenericHistoryEventViewHolder(v: View) : HistoryEventViewHolder(v) { - - private val info: TextView = v.findViewById(R.id.info) - - override fun bind(event: HistoryEvent) { - super.bind(event) - info.text = when (event) { - is ExchangeAddedEvent -> event.exchangeBaseUrl - is ExchangeUpdatedEvent -> event.exchangeBaseUrl - is ReserveBalanceUpdatedEvent -> parseAmount(event.amountReserveBalance).toString() - is HistoryPaymentSentEvent -> event.orderShortInfo.summary - is HistoryOrderAcceptedEvent -> event.orderShortInfo.summary - is HistoryOrderRefusedEvent -> event.orderShortInfo.summary - is HistoryOrderRedirectedEvent -> event.newOrderShortInfo.summary - else -> "" + private val info: TextView = v.findViewById(R.id.info) + + override fun bind(event: HistoryEvent) { + super.bind(event) + info.text = when (event) { + is ExchangeAddedEvent -> event.exchangeBaseUrl + is ExchangeUpdatedEvent -> event.exchangeBaseUrl + is ReserveBalanceUpdatedEvent -> parseAmount(event.amountReserveBalance).toString() + is HistoryPaymentSentEvent -> event.orderShortInfo.summary + is HistoryOrderAcceptedEvent -> event.orderShortInfo.summary + is HistoryOrderRefusedEvent -> event.orderShortInfo.summary + is HistoryOrderRedirectedEvent -> event.newOrderShortInfo.summary + else -> "" + } } - } -} + } -internal class HistoryReceiveViewHolder(v: View) : HistoryEventViewHolder(v) { + internal inner class HistoryReceiveViewHolder(v: View) : HistoryEventViewHolder(v) { - 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 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) - override fun bind(event: HistoryEvent) { - super.bind(event) - when(event) { - is HistoryWithdrawnEvent -> bind(event) - is HistoryRefundedEvent -> bind(event) - is HistoryTipAcceptedEvent -> bind(event) - is HistoryTipDeclinedEvent -> bind(event) + override fun bind(event: HistoryEvent) { + super.bind(event) + when (event) { + is HistoryWithdrawnEvent -> bind(event) + is HistoryRefundedEvent -> bind(event) + is HistoryTipAcceptedEvent -> bind(event) + is HistoryTipDeclinedEvent -> bind(event) + } } - } - private fun bind(event: HistoryWithdrawnEvent) { - title.text = getHostname(event.exchangeBaseUrl) - summary.setText(event.title) + private fun bind(event: HistoryWithdrawnEvent) { + title.text = getHostname(event.exchangeBaseUrl) + summary.setText(event.title) - val parsedEffective = parseAmount(event.amountWithdrawnEffective) - val parsedRaw = parseAmount(event.amountWithdrawnRaw) - showAmounts(parsedEffective, parsedRaw) - } + val parsedEffective = parseAmount(event.amountWithdrawnEffective) + val parsedRaw = parseAmount(event.amountWithdrawnRaw) + showAmounts(parsedEffective, parsedRaw) + } - private fun bind(event: HistoryRefundedEvent) { - title.text = event.orderShortInfo.summary - summary.setText(event.title) + private fun bind(event: HistoryRefundedEvent) { + title.text = event.orderShortInfo.summary + summary.setText(event.title) - val parsedEffective = parseAmount(event.amountRefundedEffective) - val parsedRaw = parseAmount(event.amountRefundedRaw) - showAmounts(parsedEffective, parsedRaw) - } + val parsedEffective = parseAmount(event.amountRefundedEffective) + val parsedRaw = parseAmount(event.amountRefundedRaw) + showAmounts(parsedEffective, parsedRaw) + } - private fun bind(event: HistoryTipAcceptedEvent) { - title.setText(event.title) - summary.text = null - val amount = parseAmount(event.tipRaw) - showAmounts(amount, amount) - } + private fun bind(event: HistoryTipAcceptedEvent) { + title.setText(event.title) + summary.text = null + val amount = parseAmount(event.tipRaw) + showAmounts(amount, amount) + } - private fun bind(event: HistoryTipDeclinedEvent) { - title.setText(event.title) - summary.text = null - val amount = parseAmount(event.tipAmount) - showAmounts(amount, amount) - amountWithdrawn.paintFlags = amountWithdrawn.paintFlags or STRIKE_THRU_TEXT_FLAG - } + private fun bind(event: HistoryTipDeclinedEvent) { + title.setText(event.title) + summary.text = null + val amount = parseAmount(event.tipAmount) + showAmounts(amount, amount) + amountWithdrawn.paintFlags = amountWithdrawn.paintFlags or STRIKE_THRU_TEXT_FLAG + } - private fun showAmounts(effective: ParsedAmount, raw: ParsedAmount) { - amountWithdrawn.text = "+$raw" - val calculatedFee = raw - effective - if (calculatedFee.isZero()) { - fee.visibility = GONE - feeLabel.visibility = GONE - } else { - fee.text = "-$calculatedFee" - fee.visibility = VISIBLE - feeLabel.visibility = VISIBLE + private fun showAmounts(effective: ParsedAmount, raw: ParsedAmount) { + amountWithdrawn.text = "+$raw" + val calculatedFee = raw - effective + if (calculatedFee.isZero()) { + fee.visibility = GONE + feeLabel.visibility = GONE + } else { + fee.text = "-$calculatedFee" + fee.visibility = VISIBLE + feeLabel.visibility = VISIBLE + } + amountWithdrawn.paintFlags = fee.paintFlags + } + + private fun getHostname(url: String): String { + return url.toUri().host!! } - amountWithdrawn.paintFlags = fee.paintFlags - } - private fun getHostname(url: String): String { - return url.toUri().host!! } -} + internal inner class HistoryPaymentViewHolder(v: View) : HistoryEventViewHolder(v) { -internal class HistoryPaymentViewHolder(v: View) : HistoryEventViewHolder(v) { + private val summary: TextView = v.findViewById(R.id.summary) + private val amountPaidWithFees: TextView = v.findViewById(R.id.amountPaidWithFees) - private val summary: TextView = v.findViewById(R.id.summary) - private val amountPaidWithFees: TextView = v.findViewById(R.id.amountPaidWithFees) + override fun bind(event: HistoryEvent) { + super.bind(event) + summary.setText(event.title) + when (event) { + is HistoryPaymentSentEvent -> bind(event) + is HistoryPaymentAbortedEvent -> bind(event) + is HistoryRefreshedEvent -> bind(event) + } + } - override fun bind(event: HistoryEvent) { - super.bind(event) - summary.setText(event.title) - when(event) { - is HistoryPaymentSentEvent -> bind(event) - is HistoryPaymentAbortedEvent -> bind(event) - is HistoryRefreshedEvent -> bind(event) + private fun bind(event: HistoryPaymentSentEvent) { + title.text = event.orderShortInfo.summary + amountPaidWithFees.text = "-${parseAmount(event.amountPaidWithFees)}" } - } - private fun bind(event: HistoryPaymentSentEvent) { - title.text = event.orderShortInfo.summary - amountPaidWithFees.text = "-${parseAmount(event.amountPaidWithFees)}" - } + private fun bind(event: HistoryPaymentAbortedEvent) { + title.text = event.orderShortInfo.summary + amountPaidWithFees.text = "-${parseAmount(event.amountLost)}" + } - private fun bind(event: HistoryPaymentAbortedEvent) { - title.text = event.orderShortInfo.summary - amountPaidWithFees.text = "-${parseAmount(event.amountLost)}" - } + private fun bind(event: HistoryRefreshedEvent) { + title.text = "" + val fee = + parseAmount(event.amountRefreshedRaw) - parseAmount(event.amountRefreshedEffective) + if (fee.isZero()) amountPaidWithFees.text = null + else amountPaidWithFees.text = "-$fee" + } - private fun bind(event: HistoryRefreshedEvent) { - title.text = "" - val fee = - parseAmount(event.amountRefreshedRaw) - parseAmount(event.amountRefreshedEffective) - if (fee.isZero()) amountPaidWithFees.text = null - else amountPaidWithFees.text = "-$fee" } } diff --git a/app/src/main/res/layout/fragment_json.xml b/app/src/main/res/layout/fragment_json.xml new file mode 100644 index 0000000..fe40156 --- /dev/null +++ b/app/src/main/res/layout/fragment_json.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/app/src/main/res/layout/history_payment.xml b/app/src/main/res/layout/history_payment.xml index cf03998..62bfa79 100644 --- a/app/src/main/res/layout/history_payment.xml +++ b/app/src/main/res/layout/history_payment.xml @@ -7,6 +7,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginEnd="16dp" + android:background="?android:selectableItemBackground" android:layout_marginBottom="8dp"> diff --git a/app/src/main/res/layout/history_row.xml b/app/src/main/res/layout/history_row.xml index 394ad79..51647ee 100644 --- a/app/src/main/res/layout/history_row.xml +++ b/app/src/main/res/layout/history_row.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="?android:selectableItemBackground" android:layout_margin="15dp">