aboutsummaryrefslogtreecommitdiff
path: root/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'wallet')
-rw-r--r--wallet/src/main/java/net/taler/wallet/MainActivity.kt5
-rw-r--r--wallet/src/main/java/net/taler/wallet/MainViewModel.kt19
-rw-r--r--wallet/src/main/java/net/taler/wallet/history/DevHistoryAdapter.kt133
-rw-r--r--wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt87
-rw-r--r--wallet/src/main/java/net/taler/wallet/history/DevHistoryManager.kt78
-rw-r--r--wallet/src/main/java/net/taler/wallet/history/HistoryEvent.kt (renamed from wallet/src/main/java/net/taler/wallet/transactions/Transaction.kt)115
-rw-r--r--wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt (renamed from wallet/src/main/java/net/taler/wallet/transactions/JsonDialogFragment.kt)2
-rw-r--r--wallet/src/main/java/net/taler/wallet/history/ReserveTransaction.kt (renamed from wallet/src/main/java/net/taler/wallet/transactions/ReserveTransaction.kt)2
-rw-r--r--wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt38
-rw-r--r--wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt17
-rw-r--r--wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt72
-rw-r--r--wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt16
-rw-r--r--wallet/src/main/res/drawable/ic_history.xml9
-rw-r--r--wallet/src/main/res/layout/fragment_transactions.xml2
-rw-r--r--wallet/src/main/res/layout/list_item_history.xml (renamed from wallet/src/main/res/layout/list_item_transaction.xml)0
-rw-r--r--wallet/src/main/res/menu/activity_main_drawer.xml27
-rw-r--r--wallet/src/main/res/navigation/nav_graph.xml10
-rw-r--r--wallet/src/main/res/values/strings.xml1
-rw-r--r--wallet/src/test/java/net/taler/wallet/history/HistoryEventTest.kt (renamed from wallet/src/test/java/net/taler/wallet/transactions/TransactionTest.kt)54
-rw-r--r--wallet/src/test/java/net/taler/wallet/history/ReserveHistoryEventTest.kt (renamed from wallet/src/test/java/net/taler/wallet/transactions/ReserveTransactionTest.kt)5
20 files changed, 539 insertions, 153 deletions
diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
index b6e9a7a..a6385a9 100644
--- a/wallet/src/main/java/net/taler/wallet/MainActivity.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
@@ -75,7 +75,7 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener {
setSupportActionBar(toolbar)
val appBarConfiguration = AppBarConfiguration(
- setOf(R.id.nav_main, R.id.nav_settings, R.id.nav_pending_operations),
+ setOf(R.id.nav_main, R.id.nav_settings, R.id.nav_pending_operations, R.id.nav_history),
drawer_layout
)
toolbar.setupWithNavController(nav, appBarConfiguration)
@@ -86,7 +86,7 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener {
val versionView: TextView = nav_view.getHeaderView(0).findViewById(R.id.versionView)
model.devMode.observe(this, Observer { enabled ->
- nav_view.menu.findItem(R.id.nav_pending_operations).isVisible = enabled
+ nav_view.menu.findItem(R.id.nav_dev).isVisible = enabled
if (enabled) {
@SuppressLint("SetTextI18n")
versionView.text = "$VERSION_NAME ($VERSION_CODE)"
@@ -116,6 +116,7 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener {
R.id.nav_home -> nav.navigate(R.id.nav_main)
R.id.nav_settings -> nav.navigate(R.id.nav_settings)
R.id.nav_pending_operations -> nav.navigate(R.id.nav_pending_operations)
+ R.id.nav_history -> nav.navigate(R.id.nav_history)
}
drawer_layout.closeDrawer(START)
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 230c310..b880036 100644
--- a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
@@ -23,11 +23,13 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.distinctUntilChanged
+import androidx.lifecycle.viewModelScope
import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import net.taler.common.Amount
import net.taler.wallet.backend.WalletBackendApi
+import net.taler.wallet.history.DevHistoryManager
import net.taler.wallet.payment.PaymentManager
import net.taler.wallet.pending.PendingOperationsManager
import net.taler.wallet.refund.RefundManager
@@ -70,13 +72,13 @@ class MainViewModel(val app: Application) : AndroidViewModel(app) {
loadBalances()
if (payload.optString("type") in transactionNotifications) {
// update transaction list
- // TODO do this in a better way
- transactionManager.showAll.value?.let {
- transactionManager.showAll.postValue(it)
- }
+ transactionManager.loadTransactions()
+ }
+ // refresh pending ops and history with each notification
+ if (devMode.value == true) {
+ pendingOperationsManager.getPending()
+ historyManager.loadHistory()
}
- // refresh pending ops with each notification
- if (devMode.value == true) pendingOperationsManager.getPending()
}
}
@@ -88,7 +90,10 @@ class MainViewModel(val app: Application) : AndroidViewModel(app) {
val paymentManager = PaymentManager(walletBackendApi, mapper)
val pendingOperationsManager: PendingOperationsManager =
PendingOperationsManager(walletBackendApi)
- val transactionManager: TransactionManager = TransactionManager(walletBackendApi, mapper)
+ val historyManager: DevHistoryManager =
+ DevHistoryManager(walletBackendApi, viewModelScope, mapper)
+ val transactionManager: TransactionManager =
+ TransactionManager(walletBackendApi, viewModelScope, mapper)
val refundManager = RefundManager(walletBackendApi)
override fun onCleared() {
diff --git a/wallet/src/main/java/net/taler/wallet/history/DevHistoryAdapter.kt b/wallet/src/main/java/net/taler/wallet/history/DevHistoryAdapter.kt
new file mode 100644
index 0000000..88db90c
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/history/DevHistoryAdapter.kt
@@ -0,0 +1,133 @@
+/*
+ * 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.content.Context
+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.recyclerview.widget.RecyclerView.Adapter
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import net.taler.common.exhaustive
+import net.taler.common.toRelativeTime
+import net.taler.wallet.R
+import net.taler.wallet.history.DevHistoryAdapter.HistoryViewHolder
+
+@Deprecated("Replaced by TransactionAdapter")
+internal class DevHistoryAdapter(
+ private val listener: OnEventClickListener,
+ private var history: History = History()
+) : Adapter<HistoryViewHolder>() {
+
+ init {
+ setHasStableIds(false)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HistoryViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.list_item_history, parent, false)
+ return HistoryViewHolder(view)
+ }
+
+ override fun getItemCount(): Int = history.size
+
+ override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) {
+ val transaction = history[position]
+ holder.bind(transaction)
+ }
+
+ fun update(updatedHistory: History) {
+ this.history = updatedHistory
+ this.notifyDataSetChanged()
+ }
+
+ internal open inner class HistoryViewHolder(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)
+ private val amount: TextView = v.findViewById(R.id.amount)
+
+ private val amountColor = amount.currentTextColor
+
+ open fun bind(historyEvent: HistoryEvent) {
+ v.setOnClickListener { listener.onTransactionClicked(historyEvent) }
+ icon.setImageResource(historyEvent.icon)
+
+ title.text = if (historyEvent.title == null) {
+ when (historyEvent) {
+ is RefreshHistoryEvent -> getRefreshTitle(historyEvent)
+ is OrderAcceptedHistoryEvent -> context.getString(R.string.transaction_order_accepted)
+ is OrderRefusedHistoryEvent -> context.getString(R.string.transaction_order_refused)
+ is TipAcceptedHistoryEvent -> context.getString(R.string.transaction_tip_accepted)
+ is TipDeclinedHistoryEvent -> context.getString(R.string.transaction_tip_declined)
+ is ReserveBalanceUpdatedHistoryEvent -> context.getString(R.string.transaction_reserve_balance_updated)
+ else -> historyEvent::class.java.simpleName
+ }
+ } else historyEvent.title
+
+ time.text = historyEvent.timestamp.ms.toRelativeTime(context)
+ bindAmount(historyEvent.displayAmount)
+ }
+
+ private fun bindAmount(displayAmount: DisplayAmount?) {
+ if (displayAmount == null) {
+ amount.visibility = GONE
+ } else {
+ amount.visibility = VISIBLE
+ when (displayAmount.type) {
+ AmountType.Positive -> {
+ amount.text = context.getString(
+ R.string.amount_positive, displayAmount.amount.amountStr
+ )
+ amount.setTextColor(context.getColor(R.color.green))
+ }
+ AmountType.Negative -> {
+ amount.text = context.getString(
+ R.string.amount_negative, displayAmount.amount.amountStr
+ )
+ amount.setTextColor(context.getColor(R.color.red))
+ }
+ AmountType.Neutral -> {
+ amount.text = displayAmount.amount.amountStr
+ amount.setTextColor(amountColor)
+ }
+ }.exhaustive
+ }
+ }
+
+ private fun getRefreshTitle(transaction: RefreshHistoryEvent): String {
+ val res = when (transaction.refreshReason) {
+ RefreshReason.MANUAL -> R.string.transaction_refresh_reason_manual
+ RefreshReason.PAY -> R.string.transaction_refresh_reason_pay
+ RefreshReason.REFUND -> R.string.transaction_refresh_reason_refund
+ RefreshReason.ABORT_PAY -> R.string.transaction_refresh_reason_abort_pay
+ RefreshReason.RECOUP -> R.string.transaction_refresh_reason_recoup
+ RefreshReason.BACKUP_RESTORED -> R.string.transaction_refresh_reason_backup_restored
+ }
+ return context.getString(R.string.transaction_refresh) + " " + context.getString(res)
+ }
+
+ }
+
+}
diff --git a/wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt b/wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt
new file mode 100644
index 0000000..c3c07a3
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.View
+import android.view.View.INVISIBLE
+import android.view.View.VISIBLE
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL
+import kotlinx.android.synthetic.main.fragment_transactions.*
+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 {
+
+ private val model: MainViewModel by activityViewModels()
+ private val historyManager by lazy { model.historyManager }
+ private val historyAdapter by lazy { DevHistoryAdapter(this) }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.fragment_transactions, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ if (savedInstanceState == null) historyManager.loadHistory()
+
+ list.apply {
+ adapter = historyAdapter
+ addItemDecoration(DividerItemDecoration(context, VERTICAL))
+ }
+ historyManager.progress.observe(viewLifecycleOwner, Observer { show ->
+ progressBar.visibility = if (show) VISIBLE else INVISIBLE
+ })
+ historyManager.history.observe(viewLifecycleOwner, Observer { result ->
+ onHistoryResult(result)
+ })
+ }
+
+ 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)
+ emptyState.fadeIn()
+ }
+ is HistoryResult.Success -> {
+ emptyState.visibility = if (result.history.isEmpty()) VISIBLE else INVISIBLE
+ historyAdapter.update(result.history)
+ list.fadeIn()
+ }
+ }
+
+}
diff --git a/wallet/src/main/java/net/taler/wallet/history/DevHistoryManager.kt b/wallet/src/main/java/net/taler/wallet/history/DevHistoryManager.kt
new file mode 100644
index 0000000..72967b2
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/history/DevHistoryManager.kt
@@ -0,0 +1,78 @@
+/*
+ * 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 androidx.annotation.UiThread
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.kotlin.readValue
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import net.taler.wallet.backend.WalletBackendApi
+import org.json.JSONObject
+
+sealed class HistoryResult {
+ object Error : HistoryResult()
+ class Success(val history: History) : HistoryResult()
+}
+
+class DevHistoryManager(
+ private val walletBackendApi: WalletBackendApi,
+ private val scope: CoroutineScope,
+ private val mapper: ObjectMapper
+) {
+
+ private val mProgress = MutableLiveData<Boolean>()
+ val progress: LiveData<Boolean> = mProgress
+
+ private val mHistory = MutableLiveData<HistoryResult>()
+ val history: LiveData<HistoryResult> = mHistory
+
+ @UiThread
+ internal fun loadHistory() {
+ mProgress.value = true
+ walletBackendApi.sendRequest("getHistory", null) { isError, result ->
+ scope.launch(Dispatchers.Default) {
+ onEventsLoaded(isError, result)
+ }
+ }
+ }
+
+ private fun onEventsLoaded(isError: Boolean, result: JSONObject) {
+ if (isError) {
+ mHistory.postValue(HistoryResult.Error)
+ return
+ }
+ 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
+ mProgress.postValue(false)
+ mHistory.postValue(
+ HistoryResult.Success(
+ history
+ )
+ )
+ }
+
+}
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transaction.kt b/wallet/src/main/java/net/taler/wallet/history/HistoryEvent.kt
index 34942d0..acca679 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/Transaction.kt
+++ b/wallet/src/main/java/net/taler/wallet/history/HistoryEvent.kt
@@ -14,7 +14,7 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.wallet.transactions
+package net.taler.wallet.history
import androidx.annotation.DrawableRes
import androidx.annotation.LayoutRes
@@ -103,13 +103,13 @@ class DisplayAmount(
val type: AmountType
)
-typealias Transactions = ArrayList<Transaction>
+typealias History = ArrayList<HistoryEvent>
@JsonTypeInfo(
use = NAME,
include = PROPERTY,
property = "type",
- defaultImpl = UnknownTransaction::class
+ defaultImpl = UnknownHistoryEvent::class
)
/** missing:
AuditorComplaintSent = "auditor-complained-sent",
@@ -127,19 +127,19 @@ ReserveCreated = "reserve-created",
@JsonSubTypes(
Type(value = ExchangeAddedEvent::class, name = "exchange-added"),
Type(value = ExchangeUpdatedEvent::class, name = "exchange-updated"),
- Type(value = ReserveBalanceUpdatedTransaction::class, name = "reserve-balance-updated"),
- Type(value = WithdrawTransaction::class, name = "withdrawn"),
- Type(value = OrderAcceptedTransaction::class, name = "order-accepted"),
- Type(value = OrderRefusedTransaction::class, name = "order-refused"),
- Type(value = OrderRedirectedTransaction::class, name = "order-redirected"),
- Type(value = PaymentTransaction::class, name = "payment-sent"),
- Type(value = PaymentAbortedTransaction::class, name = "payment-aborted"),
- Type(value = TipAcceptedTransaction::class, name = "tip-accepted"),
- Type(value = TipDeclinedTransaction::class, name = "tip-declined"),
- Type(value = RefundTransaction::class, name = "refund"),
- Type(value = RefreshTransaction::class, name = "refreshed")
+ Type(value = ReserveBalanceUpdatedHistoryEvent::class, name = "reserve-balance-updated"),
+ Type(value = WithdrawHistoryEvent::class, name = "withdrawn"),
+ Type(value = OrderAcceptedHistoryEvent::class, name = "order-accepted"),
+ Type(value = OrderRefusedHistoryEvent::class, name = "order-refused"),
+ Type(value = OrderRedirectedHistoryEvent::class, name = "order-redirected"),
+ Type(value = PaymentHistoryEvent::class, name = "payment-sent"),
+ Type(value = PaymentAbortedHistoryEvent::class, name = "payment-aborted"),
+ Type(value = TipAcceptedHistoryEvent::class, name = "tip-accepted"),
+ Type(value = TipDeclinedHistoryEvent::class, name = "tip-declined"),
+ Type(value = RefundHistoryEvent::class, name = "refund"),
+ Type(value = RefreshHistoryEvent::class, name = "refreshed")
)
-abstract class Transaction(
+abstract class HistoryEvent(
val timestamp: Timestamp,
val eventId: String,
@get:LayoutRes
@@ -155,7 +155,7 @@ abstract class Transaction(
}
-class UnknownTransaction(timestamp: Timestamp, eventId: String) : Transaction(timestamp, eventId) {
+class UnknownHistoryEvent(timestamp: Timestamp, eventId: String) : HistoryEvent(timestamp, eventId) {
override val title: String? = null
}
@@ -165,7 +165,7 @@ class ExchangeAddedEvent(
eventId: String,
val exchangeBaseUrl: String,
val builtIn: Boolean
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val title = cleanExchange(exchangeBaseUrl)
}
@@ -174,13 +174,13 @@ class ExchangeUpdatedEvent(
timestamp: Timestamp,
eventId: String,
val exchangeBaseUrl: String
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val title = cleanExchange(exchangeBaseUrl)
}
@JsonTypeName("reserve-balance-updated")
-class ReserveBalanceUpdatedTransaction(
+class ReserveBalanceUpdatedHistoryEvent(
timestamp: Timestamp,
eventId: String,
/**
@@ -200,14 +200,17 @@ class ReserveBalanceUpdatedTransaction(
* Amount that hasn't been withdrawn yet.
*/
val reserveUnclaimedAmount: Amount
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val title: String? = null
- override val displayAmount = DisplayAmount(reserveBalance, AmountType.Neutral)
+ override val displayAmount = DisplayAmount(
+ reserveBalance,
+ AmountType.Neutral
+ )
override fun isCurrency(currency: String) = reserveBalance.currency == currency
}
@JsonTypeName("withdrawn")
-class WithdrawTransaction(
+class WithdrawHistoryEvent(
timestamp: Timestamp,
eventId: String,
/**
@@ -229,45 +232,48 @@ class WithdrawTransaction(
* Amount that actually was added to the wallet's balance.
*/
val amountWithdrawnEffective: Amount
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val detailPageLayout = R.layout.fragment_event_withdraw
override val title = cleanExchange(exchangeBaseUrl)
override val icon = R.drawable.transaction_withdrawal
override val showToUser = true
- override val displayAmount = DisplayAmount(amountWithdrawnEffective, AmountType.Positive)
+ override val displayAmount = DisplayAmount(
+ amountWithdrawnEffective,
+ AmountType.Positive
+ )
override fun isCurrency(currency: String) = amountWithdrawnRaw.currency == currency
}
@JsonTypeName("order-accepted")
-class OrderAcceptedTransaction(
+class OrderAcceptedHistoryEvent(
timestamp: Timestamp,
eventId: String,
/**
* Condensed info about the order.
*/
val orderShortInfo: OrderShortInfo
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val icon = R.drawable.ic_add_circle
override val title: String? = null
override fun isCurrency(currency: String) = orderShortInfo.amount.currency == currency
}
@JsonTypeName("order-refused")
-class OrderRefusedTransaction(
+class OrderRefusedHistoryEvent(
timestamp: Timestamp,
eventId: String,
/**
* Condensed info about the order.
*/
val orderShortInfo: OrderShortInfo
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val icon = R.drawable.ic_cancel
override val title: String? = null
override fun isCurrency(currency: String) = orderShortInfo.amount.currency == currency
}
@JsonTypeName("payment-sent")
-class PaymentTransaction(
+class PaymentHistoryEvent(
timestamp: Timestamp,
eventId: String,
/**
@@ -291,17 +297,20 @@ class PaymentTransaction(
* Session ID that the payment was (re-)submitted under.
*/
val sessionId: String?
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val detailPageLayout = R.layout.fragment_event_paid
override val title = orderShortInfo.summary
override val icon = R.drawable.ic_cash_usd_outline
override val showToUser = true
- override val displayAmount = DisplayAmount(amountPaidWithFees, AmountType.Negative)
+ override val displayAmount = DisplayAmount(
+ amountPaidWithFees,
+ AmountType.Negative
+ )
override fun isCurrency(currency: String) = orderShortInfo.amount.currency == currency
}
@JsonTypeName("payment-aborted")
-class PaymentAbortedTransaction(
+class PaymentAbortedHistoryEvent(
timestamp: Timestamp,
eventId: String,
/**
@@ -312,16 +321,19 @@ class PaymentAbortedTransaction(
* Amount that was lost due to refund and refreshing fees.
*/
val amountLost: Amount
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val title = orderShortInfo.summary
override val icon = R.drawable.transaction_payment_aborted
override val showToUser = true
- override val displayAmount = DisplayAmount(amountLost, AmountType.Negative)
+ override val displayAmount = DisplayAmount(
+ amountLost,
+ AmountType.Negative
+ )
override fun isCurrency(currency: String) = orderShortInfo.amount.currency == currency
}
@JsonTypeName("refreshed")
-class RefreshTransaction(
+class RefreshHistoryEvent(
timestamp: Timestamp,
eventId: String,
/**
@@ -345,7 +357,7 @@ class RefreshTransaction(
* more refresh session IDs.
*/
val refreshGroupId: String
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val icon = R.drawable.transaction_refresh
override val title: String? = null
override val showToUser = !(amountRefreshedRaw - amountRefreshedEffective).isZero()
@@ -362,7 +374,7 @@ class RefreshTransaction(
}
@JsonTypeName("order-redirected")
-class OrderRedirectedTransaction(
+class OrderRedirectedHistoryEvent(
timestamp: Timestamp,
eventId: String,
/**
@@ -374,14 +386,14 @@ class OrderRedirectedTransaction(
* Condensed info about the order that we already paid for.
*/
val alreadyPaidOrderShortInfo: OrderShortInfo
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val icon = R.drawable.ic_directions
override val title = newOrderShortInfo.summary
override fun isCurrency(currency: String) = newOrderShortInfo.amount.currency == currency
}
@JsonTypeName("tip-accepted")
-class TipAcceptedTransaction(
+class TipAcceptedHistoryEvent(
timestamp: Timestamp,
eventId: String,
/**
@@ -392,16 +404,19 @@ class TipAcceptedTransaction(
* Raw amount of the tip, without extra fees that apply.
*/
val tipRaw: Amount
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val icon = R.drawable.transaction_tip_accepted
override val title: String? = null
override val showToUser = true
- override val displayAmount = DisplayAmount(tipRaw, AmountType.Positive)
+ override val displayAmount = DisplayAmount(
+ tipRaw,
+ AmountType.Positive
+ )
override fun isCurrency(currency: String) = tipRaw.currency == currency
}
@JsonTypeName("tip-declined")
-class TipDeclinedTransaction(
+class TipDeclinedHistoryEvent(
timestamp: Timestamp,
eventId: String,
/**
@@ -412,16 +427,19 @@ class TipDeclinedTransaction(
* Raw amount of the tip, without extra fees that apply.
*/
val tipAmount: Amount
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val icon = R.drawable.transaction_tip_declined
override val title: String? = null
override val showToUser = true
- override val displayAmount = DisplayAmount(tipAmount, AmountType.Neutral)
+ override val displayAmount = DisplayAmount(
+ tipAmount,
+ AmountType.Neutral
+ )
override fun isCurrency(currency: String) = tipAmount.currency == currency
}
@JsonTypeName("refund")
-class RefundTransaction(
+class RefundHistoryEvent(
timestamp: Timestamp,
eventId: String,
val orderShortInfo: OrderShortInfo,
@@ -443,12 +461,15 @@ class RefundTransaction(
* Amount will be added to the wallet's balance after fees and refreshing.
*/
val amountRefundedEffective: Amount
-) : Transaction(timestamp, eventId) {
+) : HistoryEvent(timestamp, eventId) {
override val icon = R.drawable.transaction_refund
override val title = orderShortInfo.summary
override val detailPageLayout = R.layout.fragment_event_paid
override val showToUser = true
- override val displayAmount = DisplayAmount(amountRefundedEffective, AmountType.Positive)
+ override val displayAmount = DisplayAmount(
+ amountRefundedEffective,
+ AmountType.Positive
+ )
override fun isCurrency(currency: String) = amountRefundedRaw.currency == currency
}
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/JsonDialogFragment.kt b/wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt
index 2337059..31c2b93 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/JsonDialogFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt
@@ -14,7 +14,7 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.wallet.transactions
+package net.taler.wallet.history
import android.os.Bundle
import android.view.LayoutInflater
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/ReserveTransaction.kt b/wallet/src/main/java/net/taler/wallet/history/ReserveTransaction.kt
index e497e9a..6c8fdaa 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/ReserveTransaction.kt
+++ b/wallet/src/main/java/net/taler/wallet/history/ReserveTransaction.kt
@@ -14,7 +14,7 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.wallet.transactions
+package net.taler.wallet.history
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.annotation.JsonSubTypes
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
index 440d07f..5aca896 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
@@ -34,13 +34,23 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder
import net.taler.common.exhaustive
import net.taler.common.toRelativeTime
import net.taler.wallet.R
+import net.taler.wallet.history.AmountType
+import net.taler.wallet.history.DisplayAmount
+import net.taler.wallet.history.History
+import net.taler.wallet.history.HistoryEvent
+import net.taler.wallet.history.OrderAcceptedHistoryEvent
+import net.taler.wallet.history.OrderRefusedHistoryEvent
+import net.taler.wallet.history.RefreshHistoryEvent
+import net.taler.wallet.history.RefreshReason
+import net.taler.wallet.history.ReserveBalanceUpdatedHistoryEvent
+import net.taler.wallet.history.TipAcceptedHistoryEvent
+import net.taler.wallet.history.TipDeclinedHistoryEvent
import net.taler.wallet.transactions.TransactionAdapter.TransactionViewHolder
internal class TransactionAdapter(
- private val devMode: Boolean,
- private val listener: OnEventClickListener,
- private var transactions: Transactions = Transactions()
+ private val listener: OnTransactionClickListener,
+ private var transactions: History = History()
) : Adapter<TransactionViewHolder>() {
lateinit var tracker: SelectionTracker<String>
@@ -52,7 +62,7 @@ internal class TransactionAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionViewHolder {
val view = LayoutInflater.from(parent.context)
- .inflate(R.layout.list_item_transaction, parent, false)
+ .inflate(R.layout.list_item_history, parent, false)
return TransactionViewHolder(view)
}
@@ -63,7 +73,7 @@ internal class TransactionAdapter(
holder.bind(transaction, tracker.isSelected(transaction.eventId))
}
- fun update(updatedTransactions: Transactions) {
+ fun update(updatedTransactions: History) {
this.transactions = updatedTransactions
this.notifyDataSetChanged()
}
@@ -84,8 +94,8 @@ internal class TransactionAdapter(
private val selectableForeground = v.foreground
private val amountColor = amount.currentTextColor
- open fun bind(transaction: Transaction, selected: Boolean) {
- if (devMode || transaction.detailPageLayout != 0) {
+ open fun bind(transaction: HistoryEvent, selected: Boolean) {
+ if (transaction.detailPageLayout != 0) {
v.foreground = selectableForeground
v.setOnClickListener { listener.onTransactionClicked(transaction) }
} else {
@@ -97,12 +107,12 @@ internal class TransactionAdapter(
title.text = if (transaction.title == null) {
when (transaction) {
- is RefreshTransaction -> getRefreshTitle(transaction)
- is OrderAcceptedTransaction -> context.getString(R.string.transaction_order_accepted)
- is OrderRefusedTransaction -> context.getString(R.string.transaction_order_refused)
- is TipAcceptedTransaction -> context.getString(R.string.transaction_tip_accepted)
- is TipDeclinedTransaction -> context.getString(R.string.transaction_tip_declined)
- is ReserveBalanceUpdatedTransaction -> context.getString(R.string.transaction_reserve_balance_updated)
+ is RefreshHistoryEvent -> getRefreshTitle(transaction)
+ is OrderAcceptedHistoryEvent -> context.getString(R.string.transaction_order_accepted)
+ is OrderRefusedHistoryEvent -> context.getString(R.string.transaction_order_refused)
+ is TipAcceptedHistoryEvent -> context.getString(R.string.transaction_tip_accepted)
+ is TipDeclinedHistoryEvent -> context.getString(R.string.transaction_tip_declined)
+ is ReserveBalanceUpdatedHistoryEvent -> context.getString(R.string.transaction_reserve_balance_updated)
else -> transaction::class.java.simpleName
}
} else transaction.title
@@ -137,7 +147,7 @@ internal class TransactionAdapter(
}
}
- private fun getRefreshTitle(transaction: RefreshTransaction): String {
+ private fun getRefreshTitle(transaction: RefreshHistoryEvent): String {
val res = when (transaction.refreshReason) {
RefreshReason.MANUAL -> R.string.transaction_refresh_reason_manual
RefreshReason.PAY -> R.string.transaction_refresh_reason_pay
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 909a7bf..bb70b5c 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
@@ -37,6 +37,11 @@ import net.taler.common.toAbsoluteTime
import net.taler.wallet.MainViewModel
import net.taler.wallet.R
import net.taler.wallet.cleanExchange
+import net.taler.wallet.history.JsonDialogFragment
+import net.taler.wallet.history.OrderShortInfo
+import net.taler.wallet.history.PaymentHistoryEvent
+import net.taler.wallet.history.RefundHistoryEvent
+import net.taler.wallet.history.WithdrawHistoryEvent
class TransactionDetailFragment : Fragment() {
@@ -65,9 +70,9 @@ class TransactionDetailFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
timeView.text = event.timestamp.ms.toAbsoluteTime(requireContext())
when (val e = event) {
- is WithdrawTransaction -> bind(e)
- is PaymentTransaction -> bind(e)
- is RefundTransaction -> bind(e)
+ is WithdrawHistoryEvent -> bind(e)
+ is PaymentHistoryEvent -> bind(e)
+ is RefundHistoryEvent -> bind(e)
else -> Toast.makeText(
requireContext(),
"event ${e.javaClass} not implement",
@@ -90,7 +95,7 @@ class TransactionDetailFragment : Fragment() {
}
}
- private fun bind(event: WithdrawTransaction) {
+ private fun bind(event: WithdrawHistoryEvent) {
effectiveAmountLabel.text = getString(R.string.withdraw_total)
effectiveAmountView.text = event.amountWithdrawnEffective.toString()
chosenAmountLabel.text = getString(R.string.amount_chosen)
@@ -101,13 +106,13 @@ class TransactionDetailFragment : Fragment() {
exchangeView.text = cleanExchange(event.exchangeBaseUrl)
}
- private fun bind(event: PaymentTransaction) {
+ private fun bind(event: PaymentHistoryEvent) {
amountPaidWithFeesView.text = event.amountPaidWithFees.toString()
val fee = event.amountPaidWithFees - event.orderShortInfo.amount
bindOrderAndFee(event.orderShortInfo, fee)
}
- private fun bind(event: RefundTransaction) {
+ private fun bind(event: RefundHistoryEvent) {
amountPaidWithFeesLabel.text = getString(R.string.transaction_refund)
amountPaidWithFeesView.setTextColor(getColor(requireContext(), R.color.green))
amountPaidWithFeesView.text =
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 549b2a8..850a3bb 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
@@ -18,70 +18,62 @@ package net.taler.wallet.transactions
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.asLiveData
-import androidx.lifecycle.switchMap
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.onCompletion
-import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import net.taler.wallet.backend.WalletBackendApi
+import net.taler.wallet.history.History
+import net.taler.wallet.history.HistoryEvent
+import org.json.JSONObject
sealed class TransactionsResult {
object Error : TransactionsResult()
- class Success(val transactions: Transactions) : TransactionsResult()
+ class Success(val transactions: History) : TransactionsResult()
}
-@Suppress("EXPERIMENTAL_API_USAGE")
class TransactionManager(
private val walletBackendApi: WalletBackendApi,
+ private val scope: CoroutineScope,
private val mapper: ObjectMapper
) {
private val mProgress = MutableLiveData<Boolean>()
val progress: LiveData<Boolean> = mProgress
- val showAll = MutableLiveData<Boolean>()
-
var selectedCurrency: String? = null
- var selectedEvent: Transaction? = null
+ var selectedEvent: HistoryEvent? = null
- val transactions: LiveData<TransactionsResult> = showAll.switchMap { showAll ->
- loadTransactions(showAll)
- .onStart { mProgress.postValue(true) }
- .onCompletion { mProgress.postValue(false) }
- .asLiveData(Dispatchers.IO)
- }
+ private val mTransactions = MutableLiveData<TransactionsResult>()
+ val transactions: LiveData<TransactionsResult> = mTransactions
- private fun loadTransactions(showAll: Boolean) = callbackFlow {
+ fun loadTransactions() {
+ mProgress.postValue(true)
walletBackendApi.sendRequest("getHistory", null) { isError, result ->
- launch(Dispatchers.Default) {
- if (isError) {
- offer(TransactionsResult.Error)
- close()
- return@launch
- }
- val transactions = Transactions()
- val json = result.getJSONArray("history")
- val currency = selectedCurrency
- for (i in 0 until json.length()) {
- val event: Transaction = mapper.readValue(json.getString(i))
- event.json = json.getJSONObject(i)
- if (currency == null || event.isCurrency(currency)) {
- transactions.add(event)
- }
- }
- transactions.reverse() // show latest first
- val filtered =
- if (showAll) transactions else transactions.filter { it.showToUser } as Transactions
- offer(TransactionsResult.Success(filtered))
- close()
+ scope.launch(Dispatchers.Default) {
+ onTransactionsLoaded(isError, result)
+ }
+ }
+ }
+
+ private fun onTransactionsLoaded(isError: Boolean, result: JSONObject) {
+ if (isError) {
+ mTransactions.postValue(TransactionsResult.Error)
+ return
+ }
+ val transactions = History()
+ val json = result.getJSONArray("history")
+ val currency = selectedCurrency
+ for (i in 0 until json.length()) {
+ val event: HistoryEvent = mapper.readValue(json.getString(i))
+ if (event.showToUser && (currency == null || event.isCurrency(currency))) {
+ transactions.add(event)
}
}
- awaitClose()
+ transactions.reverse() // show latest first
+ mProgress.postValue(false)
+ mTransactions.postValue(TransactionsResult.Success(transactions))
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
index e7adaf1..2b5337f 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
@@ -42,17 +42,18 @@ import net.taler.common.fadeIn
import net.taler.common.fadeOut
import net.taler.wallet.MainViewModel
import net.taler.wallet.R
+import net.taler.wallet.history.HistoryEvent
-interface OnEventClickListener {
- fun onTransactionClicked(transaction: Transaction)
+interface OnTransactionClickListener {
+ fun onTransactionClicked(transaction: HistoryEvent)
}
-class TransactionsFragment : Fragment(), OnEventClickListener, ActionMode.Callback {
+class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode.Callback {
private val model: MainViewModel by activityViewModels()
private val transactionManager by lazy { model.transactionManager }
- private val transactionAdapter by lazy { TransactionAdapter(model.devMode.value == true, this) }
+ private val transactionAdapter by lazy { TransactionAdapter(this) }
private val currency by lazy { transactionManager.selectedCurrency!! }
private var tracker: SelectionTracker<String>? = null
private var actionMode: ActionMode? = null
@@ -109,7 +110,7 @@ class TransactionsFragment : Fragment(), OnEventClickListener, ActionMode.Callba
})
// kicks off initial load, needs to be adapted if showAll state is ever saved
- if (savedInstanceState == null) transactionManager.showAll.value = model.devMode.value
+ if (savedInstanceState == null) transactionManager.loadTransactions()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
@@ -137,14 +138,11 @@ class TransactionsFragment : Fragment(), OnEventClickListener, ActionMode.Callba
}
}
- override fun onTransactionClicked(transaction: Transaction) {
+ override fun onTransactionClicked(transaction: HistoryEvent) {
if (actionMode != null) return // don't react on clicks while in action mode
if (transaction.detailPageLayout != 0) {
transactionManager.selectedEvent = transaction
findNavController().navigate(R.id.action_nav_transaction_detail)
- } else if (model.devMode.value == true) {
- JsonDialogFragment.new(transaction.json.toString(2))
- .show(parentFragmentManager, null)
}
}
diff --git a/wallet/src/main/res/drawable/ic_history.xml b/wallet/src/main/res/drawable/ic_history.xml
new file mode 100644
index 0000000..d9f75ea
--- /dev/null
+++ b/wallet/src/main/res/drawable/ic_history.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z" />
+</vector>
diff --git a/wallet/src/main/res/layout/fragment_transactions.xml b/wallet/src/main/res/layout/fragment_transactions.xml
index aaf638c..547da24 100644
--- a/wallet/src/main/res/layout/fragment_transactions.xml
+++ b/wallet/src/main/res/layout/fragment_transactions.xml
@@ -27,7 +27,7 @@
android:scrollbars="vertical"
android:visibility="invisible"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- tools:listitem="@layout/list_item_transaction"
+ tools:listitem="@layout/list_item_history"
tools:visibility="visible" />
<TextView
diff --git a/wallet/src/main/res/layout/list_item_transaction.xml b/wallet/src/main/res/layout/list_item_history.xml
index 2fabe1d..2fabe1d 100644
--- a/wallet/src/main/res/layout/list_item_transaction.xml
+++ b/wallet/src/main/res/layout/list_item_history.xml
diff --git a/wallet/src/main/res/menu/activity_main_drawer.xml b/wallet/src/main/res/menu/activity_main_drawer.xml
index 896ff69..62abc32 100644
--- a/wallet/src/main/res/menu/activity_main_drawer.xml
+++ b/wallet/src/main/res/menu/activity_main_drawer.xml
@@ -18,7 +18,9 @@
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="@layout/activity_main">
- <group android:checkableBehavior="single">
+ <group
+ android:id="@+id/nav_group_main"
+ android:checkableBehavior="single">
<item
android:id="@+id/nav_home"
android:icon="@drawable/ic_account_balance_wallet"
@@ -28,10 +30,25 @@
android:id="@+id/nav_settings"
android:icon="@drawable/ic_settings"
android:title="@string/menu_settings" />
- <item
- android:id="@+id/nav_pending_operations"
- android:icon="@drawable/ic_sync"
- android:title="@string/pending_operations_title" />
</group>
+ <item
+ android:id="@+id/nav_dev"
+ android:title="@string/settings_dev_mode">
+ <menu>
+ <group
+ android:id="@+id/nav_group_dev"
+ android:checkableBehavior="single">
+ <item
+ android:id="@+id/nav_pending_operations"
+ android:icon="@drawable/ic_sync"
+ android:title="@string/pending_operations_title" />
+ <item
+ android:id="@+id/nav_history"
+ android:icon="@drawable/ic_history"
+ android:title="@string/nav_history" />
+ </group>
+ </menu>
+ </item>
+
</menu>
diff --git a/wallet/src/main/res/navigation/nav_graph.xml b/wallet/src/main/res/navigation/nav_graph.xml
index f8d515e..8e717c1 100644
--- a/wallet/src/main/res/navigation/nav_graph.xml
+++ b/wallet/src/main/res/navigation/nav_graph.xml
@@ -129,6 +129,12 @@
tools:layout="@layout/fragment_pending_operations" />
<fragment
+ android:id="@+id/nav_history"
+ android:name="net.taler.wallet.history.DevHistoryFragment"
+ android:label="@string/nav_history"
+ tools:layout="@layout/fragment_transactions" />
+
+ <fragment
android:id="@+id/errorFragment"
android:name="net.taler.wallet.withdraw.ErrorFragment"
android:label="@string/nav_error"
@@ -143,6 +149,10 @@
app:destination="@id/nav_pending_operations" />
<action
+ android:id="@+id/action_global_history"
+ app:destination="@id/nav_history" />
+
+ <action
android:id="@+id/action_nav_transaction_detail"
app:destination="@id/nav_transactions_detail" />
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
index a28545f..56ff2ef 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -40,6 +40,7 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card
<string name="nav_prompt_withdraw">Withdraw Digital Cash</string>
<string name="nav_exchange_tos">Exchange\'s Terms of Service</string>
<string name="nav_exchange_fees">Exchange Fees</string>
+ <string name="nav_history">Event History</string>
<string name="nav_error">Error</string>
<string name="button_back">Go Back</string>
diff --git a/wallet/src/test/java/net/taler/wallet/transactions/TransactionTest.kt b/wallet/src/test/java/net/taler/wallet/history/HistoryEventTest.kt
index 6549434..109b8dc 100644
--- a/wallet/src/test/java/net/taler/wallet/transactions/TransactionTest.kt
+++ b/wallet/src/test/java/net/taler/wallet/history/HistoryEventTest.kt
@@ -14,20 +14,38 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.wallet.transactions
+package net.taler.wallet.history
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue
import net.taler.common.Amount
-import net.taler.wallet.transactions.RefreshReason.PAY
-import net.taler.wallet.transactions.ReserveType.MANUAL
+import net.taler.wallet.history.ExchangeAddedEvent
+import net.taler.wallet.history.ExchangeUpdatedEvent
+import net.taler.wallet.history.HistoryEvent
+import net.taler.wallet.history.OrderAcceptedHistoryEvent
+import net.taler.wallet.history.OrderRedirectedHistoryEvent
+import net.taler.wallet.history.OrderRefusedHistoryEvent
+import net.taler.wallet.history.OrderShortInfo
+import net.taler.wallet.history.PaymentAbortedHistoryEvent
+import net.taler.wallet.history.PaymentHistoryEvent
+import net.taler.wallet.history.RefreshHistoryEvent
+import net.taler.wallet.history.RefreshReason.PAY
+import net.taler.wallet.history.RefundHistoryEvent
+import net.taler.wallet.history.ReserveBalanceUpdatedHistoryEvent
+import net.taler.wallet.history.ReserveShortInfo
+import net.taler.wallet.history.ReserveType.MANUAL
+import net.taler.wallet.history.TipAcceptedHistoryEvent
+import net.taler.wallet.history.TipDeclinedHistoryEvent
+import net.taler.wallet.history.UnknownHistoryEvent
+import net.taler.wallet.history.WithdrawHistoryEvent
+import net.taler.wallet.history.WithdrawalSourceReserve
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import kotlin.random.Random
-class TransactionTest {
+class HistoryEventTest {
private val mapper = ObjectMapper().registerModule(KotlinModule())
@@ -111,7 +129,7 @@ class TransactionTest {
"reservePub": "BRT2P0YMQSD5F48V9XHVNH73ZTS6EZC0KCQCPGPZQWTSQB77615G"
}
}""".trimIndent()
- val transaction: ReserveBalanceUpdatedTransaction = mapper.readValue(json)
+ val transaction: ReserveBalanceUpdatedHistoryEvent = mapper.readValue(json)
assertEquals(timestamp, transaction.timestamp.ms)
assertEquals("TESTKUDOS:23", transaction.reserveAwaitedAmount.toJSONString())
@@ -137,7 +155,7 @@ class TransactionTest {
"reservePub": "BRT2P0YMQSD5F48V9XHVNH73ZTS6EZC0KCQCPGPZQWTSQB77615G"
}
}""".trimIndent()
- val event: WithdrawTransaction = mapper.readValue(json)
+ val event: WithdrawHistoryEvent = mapper.readValue(json)
assertEquals(
"974FT7JDNR20EQKNR21G1HV9PB6T5AZHYHX9NHR51Q30ZK3T10S0",
@@ -186,7 +204,7 @@ class TransactionTest {
"t_ms": $timestamp
}
}""".trimIndent()
- val transaction: OrderAcceptedTransaction = mapper.readValue(json)
+ val transaction: OrderAcceptedHistoryEvent = mapper.readValue(json)
assertEquals(orderShortInfo, transaction.orderShortInfo)
assertEquals(timestamp, transaction.timestamp.ms)
@@ -208,7 +226,7 @@ class TransactionTest {
"t_ms": $timestamp
}
}""".trimIndent()
- val transaction: OrderRefusedTransaction = mapper.readValue(json)
+ val transaction: OrderRefusedHistoryEvent = mapper.readValue(json)
assertEquals(orderShortInfo, transaction.orderShortInfo)
assertEquals(timestamp, transaction.timestamp.ms)
@@ -234,7 +252,7 @@ class TransactionTest {
"numCoins": 6,
"amountPaidWithFees": "KUDOS:0.6"
}""".trimIndent()
- val event: PaymentTransaction = mapper.readValue(json)
+ val event: PaymentHistoryEvent = mapper.readValue(json)
assertEquals(orderShortInfo, event.orderShortInfo)
assertEquals(false, event.replay)
@@ -263,7 +281,7 @@ class TransactionTest {
"numCoins": 6,
"amountPaidWithFees": "KUDOS:0.6"
}""".trimIndent()
- val event: PaymentTransaction = mapper.readValue(json)
+ val event: PaymentHistoryEvent = mapper.readValue(json)
assertEquals(orderShortInfo, event.orderShortInfo)
assertEquals(true, event.replay)
@@ -290,7 +308,7 @@ class TransactionTest {
},
"amountLost": "KUDOS:0.1"
}""".trimIndent()
- val transaction: PaymentAbortedTransaction = mapper.readValue(json)
+ val transaction: PaymentAbortedHistoryEvent = mapper.readValue(json)
assertEquals(orderShortInfo, transaction.orderShortInfo)
assertEquals("KUDOS:0.1", transaction.amountLost.toJSONString())
@@ -308,7 +326,7 @@ class TransactionTest {
"tipId": "tip-accepted;898724XGQ1GGMZB4WY3KND582NSP74FZ60BX0Y87FF81H0FJ8XD0",
"tipRaw": "KUDOS:4"
}""".trimIndent()
- val transaction: TipAcceptedTransaction = mapper.readValue(json)
+ val transaction: TipAcceptedHistoryEvent = mapper.readValue(json)
assertEquals(
"tip-accepted;898724XGQ1GGMZB4WY3KND582NSP74FZ60BX0Y87FF81H0FJ8XD0",
@@ -329,7 +347,7 @@ class TransactionTest {
"tipId": "tip-accepted;998724XGQ1GGMZB4WY3KND582NSP74FZ60BX0Y87FF81H0FJ8XD0",
"tipAmount": "KUDOS:4"
}""".trimIndent()
- val transaction: TipDeclinedTransaction = mapper.readValue(json)
+ val transaction: TipDeclinedHistoryEvent = mapper.readValue(json)
assertEquals(
"tip-accepted;998724XGQ1GGMZB4WY3KND582NSP74FZ60BX0Y87FF81H0FJ8XD0",
@@ -359,7 +377,7 @@ class TransactionTest {
"amountRefundedInvalid": "KUDOS:0.5",
"amountRefundedEffective": "KUDOS:0.4"
}""".trimIndent()
- val event: RefundTransaction = mapper.readValue(json)
+ val event: RefundHistoryEvent = mapper.readValue(json)
assertEquals("refund;998724", event.refundGroupId)
assertEquals("KUDOS:1", event.amountRefundedRaw.toJSONString())
@@ -385,7 +403,7 @@ class TransactionTest {
"numOutputCoins": 0,
"numRefreshedInputCoins": 1
}""".trimIndent()
- val event: RefreshTransaction = mapper.readValue(json)
+ val event: RefreshHistoryEvent = mapper.readValue(json)
assertEquals("KUDOS:0", event.amountRefreshedEffective.toJSONString())
assertEquals("KUDOS:1", event.amountRefreshedRaw.toJSONString())
@@ -420,7 +438,7 @@ class TransactionTest {
"t_ms": $timestamp
}
}""".trimIndent()
- val transaction: OrderRedirectedTransaction = mapper.readValue(json)
+ val transaction: OrderRedirectedHistoryEvent = mapper.readValue(json)
assertEquals(
"898724XGQ1GGMZB4WY3KND582NSP74FZ60BX0Y87FF81H0FJ8XD0",
@@ -461,9 +479,9 @@ class TransactionTest {
},
"eventId": "does-not-exist;898724XGQ1GGMZB4WY3KND582NSP74FZ60BX0Y87FF81H0FJ8XD0"
}""".trimIndent()
- val event: Transaction = mapper.readValue(json)
+ val event: HistoryEvent = mapper.readValue(json)
- assertEquals(UnknownTransaction::class.java, event.javaClass)
+ assertEquals(UnknownHistoryEvent::class.java, event.javaClass)
assertEquals(timestamp, event.timestamp.ms)
}
diff --git a/wallet/src/test/java/net/taler/wallet/transactions/ReserveTransactionTest.kt b/wallet/src/test/java/net/taler/wallet/history/ReserveHistoryEventTest.kt
index 4a3c75b..f09d7b6 100644
--- a/wallet/src/test/java/net/taler/wallet/transactions/ReserveTransactionTest.kt
+++ b/wallet/src/test/java/net/taler/wallet/history/ReserveHistoryEventTest.kt
@@ -14,16 +14,17 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.wallet.transactions
+package net.taler.wallet.history
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue
+import net.taler.wallet.history.ReserveDepositTransaction
import org.junit.Assert.assertEquals
import org.junit.Test
import kotlin.random.Random
-class ReserveTransactionTest {
+class ReserveHistoryEventTest {
private val mapper = ObjectMapper().registerModule(KotlinModule())