commit cc9de0a930827883596f0467a86f55e885b1c486
parent 1f73fe833d1ae040fddb413311f896d1c6859bd7
Author: Iván Ávalos <avalos@disroot.org>
Date: Sat, 23 Nov 2024 23:58:30 +0100
[wallet] QC: language selector for ToS
Diffstat:
5 files changed, 98 insertions(+), 22 deletions(-)
diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalancesComposable.kt b/wallet/src/main/java/net/taler/wallet/balances/BalancesComposable.kt
@@ -104,7 +104,9 @@ fun BalancesComposable(
onTransactionsDelete = onTransactionsDelete,
onShowBalancesClicked = onShowBalancesClicked,
)
- } ?: error("no balance matching scopeInfo")
+ } ?: run {
+ onShowBalancesClicked()
+ }
}
} else {
EmptyBalancesComposable(
diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt
@@ -163,9 +163,13 @@ class ExchangeManager(
/**
* Fetch exchange terms of service.
*/
- suspend fun getExchangeTos(exchangeBaseUrl: String): TosResponse? {
+ suspend fun getExchangeTos(
+ exchangeBaseUrl: String,
+ language: String? = null,
+ ): TosResponse? {
var result: TosResponse? = null
api.request("getExchangeTos", TosResponse.serializer()) {
+ language?.let { put("acceptLanguage", it) }
put("exchangeBaseUrl", exchangeBaseUrl)
}.onError { error ->
Log.d(TAG, "Error getExchangeTos: $error")
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt
@@ -23,6 +23,8 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.marginBottom
@@ -44,8 +46,9 @@ import net.taler.wallet.MainViewModel
import net.taler.wallet.R
import net.taler.wallet.databinding.FragmentReviewExchangeTosBinding
import java.text.ParseException
+import java.util.Locale
-class ReviewExchangeTosFragment : Fragment() {
+class ReviewExchangeTosFragment : Fragment(), AdapterView.OnItemSelectedListener {
private val model: MainViewModel by activityViewModels()
private val exchangeManager by lazy { model.exchangeManager }
@@ -55,6 +58,10 @@ class ReviewExchangeTosFragment : Fragment() {
private val adapter by lazy { TosAdapter(markwon) }
private var tos: TosResponse? = null
+ private var exchangeBaseUrl: String? = null
+ private var langAdapter: ArrayAdapter<String>? = null
+ private var selectedLang: String? = null
+ private var manualSelect: Boolean = true
override fun onCreateView(
inflater: LayoutInflater,
@@ -69,17 +76,22 @@ class ReviewExchangeTosFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
setupInsets()
- val exchangeBaseUrl = arguments?.getString("exchangeBaseUrl")
+ exchangeBaseUrl = arguments?.getString("exchangeBaseUrl")
?: error("no exchangeBaseUrl passed")
val readOnly = arguments?.getBoolean("readOnly") ?: false
+ langAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item)
+ langAdapter?.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ ui.langSpinner.adapter = langAdapter
+ ui.langSpinner.onItemSelectedListener = this
+
ui.buttonCard.visibility = if (readOnly) GONE else VISIBLE
ui.acceptTosCheckBox.isChecked = false
ui.acceptTosCheckBox.setOnCheckedChangeListener { _, _ ->
tos?.let {
viewLifecycleOwner.lifecycleScope.launch {
if (exchangeManager.acceptCurrentTos(
- exchangeBaseUrl = exchangeBaseUrl,
+ exchangeBaseUrl = exchangeBaseUrl!!,
currentEtag = it.currentEtag,
)) {
findNavController().navigateUp()
@@ -90,24 +102,54 @@ class ReviewExchangeTosFragment : Fragment() {
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
- tos = exchangeManager.getExchangeTos(exchangeBaseUrl)
- // FIXME: better null handling!
- tos?.let {
- val sections = try {
- parseTos(markwon, it.content)
- } catch (e: ParseException) {
- onTosError(e.message ?: "Unknown Error")
- return@repeatOnLifecycle
- }
+ renderTos(exchangeBaseUrl!!, selectedLang)
+ }
+ }
+ }
- adapter.setSections(sections)
- ui.tosList.adapter = adapter
- ui.tosList.fadeIn()
+ private suspend fun renderTos(
+ exchangeBaseUrl: String,
+ language: String? = null,
+ ) {
+ val lc = Locale.getDefault().language
+ selectedLang = language ?: lc
+ tos = exchangeManager.getExchangeTos(exchangeBaseUrl, selectedLang)
+
+ // Setup language adapter
+ val languages = tos?.tosAvailableLanguages ?: emptyList()
+ langAdapter?.clear()
+ langAdapter?.addAll(languages.map { lang ->
+ Locale(lang).displayLanguage
+ })
+ langAdapter?.notifyDataSetChanged()
+
+ // Setup language spinner
+ if (languages.size > 1) {
+ ui.langSpinner.visibility = VISIBLE
+ val i = languages.indexOf(selectedLang)
+ if (i >= 0) {
+ manualSelect = false
+ ui.langSpinner.setSelection(i)
+ }
+ } else {
+ ui.langSpinner.visibility = GONE
+ }
- ui.acceptTosCheckBox.fadeIn()
- ui.progressBar.fadeOut()
- }
+ // FIXME: better null handling!
+ tos?.let {
+ val sections = try {
+ parseTos(markwon, it.content)
+ } catch (e: ParseException) {
+ onTosError(e.message ?: "Unknown Error")
+ return
}
+
+ adapter.setSections(sections)
+ ui.tosList.adapter = adapter
+ ui.tosList.fadeIn()
+
+ ui.acceptTosCheckBox.fadeIn()
+ ui.progressBar.fadeOut()
}
}
@@ -145,4 +187,18 @@ class ReviewExchangeTosFragment : Fragment() {
ui.errorView.fadeIn()
}
+ override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
+ if (manualSelect) {
+ tos?.tosAvailableLanguages?.get(position)?.let { lang ->
+ viewLifecycleOwner.lifecycleScope.launch {
+ renderTos(exchangeBaseUrl!!, lang)
+ }
+ }
+ } else {
+ manualSelect = true
+ }
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>?) {}
+
}
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/TosSection.kt b/wallet/src/main/java/net/taler/wallet/withdraw/TosSection.kt
@@ -79,5 +79,7 @@ private fun getNodeText(rootNode: Node): String {
@Serializable
data class TosResponse(
val content: String,
- val currentEtag: String
+ val currentEtag: String,
+ val contentLanguage: String? = null,
+ val tosAvailableLanguages: List<String> = emptyList(),
)
diff --git a/wallet/src/main/res/layout/fragment_review_exchange_tos.xml b/wallet/src/main/res/layout/fragment_review_exchange_tos.xml
@@ -21,6 +21,18 @@
android:layout_height="match_parent"
tools:context=".withdraw.ReviewExchangeTosFragment">
+ <Spinner
+ android:id="@+id/langSpinner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ android:visibility="gone"
+ tools:visibility="visible"/>
+
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/tosList"
android:layout_width="0dp"
@@ -29,7 +41,7 @@
app:layout_constraintBottom_toTopOf="@+id/buttonCard"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/langSpinner"
android:clipToPadding="false"
tools:listitem="@layout/list_item_tos" />