From 630f19931a093e5c0b4440a138a0717a8ce47e78 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Thu, 5 Mar 2020 14:39:13 -0300 Subject: Factor out history code from ViewModel Show history event JSON only in developer mode --- .../main/java/net/taler/wallet/WalletViewModel.kt | 49 +-------- .../net/taler/wallet/history/HistoryManager.kt | 71 +++++++++++++ .../java/net/taler/wallet/history/WalletHistory.kt | 115 --------------------- .../taler/wallet/history/WalletHistoryFragment.kt | 107 +++++++++++++++++++ app/src/main/res/navigation/nav_graph.xml | 4 +- app/src/main/res/values/strings.xml | 10 +- 6 files changed, 188 insertions(+), 168 deletions(-) create mode 100644 app/src/main/java/net/taler/wallet/history/HistoryManager.kt delete mode 100644 app/src/main/java/net/taler/wallet/history/WalletHistory.kt create mode 100644 app/src/main/java/net/taler/wallet/history/WalletHistoryFragment.kt (limited to 'app') diff --git a/app/src/main/java/net/taler/wallet/WalletViewModel.kt b/app/src/main/java/net/taler/wallet/WalletViewModel.kt index bec662a..14a800f 100644 --- a/app/src/main/java/net/taler/wallet/WalletViewModel.kt +++ b/app/src/main/java/net/taler/wallet/WalletViewModel.kt @@ -22,21 +22,12 @@ import androidx.annotation.UiThread import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.asLiveData import androidx.lifecycle.distinctUntilChanged -import androidx.lifecycle.switchMap import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.KotlinModule -import com.fasterxml.jackson.module.kotlin.readValue -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 net.taler.wallet.backend.WalletBackendApi -import net.taler.wallet.history.History -import net.taler.wallet.history.HistoryEvent +import net.taler.wallet.history.HistoryManager import net.taler.wallet.payment.PaymentManager import net.taler.wallet.pending.PendingOperationsManager import net.taler.wallet.withdraw.WithdrawManager @@ -46,26 +37,12 @@ const val TAG = "taler-wallet" data class BalanceItem(val available: Amount, val pendingIncoming: Amount) -@Suppress("EXPERIMENTAL_API_USAGE") class WalletViewModel(val app: Application) : AndroidViewModel(app) { private val mBalances = MutableLiveData>() val balances: LiveData> = mBalances.distinctUntilChanged() val devMode = MutableLiveData(BuildConfig.DEBUG) - - private val mHistoryProgress = MutableLiveData() - val historyProgress: LiveData = mHistoryProgress - - val historyShowAll = MutableLiveData() - - val history: LiveData = historyShowAll.switchMap { showAll -> - loadHistory(showAll) - .onStart { mHistoryProgress.postValue(true) } - .onCompletion { mHistoryProgress.postValue(false) } - .asLiveData(Dispatchers.IO) - } - val showProgressBar = MutableLiveData() private var activeGetBalance = 0 @@ -88,6 +65,7 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) { val paymentManager = PaymentManager(walletBackendApi, mapper) val pendingOperationsManager: PendingOperationsManager = PendingOperationsManager(walletBackendApi) + val historyManager = HistoryManager(walletBackendApi, mapper) override fun onCleared() { walletBackendApi.destroy() @@ -123,29 +101,6 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) { } } - private fun loadHistory(showAll: Boolean) = callbackFlow { - mHistoryProgress.postValue(true) - walletBackendApi.sendRequest("getHistory", null) { isError, result -> - if (isError) { - // TODO show error message in [WalletHistory] fragment - close() - return@sendRequest - } - val history = History() - val json = result.getJSONArray("history") - for (i in 0 until json.length()) { - val event: HistoryEvent = mapper.readValue(json.getString(i)) - event.json = json.getJSONObject(i) - history.add(event) - } - history.reverse() // show latest first - mHistoryProgress.postValue(false) - offer(if (showAll) history else history.filter { it.showToUser } as History) - close() - } - awaitClose() - } - @UiThread fun dangerouslyReset() { walletBackendApi.sendRequest("reset", null) diff --git a/app/src/main/java/net/taler/wallet/history/HistoryManager.kt b/app/src/main/java/net/taler/wallet/history/HistoryManager.kt new file mode 100644 index 0000000..c350daa --- /dev/null +++ b/app/src/main/java/net/taler/wallet/history/HistoryManager.kt @@ -0,0 +1,71 @@ +/* + * 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 + */ + +package net.taler.wallet.history + +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.Dispatchers +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.onCompletion +import kotlinx.coroutines.flow.onStart +import net.taler.wallet.backend.WalletBackendApi + +@Suppress("EXPERIMENTAL_API_USAGE") +class HistoryManager( + private val walletBackendApi: WalletBackendApi, + private val mapper: ObjectMapper +) { + + private val mProgress = MutableLiveData() + val progress: LiveData = mProgress + + val showAll = MutableLiveData() + + val history: LiveData = showAll.switchMap { showAll -> + loadHistory(showAll) + .onStart { mProgress.postValue(true) } + .onCompletion { mProgress.postValue(false) } + .asLiveData(Dispatchers.IO) + } + + private fun loadHistory(showAll: Boolean) = callbackFlow { + walletBackendApi.sendRequest("getHistory", null) { isError, result -> + if (isError) { + // TODO show error message in [WalletHistory] fragment + close() + return@sendRequest + } + 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 + offer(if (showAll) history else history.filter { it.showToUser } as History) + close() + } + awaitClose() + } + +} diff --git a/app/src/main/java/net/taler/wallet/history/WalletHistory.kt b/app/src/main/java/net/taler/wallet/history/WalletHistory.kt deleted file mode 100644 index bb37ffa..0000000 --- a/app/src/main/java/net/taler/wallet/history/WalletHistory.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 - */ - -package net.taler.wallet.history - - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.fragment_show_history.* -import net.taler.wallet.R -import net.taler.wallet.WalletViewModel - -interface OnEventClickListener { - fun onEventClicked(event: HistoryEvent) -} - -/** - * Wallet history. - * - */ -class WalletHistory : Fragment(), OnEventClickListener { - - private lateinit var model: WalletViewModel - private lateinit var showAllItem: MenuItem - private val historyAdapter = WalletHistoryAdapter(this) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - - model = activity?.run { - ViewModelProvider(this)[WalletViewModel::class.java] - } ?: throw Exception("Invalid Activity") - - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.history, menu) - showAllItem = menu.findItem(R.id.show_all_history) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.show_all_history -> { - item.isChecked = !item.isChecked - model.historyShowAll.value = item.isChecked - true - } - R.id.reload_history -> { - model.historyShowAll.value = showAllItem.isChecked - true - } - else -> super.onOptionsItemSelected(item) - } - } - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_show_history, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - historyList.apply { - val myLayoutManager = LinearLayoutManager(context) - val myItemDecoration = DividerItemDecoration(context, myLayoutManager.orientation) - layoutManager = myLayoutManager - adapter = historyAdapter - addItemDecoration(myItemDecoration) - } - - model.historyProgress.observe(viewLifecycleOwner, Observer { show -> - historyProgressBar.visibility = if (show) VISIBLE else INVISIBLE - }) - model.history.observe(viewLifecycleOwner, Observer { history -> - historyEmptyState.visibility = if (history.isEmpty()) VISIBLE else INVISIBLE - historyAdapter.update(history) - }) - - // kicks off initial load, needs to be adapted if showAll state is ever saved - if (savedInstanceState == null) model.historyShowAll.value = false - } - - override fun onEventClicked(event: HistoryEvent) { - JsonDialogFragment.new(event.json.toString(4)) - .show(parentFragmentManager, null) - } - -} diff --git a/app/src/main/java/net/taler/wallet/history/WalletHistoryFragment.kt b/app/src/main/java/net/taler/wallet/history/WalletHistoryFragment.kt new file mode 100644 index 0000000..75b7d02 --- /dev/null +++ b/app/src/main/java/net/taler/wallet/history/WalletHistoryFragment.kt @@ -0,0 +1,107 @@ +/* + * 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 + */ + +package net.taler.wallet.history + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.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 +import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL +import kotlinx.android.synthetic.main.fragment_show_history.* +import net.taler.wallet.R +import net.taler.wallet.WalletViewModel + +interface OnEventClickListener { + fun onEventClicked(event: HistoryEvent) +} + +class WalletHistoryFragment : Fragment(), OnEventClickListener { + + private val model: WalletViewModel by activityViewModels() + private val historyManager by lazy { model.historyManager } + private lateinit var showAllItem: MenuItem + private val historyAdapter = WalletHistoryAdapter(this) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_show_history, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + historyList.apply { + layoutManager = LinearLayoutManager(context) + adapter = historyAdapter + addItemDecoration(DividerItemDecoration(context, VERTICAL)) + } + + historyManager.progress.observe(viewLifecycleOwner, Observer { show -> + historyProgressBar.visibility = if (show) VISIBLE else INVISIBLE + }) + historyManager.history.observe(viewLifecycleOwner, Observer { history -> + historyEmptyState.visibility = if (history.isEmpty()) VISIBLE else INVISIBLE + historyAdapter.update(history) + }) + + // kicks off initial load, needs to be adapted if showAll state is ever saved + if (savedInstanceState == null) historyManager.showAll.value = model.devMode.value + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.history, menu) + showAllItem = menu.findItem(R.id.show_all_history) + showAllItem.isChecked = historyManager.showAll.value == true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.show_all_history -> { + item.isChecked = !item.isChecked + historyManager.showAll.value = item.isChecked + true + } + R.id.reload_history -> { + historyManager.showAll.value = showAllItem.isChecked + true + } + else -> super.onOptionsItemSelected(item) + } + } + + override fun onEventClicked(event: HistoryEvent) { + if (model.devMode.value != true) return + JsonDialogFragment.new(event.json.toString(4)) + .show(parentFragmentManager, null) + } + +} diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 648c88e..3a14e8b 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -59,8 +59,8 @@ tools:layout="@layout/fragment_settings" /> inbound There is no digital cash in your wallet.\n\nYou can get test money from the demo bank:\n\nhttps://bank.demo.taler.net + History + Fee: + Show All + Reload History + The wallet history is empty + Exchange Added Exchange Updated @@ -57,10 +63,6 @@ Refund Obtained change Unknown Event - Fee: - Show All - Reload History - The wallet history is empty +%s payment fee Confirm Payment -- cgit v1.2.3