diff options
-rw-r--r-- | app/build.gradle | 5 | ||||
-rw-r--r-- | app/src/main/java/net/taler/wallet/WalletViewModel.kt | 32 | ||||
-rw-r--r-- | app/src/main/java/net/taler/wallet/history/HistoryEvent.kt | 7 | ||||
-rw-r--r-- | app/src/main/java/net/taler/wallet/history/WalletHistory.kt | 58 | ||||
-rw-r--r-- | app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt | 15 | ||||
-rw-r--r-- | app/src/main/res/layout/fragment_show_history.xml | 39 | ||||
-rw-r--r-- | app/src/main/res/layout/history_payment_sent.xml | 11 | ||||
-rw-r--r-- | app/src/main/res/layout/history_row.xml | 2 | ||||
-rw-r--r-- | app/src/main/res/layout/history_withdrawn.xml | 25 | ||||
-rw-r--r-- | app/src/main/res/menu/history.xml | 17 | ||||
-rw-r--r-- | app/src/main/res/values/strings.xml | 6 | ||||
-rw-r--r-- | app/src/main/res/values/styles.xml | 5 |
12 files changed, 146 insertions, 76 deletions
diff --git a/app/build.gradle b/app/build.gradle index 7e0f2ce..c40ddef 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,14 +45,15 @@ dependencies { implementation project(":akono") implementation 'com.google.guava:guava:28.0-android' - def nav_version = "2.2.0-rc03" + def nav_version = "2.2.0-rc04" implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" // ViewModel and LiveData - def lifecycle_version = "2.1.0" + def lifecycle_version = "2.2.0-rc03" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" // QR codes implementation 'com.google.zxing:core:3.4.0' diff --git a/app/src/main/java/net/taler/wallet/WalletViewModel.kt b/app/src/main/java/net/taler/wallet/WalletViewModel.kt index f556ff3..f932cff 100644 --- a/app/src/main/java/net/taler/wallet/WalletViewModel.kt +++ b/app/src/main/java/net/taler/wallet/WalletViewModel.kt @@ -18,11 +18,15 @@ package net.taler.wallet import android.app.Application import android.util.Log -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.* 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 org.json.JSONObject @@ -61,6 +65,7 @@ open class WithdrawStatus { val tosText: String, val tosEtag: String ) : WithdrawStatus() + class Success : WithdrawStatus() data class ReceivedDetails( val talerWithdrawUri: String, @@ -81,6 +86,7 @@ open class PendingOperations( ) +@Suppress("EXPERIMENTAL_API_USAGE") class WalletViewModel(val app: Application) : AndroidViewModel(app) { private var initialized = false @@ -104,6 +110,18 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) { value = PendingOperations(listOf()) } + private val mHistoryProgress = MutableLiveData<Boolean>() + val historyProgress: LiveData<Boolean> = mHistoryProgress + + val historyShowAll = MutableLiveData<Boolean>() + + val history: LiveData<History> = historyShowAll.switchMap { showAll -> + loadHistory(showAll) + .onStart { mHistoryProgress.postValue(true) } + .onCompletion { mHistoryProgress.postValue(false) } + .asLiveData(Dispatchers.IO) + } + private var activeGetBalance = 0 private var activeGetPending = 0 @@ -188,15 +206,21 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) { } } - fun getHistory(cb: (r: History) -> Unit) { + 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 = mapper.readValue(result.getString("history")) - cb(history) + history.reverse() // show latest first + mHistoryProgress.postValue(false) + offer(if (showAll) history else history.filter { it.showToUser } as History) + close() } + awaitClose() } fun withdrawTestkudos() { diff --git a/app/src/main/java/net/taler/wallet/history/HistoryEvent.kt b/app/src/main/java/net/taler/wallet/history/HistoryEvent.kt index 31473f6..b21147a 100644 --- a/app/src/main/java/net/taler/wallet/history/HistoryEvent.kt +++ b/app/src/main/java/net/taler/wallet/history/HistoryEvent.kt @@ -80,7 +80,7 @@ class ReserveShortInfo( val reserveCreationDetail: ReserveCreationDetail ) -class History : ArrayList<HistoryEvent>() +typealias History = ArrayList<HistoryEvent> @JsonTypeInfo( use = NAME, @@ -109,7 +109,8 @@ abstract class HistoryEvent( @get:StringRes open val title: Int = 0, @get:DrawableRes - open val icon: Int = R.drawable.ic_account_balance + open val icon: Int = R.drawable.ic_account_balance, + open val showToUser: Boolean = false ) @@ -178,6 +179,7 @@ class HistoryWithdrawnEvent( override val layout = R.layout.history_withdrawn override val title = R.string.history_event_withdrawn override val icon = R.drawable.history_withdrawn + override val showToUser = true } @JsonTypeName("order-accepted") @@ -232,6 +234,7 @@ class HistoryPaymentSentEvent( override val layout = R.layout.history_payment_sent override val title = R.string.history_event_payment_sent override val icon = R.drawable.ic_cash_usd_outline + override val showToUser = true } @JsonTypeName("refreshed") diff --git a/app/src/main/java/net/taler/wallet/history/WalletHistory.kt b/app/src/main/java/net/taler/wallet/history/WalletHistory.kt index cdc90ae..0b2e214 100644 --- a/app/src/main/java/net/taler/wallet/history/WalletHistory.kt +++ b/app/src/main/java/net/taler/wallet/history/WalletHistory.kt @@ -19,11 +19,14 @@ package net.taler.wallet.history import android.os.Bundle import android.view.* +import android.view.View.INVISIBLE +import android.view.View.VISIBLE import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.fragment_show_history.* import net.taler.wallet.R import net.taler.wallet.WalletViewModel @@ -34,64 +37,65 @@ import net.taler.wallet.WalletViewModel class WalletHistory : Fragment() { lateinit var model: WalletViewModel + private lateinit var showAllItem: MenuItem private val historyAdapter = WalletHistoryAdapter() - lateinit var historyPlaceholder: View override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) model = activity?.run { - ViewModelProviders.of(this)[WalletViewModel::class.java] + ViewModelProvider(this)[WalletViewModel::class.java] } ?: throw Exception("Invalid Activity") } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - activity?.menuInflater?.inflate(R.menu.history, menu) + 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 -> { - updateHistory() + model.historyShowAll.value = showAllItem.isChecked true } else -> super.onOptionsItemSelected(item) } } - private fun updateHistory() { - model.getHistory { - if (it.isEmpty()) { - historyPlaceholder.visibility = View.VISIBLE - } - historyAdapter.update(it) - } - } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - // Inflate the layout for this fragment - val view = inflater.inflate(R.layout.fragment_show_history, container, false) - val myLayoutManager = LinearLayoutManager(context).apply { - reverseLayout = true // show latest events first - } - val myItemDecoration = DividerItemDecoration(context, myLayoutManager.orientation) - view.findViewById<RecyclerView>(R.id.list_history).apply { + 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) } - historyPlaceholder = view.findViewById<View>(R.id.list_history_placeholder) - historyPlaceholder.visibility = View.GONE - - updateHistory() + model.historyProgress.observe(this, Observer { show -> + historyProgressBar.visibility = if (show) VISIBLE else INVISIBLE + }) + model.history.observe(this, Observer { history -> + historyEmptyState.visibility = if (history.isEmpty()) VISIBLE else INVISIBLE + historyAdapter.update(history) + }) - return view + // kicks off initial load, needs to be adapted if showAll state is ever saved + if (savedInstanceState == null) model.historyShowAll.value = false } companion object { diff --git a/app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt b/app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt index 14fc1e9..9d601e1 100644 --- a/app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt +++ b/app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt @@ -64,7 +64,7 @@ internal class WalletHistoryAdapter(private var history: History = History()) : internal abstract class HistoryEventViewHolder(protected val v: View) : ViewHolder(v) { private val icon: ImageView = v.findViewById(R.id.icon) - private val title: TextView = v.findViewById(R.id.title) + protected val title: TextView = v.findViewById(R.id.title) private val time: TextView = v.findViewById(R.id.time) @CallSuper @@ -77,7 +77,15 @@ internal abstract class HistoryEventViewHolder(protected val v: View) : ViewHold private fun getRelativeTime(timestamp: Long): CharSequence { val now = System.currentTimeMillis() - return getRelativeTimeSpanString(timestamp, now, MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE) + return if (now - timestamp > DAY_IN_MILLIS * 2) { + formatDateTime( + v.context, + timestamp, + FORMAT_SHOW_TIME or FORMAT_SHOW_DATE or FORMAT_ABBREV_MONTH or FORMAT_NO_YEAR + ) + } else { + getRelativeTimeSpanString(timestamp, now, MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE) + } } } @@ -132,7 +140,8 @@ internal class HistoryPaymentSentViewHolder(v: View) : HistoryEventViewHolder(v) super.bind(event) event as HistoryPaymentSentEvent - summary.text = event.orderShortInfo.summary + title.text = event.orderShortInfo.summary + summary.setText(event.title) amountPaidWithFees.text = parseAmount(event.amountPaidWithFees).toString() } diff --git a/app/src/main/res/layout/fragment_show_history.xml b/app/src/main/res/layout/fragment_show_history.xml index 6265a50..dc93889 100644 --- a/app/src/main/res/layout/fragment_show_history.xml +++ b/app/src/main/res/layout/fragment_show_history.xml @@ -1,28 +1,31 @@ <?xml version="1.0" encoding="utf-8"?> -<androidx.core.widget.NestedScrollView - xmlns:android="http://schemas.android.com/apk/res/android" +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> - <LinearLayout + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/historyList" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:scrollbars="vertical" /> - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/list_history" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:scrollbars="vertical"/> + <TextView + android:id="@+id/historyEmptyState" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:text="@string/history_empty" + android:visibility="invisible" + tools:visibility="visible" /> - <TextView - android:id="@+id/list_history_placeholder" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:text="The wallet history is empty" - tools:visibility="gone"/> - </LinearLayout> + <ProgressBar + android:id="@+id/historyProgressBar" + style="?android:progressBarStyleLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:visibility="invisible" + tools:visibility="visible" /> -</androidx.core.widget.NestedScrollView>
\ No newline at end of file +</FrameLayout> diff --git a/app/src/main/res/layout/history_payment_sent.xml b/app/src/main/res/layout/history_payment_sent.xml index 0c39133..cf03998 100644 --- a/app/src/main/res/layout/history_payment_sent.xml +++ b/app/src/main/res/layout/history_payment_sent.xml @@ -22,16 +22,15 @@ <TextView android:id="@+id/title" + style="@style/HistoryTitle" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginEnd="8dp" - android:text="@string/history_event_payment_sent" - android:textColor="?android:textColorPrimary" - android:textSize="20sp" app:layout_constraintEnd_toStartOf="@+id/amountPaidWithFees" app:layout_constraintStart_toEndOf="@+id/icon" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + tools:text="Lots of books with very long titles" /> <TextView android:id="@+id/summary" @@ -46,16 +45,18 @@ app:layout_constraintStart_toEndOf="@+id/icon" app:layout_constraintTop_toBottomOf="@+id/title" app:layout_constraintVertical_bias="0.0" - tools:text="Lots of books" /> + tools:text="@string/history_event_payment_sent" /> <TextView android:id="@+id/amountPaidWithFees" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/red" + android:textSize="16sp" app:layout_constraintBottom_toTopOf="@+id/time" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.0" tools:text="0.2 TESTKUDOS" /> <TextView diff --git a/app/src/main/res/layout/history_row.xml b/app/src/main/res/layout/history_row.xml index cab7f0f..394ad79 100644 --- a/app/src/main/res/layout/history_row.xml +++ b/app/src/main/res/layout/history_row.xml @@ -18,11 +18,11 @@ tools:ignore="ContentDescription" /> <TextView + style="@style/HistoryTitle" android:id="@+id/title" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" - android:textSize="20sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/icon" app:layout_constraintTop_toTopOf="parent" diff --git a/app/src/main/res/layout/history_withdrawn.xml b/app/src/main/res/layout/history_withdrawn.xml index e02046b..75bae07 100644 --- a/app/src/main/res/layout/history_withdrawn.xml +++ b/app/src/main/res/layout/history_withdrawn.xml @@ -22,29 +22,38 @@ <TextView android:id="@+id/title" + style="@style/HistoryTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:text="@string/history_event_withdrawn" - android:textColor="?android:textColorPrimary" - android:textSize="20sp" + android:visibility="gone" app:layout_constraintStart_toEndOf="@+id/icon" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/exchangeUrl" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:layout_marginBottom="8dp" app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/fee" - app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintEnd_toStartOf="@+id/feeLabel" app:layout_constraintStart_toEndOf="@+id/icon" app:layout_constraintTop_toBottomOf="@+id/title" - app:layout_constraintVertical_bias="0.0" - tools:text="http://taler.exchange.url" /> + tools:text="http://taler.quite-long-exchange.url" /> + + <TextView + android:id="@+id/feeLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="2dp" + android:text="@string/history_fee_label" + app:layout_constraintEnd_toStartOf="@+id/fee" + app:layout_constraintTop_toTopOf="@+id/fee" /> <TextView android:id="@+id/fee" @@ -76,6 +85,6 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/fee" - tools:text="23 min ago" /> + tools:text="23 min. ago" /> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/menu/history.xml b/app/src/main/res/menu/history.xml index c8fb3e7..8c2aa69 100644 --- a/app/src/main/res/menu/history.xml +++ b/app/src/main/res/menu/history.xml @@ -1,8 +1,15 @@ <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto"> - <item android:id="@+id/reload_history" - android:title="Reload History" - android:orderInCategory="100" - app:showAsAction="never"/> + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item + android:id="@+id/show_all_history" + android:checkable="true" + android:checked="false" + android:title="@string/history_show_all" + app:showAsAction="never" /> + <item + android:id="@+id/reload_history" + android:orderInCategory="100" + android:title="@string/history_reload" + app:showAsAction="never" /> </menu> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1caf41e..6a92705 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,11 +20,15 @@ <string name="history_event_exchange_added">Exchange Added</string> <string name="history_event_exchange_updated">Exchange Updated</string> <string name="history_event_reserve_balance_updated">Reserve Balance Updated</string> - <string name="history_event_payment_sent">Payment Made</string> + <string name="history_event_payment_sent">Payment</string> <string name="history_event_withdrawn">Withdraw</string> <string name="history_event_order_accepted">Order Confirmed</string> <string name="history_event_order_refused">Order Cancelled</string> <string name="history_event_refreshed">Refresh</string> + <string name="history_fee_label">Fee:</string> + <string name="history_show_all">Show All</string> + <string name="history_reload">Reload History</string> + <string name="history_empty">The wallet history is empty</string> <!-- TODO: Remove or change this placeholder text --> <string name="hello_blank_fragment">Hello blank fragment</string> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 16dbab3..619c0c9 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -14,4 +14,9 @@ <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/> <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/> + <style name="HistoryTitle"> + <item name="android:textSize">18sp</item> + <item name="android:textColor">?android:textColorPrimary</item> + </style> + </resources> |