taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

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:
Mbuild.gradle | 1+
Mwallet/build.gradle | 22++++++++++++++++++++++
Mwallet/proguard-rules.pro | 2++
Mwallet/src/main/java/net/taler/wallet/MainFragment.kt | 11+++++++++++
Mwallet/src/main/java/net/taler/wallet/MainViewModel.kt | 24++++++++++++++++++++++++
Mwallet/src/main/java/net/taler/wallet/balances/Balances.kt | 40++++++++++++++++++++++++++++++++++++++++
Awallet/src/main/java/net/taler/wallet/settings/UserPreferencesSerializer.kt | 48++++++++++++++++++++++++++++++++++++++++++++++++
Awallet/src/main/proto/user_prefs.proto | 21+++++++++++++++++++++
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