From b0869289dc9fa2f983991915e77ba0260e59ed8b Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 20 May 2020 11:47:26 -0300 Subject: [wallet] implement transaction search --- .../wallet/transactions/TransactionManager.kt | 35 ++++++++----- .../wallet/transactions/TransactionsFragment.kt | 57 +++++++++++++++++----- 2 files changed, 67 insertions(+), 25 deletions(-) (limited to 'wallet/src/main/java/net/taler') 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 d5cee16..882b29b 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt @@ -20,6 +20,7 @@ import androidx.annotation.UiThread import androidx.annotation.WorkerThread import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.switchMap import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.readValue import kotlinx.coroutines.CoroutineScope @@ -46,26 +47,36 @@ class TransactionManager( var selectedCurrency: String? = null var selectedTransaction: Transaction? = null + val searchQuery = MutableLiveData(null) + private val allTransactions = HashMap>() private val mTransactions = HashMap>() val transactions: LiveData @UiThread - get() { + get() = searchQuery.switchMap { query -> val currency = selectedCurrency check(currency != null) { "Did not select currency before getting transactions" } - return mTransactions.getOrPut(currency) { MutableLiveData() } + loadTransactions(query) + mTransactions[currency]!! // non-null because filled in [loadTransactions] } @UiThread - fun loadTransactions() { + fun loadTransactions(searchQuery: String? = null) { val currency = selectedCurrency ?: return - val liveData = mTransactions.getOrPut(currency) { - MutableLiveData() + val liveData = mTransactions.getOrPut(currency) { MutableLiveData() } + if (searchQuery == null && allTransactions.containsKey(currency)) { + liveData.value = TransactionsResult.Success(allTransactions[currency]!!) } if (liveData.value == null) mProgress.value = true val request = JSONObject(mapOf("currency" to currency)) + searchQuery?.let { request.put("search", it) } walletBackendApi.sendRequest("getTransactions", request) { isError, result -> - scope.launch(Dispatchers.Default) { - onTransactionsLoaded(liveData, isError, result) + if (isError) { + liveData.postValue(TransactionsResult.Error) + } else { + val currencyToUpdate = if (searchQuery == null) currency else null + scope.launch(Dispatchers.Default) { + onTransactionsLoaded(liveData, currencyToUpdate, result) + } } } } @@ -73,13 +84,9 @@ class TransactionManager( @WorkerThread private fun onTransactionsLoaded( liveData: MutableLiveData, - isError: Boolean, + currency: String?, // only non-null if we should update all transactions cache result: JSONObject ) { - if (isError) { - liveData.postValue(TransactionsResult.Error) - return - } val transactionsArray = result.getString("transactions") val transactions: LinkedList = mapper.readValue(transactionsArray) // TODO remove when fixed in wallet-core @@ -87,6 +94,10 @@ class TransactionManager( transactions.reverse() // show latest first mProgress.postValue(false) liveData.postValue(TransactionsResult.Success(transactions)) + // update all transactions on UiThread if there was a currency + currency?.let { + scope.launch(Dispatchers.Main) { allTransactions[currency] = transactions } + } } @UiThread 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 dfd00ea..526aa94 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt @@ -23,11 +23,11 @@ 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 android.widget.Toast import android.widget.Toast.LENGTH_LONG +import androidx.appcompat.widget.SearchView +import androidx.appcompat.widget.SearchView.OnQueryTextListener import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer @@ -102,14 +102,11 @@ class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode. }) transactionManager.progress.observe(viewLifecycleOwner, Observer { show -> - progressBar.visibility = if (show) VISIBLE else INVISIBLE + if (show) progressBar.fadeIn() else progressBar.fadeOut() }) transactionManager.transactions.observe(viewLifecycleOwner, Observer { result -> onTransactionsResult(result) }) - - // kicks off initial load, needs to be adapted if showAll state is ever saved - if (savedInstanceState == null) transactionManager.loadTransactions() } override fun onActivityCreated(savedInstanceState: Bundle?) { @@ -129,12 +126,29 @@ class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode. override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.transactions, menu) + setupSearch(menu.findItem(R.id.action_search)) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - else -> super.onOptionsItemSelected(item) - } + private fun setupSearch(item: MenuItem) { + item.setOnActionExpandListener(object : MenuItem.OnActionExpandListener { + override fun onMenuItemActionExpand(item: MenuItem) = true + override fun onMenuItemActionCollapse(item: MenuItem): Boolean { + onSearchClosed() + return true + } + }) + val searchView = item.actionView as SearchView + searchView.setOnQueryTextListener(object : OnQueryTextListener { + override fun onQueryTextChange(newText: String) = false + override fun onQueryTextSubmit(query: String): Boolean { + // workaround to avoid issues with some emulators and keyboard devices + // firing twice if a keyboard enter is used + // see https://code.google.com/p/android/issues/detail?id=24599 + item.actionView.clearFocus() + onSearch(query) + return true + } + }) } override fun onTransactionClicked(transaction: Transaction) { @@ -152,12 +166,29 @@ class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode. emptyState.fadeIn() } is TransactionsResult.Success -> { - emptyState.visibility = if (result.transactions.isEmpty()) VISIBLE else INVISIBLE - transactionAdapter.update(result.transactions) - list.fadeIn() + if (result.transactions.isEmpty()) { + val isSearch = transactionManager.searchQuery.value != null + emptyState.setText(if (isSearch) R.string.transactions_empty_search else R.string.transactions_empty) + emptyState.fadeIn() + list.fadeOut() + } else { + emptyState.fadeOut() + transactionAdapter.update(result.transactions) + list.fadeIn() + } } } + private fun onSearch(query: String) { + list.fadeOut() + progressBar.fadeIn() + transactionManager.searchQuery.value = query + } + + private fun onSearchClosed() { + transactionManager.searchQuery.value = null + } + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { val inflater = mode.menuInflater inflater.inflate(R.menu.transactions_action_mode, menu) -- cgit v1.2.3