aboutsummaryrefslogtreecommitdiff
path: root/wallet/src
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2020-05-12 15:26:44 -0300
committerTorsten Grote <t@grobox.de>2020-05-15 14:26:41 -0300
commite74f39ee86f32b4e0324405af1f0c7be061fb372 (patch)
tree484a9e2f6ad3d8d6c9662ff5f41c6f254d218b30 /wallet/src
parent4a6630d1d147ae35358272dc5222964831c234ab (diff)
downloadtaler-android-e74f39ee86f32b4e0324405af1f0c7be061fb372.tar.gz
taler-android-e74f39ee86f32b4e0324405af1f0c7be061fb372.tar.bz2
taler-android-e74f39ee86f32b4e0324405af1f0c7be061fb372.zip
[wallet] separate history and transactions UI
The history with its JSON payload is only shown in dev mode while the transactions are prepared to move to the new API.
Diffstat (limited to 'wallet/src')
-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())