commit 2d3d9ae7ce871a256eb1021b9d6b0ab5e461e027
parent 5a96d14b3ece9553ea45c1418fda0f2ba21811a8
Author: Iván Ávalos <avalos@disroot.org>
Date: Sat, 2 Nov 2024 01:06:57 +0100
[wallet] save selected scopeInfo on DataStore
Diffstat:
8 files changed, 169 insertions(+), 0 deletions(-)
diff --git a/build.gradle b/build.gradle
@@ -7,6 +7,7 @@ buildscript {
lifecycle_version = '2.8.4'
constraintlayout_version = '2.1.4'
junit_version = "4.13.2"
+ datastore_version = "1.1.1"
// should debug build types be minified with D8 as well? good for catching issues early
minify_debug = true
}
diff --git a/wallet/build.gradle b/wallet/build.gradle
@@ -20,6 +20,7 @@ plugins {
id "com.android.application"
id "kotlin-android"
id "kotlinx-serialization"
+ id "com.google.protobuf" version "0.9.4"
}
def qtart_version = "0.13.6"
@@ -175,6 +176,10 @@ dependencies {
// Java Native Access (must always match JNA in qtart)
implementation "net.java.dev.jna:jna:5.13.0@aar"
+ // Shared preferences
+ implementation "androidx.datastore:datastore:$datastore_version"
+ implementation "com.google.protobuf:protobuf-javalite:3.18.0"
+
testImplementation "junit:junit:$junit_version"
testImplementation 'org.json:json:20220320'
}
@@ -201,3 +206,19 @@ android.applicationVariants.configureEach { variant ->
// leaves version code alone of there's no baseAbiVersionCode
}
}
+
+protobuf {
+ protoc {
+ artifact = "com.google.protobuf:protoc:4.28.3"
+ }
+
+ generateProtoTasks {
+ all().each { task ->
+ task.builtins {
+ java {
+ option 'lite'
+ }
+ }
+ }
+ }
+}
+\ No newline at end of file
diff --git a/wallet/proguard-rules.pro b/wallet/proguard-rules.pro
@@ -24,6 +24,8 @@
-dontobfuscate
-keep class net.taler.wallet.** {*;}
+-keep class androidx.datastore.*.** {*;}
+-keep class com.google.protobuf.*.** {*;}
-dontwarn java.lang.management.ManagementFactory
-dontwarn java.lang.management.RuntimeMXBean
diff --git a/wallet/src/main/java/net/taler/wallet/MainFragment.kt b/wallet/src/main/java/net/taler/wallet/MainFragment.kt
@@ -78,6 +78,7 @@ import androidx.fragment.compose.AndroidFragment
import androidx.fragment.compose.FragmentState
import androidx.fragment.compose.rememberFragmentState
import androidx.navigation.fragment.findNavController
+import kotlinx.coroutines.flow.first
import net.taler.wallet.balances.BalanceState
import net.taler.wallet.balances.BalancesComposable
import net.taler.wallet.balances.ScopeInfo
@@ -179,6 +180,16 @@ class MainFragment: Fragment() {
WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom
)
) { innerPadding ->
+ LaunchedEffect(Unit) {
+ model.transactionManager.selectScope(
+ model.getSelectedScope(context).first(),
+ )
+ }
+
+ LaunchedEffect(selectedScope) {
+ model.saveSelectedScope(context, selectedScope)
+ }
+
LaunchedEffect(tab, selectedScope) {
setTitle(tab, selectedScope)
}
diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
@@ -17,6 +17,7 @@
package net.taler.wallet
import android.app.Application
+import android.content.Context
import android.net.Uri
import android.util.Log
import androidx.annotation.UiThread
@@ -28,6 +29,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.getAndUpdate
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import net.taler.common.Amount
@@ -53,6 +55,7 @@ import net.taler.wallet.payment.PaymentManager
import net.taler.wallet.peer.PeerManager
import net.taler.wallet.refund.RefundManager
import net.taler.wallet.settings.SettingsManager
+import net.taler.wallet.settings.userPreferencesDataStore
import net.taler.wallet.transactions.TransactionManager
import net.taler.wallet.withdraw.WithdrawManager
import org.json.JSONObject
@@ -278,6 +281,27 @@ class MainViewModel(
}
}
+ fun getSelectedScope(c: Context) = c.userPreferencesDataStore.data.map { scope ->
+ if (scope.hasSelectedScope()) {
+ ScopeInfo.fromPrefs(scope.selectedScope)
+ } else {
+ null
+ }
+ }
+
+ fun saveSelectedScope(c: Context, scopeInfo: ScopeInfo?) = viewModelScope.launch {
+ c.userPreferencesDataStore.updateData { current ->
+ if (scopeInfo != null) {
+ current.toBuilder()
+ .setSelectedScope(scopeInfo.toPrefs())
+ .build()
+ } else {
+ current.toBuilder()
+ .clearSelectedScope()
+ .build()
+ }
+ }
+ }
}
enum class ScanQrContext {
diff --git a/wallet/src/main/java/net/taler/wallet/balances/Balances.kt b/wallet/src/main/java/net/taler/wallet/balances/Balances.kt
@@ -19,6 +19,8 @@ package net.taler.wallet.balances
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.taler.common.Amount
+import net.taler.wallet.PrefsScopeInfo
+import net.taler.wallet.ScopeInfoType
@Serializable
data class BalanceItem(
@@ -53,4 +55,42 @@ sealed class ScopeInfo {
override val currency: String,
val url: String,
): ScopeInfo()
+
+ fun toPrefs(): PrefsScopeInfo {
+ val type = when (this) {
+ is Global -> ScopeInfoType.GLOBAL
+ is Exchange -> ScopeInfoType.EXCHANGE
+ is Auditor -> ScopeInfoType.AUDITOR
+ }
+
+ val url = when (this) {
+ is Exchange -> url
+ is Auditor -> url
+ else -> null
+ }
+
+ return PrefsScopeInfo.newBuilder()
+ .setType(type)
+ .setUrl(url)
+ .setCurrency(currency)
+ .build()
+ }
+
+ companion object {
+ fun fromPrefs(scope: PrefsScopeInfo) = when (scope.type) {
+ ScopeInfoType.GLOBAL -> Global(currency = scope.currency)
+
+ ScopeInfoType.EXCHANGE -> Exchange(
+ currency = scope.currency,
+ url = scope.url,
+ )
+
+ ScopeInfoType.AUDITOR -> Auditor(
+ currency = scope.currency,
+ url = scope.url,
+ )
+
+ else -> null
+ }
+ }
}
\ No newline at end of file
diff --git a/wallet/src/main/java/net/taler/wallet/settings/UserPreferencesSerializer.kt b/wallet/src/main/java/net/taler/wallet/settings/UserPreferencesSerializer.kt
@@ -0,0 +1,48 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2024 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.settings
+
+import android.content.Context
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.Serializer
+import androidx.datastore.dataStore
+import com.google.protobuf.InvalidProtocolBufferException
+import net.taler.wallet.UserPreferences
+import java.io.InputStream
+import java.io.OutputStream
+
+object UserPreferencesSerializer: Serializer<UserPreferences> {
+ override val defaultValue = UserPreferences.getDefaultInstance()
+
+ override suspend fun readFrom(input: InputStream): UserPreferences {
+ try {
+ return UserPreferences.parseFrom(input)
+ } catch (exception: InvalidProtocolBufferException) {
+ throw CorruptionException("Cannot read proto.", exception)
+ }
+ }
+
+ override suspend fun writeTo(t: UserPreferences, output: OutputStream) =
+ t.writeTo(output)
+
+}
+
+val Context.userPreferencesDataStore: DataStore<UserPreferences> by dataStore(
+ fileName = "settings.pb",
+ serializer = UserPreferencesSerializer,
+)
diff --git a/wallet/src/main/proto/user_prefs.proto b/wallet/src/main/proto/user_prefs.proto
@@ -0,0 +1,20 @@
+syntax = "proto3";
+
+option java_package = "net.taler.wallet";
+option java_multiple_files = true;
+
+enum ScopeInfoType {
+ GLOBAL = 0;
+ EXCHANGE = 1;
+ AUDITOR = 2;
+}
+
+message PrefsScopeInfo {
+ ScopeInfoType type = 1;
+ string currency = 2;
+ optional string url = 3;
+}
+
+message UserPreferences {
+ optional PrefsScopeInfo selectedScope = 1;
+}
+\ No newline at end of file