gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-android] branch master updated (8c098d9 -> 5ed3f1b)


From: gnunet
Subject: [taler-taler-android] branch master updated (8c098d9 -> 5ed3f1b)
Date: Tue, 28 Mar 2023 18:26:48 +0200

This is an automated email from the git hooks/post-receive script.

torsten-grote pushed a change to branch master
in repository taler-android.

    from 8c098d9  Translated using Weblate (Catalan)
     new ffe6769  [wallet] Don't run qtart in a separate process
     new 5ed3f1b  [wallet] Bring back essential proguard rule that got lost in 
some past commit

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 wallet/proguard-rules.pro                          |   3 +
 wallet/src/main/AndroidManifest.xml                |   4 -
 .../src/main/java/net/taler/wallet/MainActivity.kt |   1 -
 .../main/java/net/taler/wallet/MainViewModel.kt    |  74 +++---
 wallet/src/main/java/net/taler/wallet/WalletApp.kt |   2 +-
 .../java/net/taler/wallet/backend/ApiResponse.kt   |  61 +++++
 .../net/taler/wallet/backend/BackendManager.kt     |  89 +++++++
 .../BalanceResponse.kt => backend/InitResponse.kt} |  22 +-
 .../{WalletApp.kt => backend/RequestManager.kt}    |  31 ++-
 .../net/taler/wallet/backend/TalerErrorCode.kt     |   5 +-
 .../net/taler/wallet/backend/WalletBackendApi.kt   | 175 ++++----------
 .../taler/wallet/backend/WalletBackendService.kt   | 266 ---------------------
 .../wallet/pending/PendingOperationsManager.kt     |  45 ++--
 13 files changed, 309 insertions(+), 469 deletions(-)
 create mode 100644 wallet/src/main/java/net/taler/wallet/backend/ApiResponse.kt
 create mode 100644 
wallet/src/main/java/net/taler/wallet/backend/BackendManager.kt
 copy wallet/src/main/java/net/taler/wallet/{balances/BalanceResponse.kt => 
backend/InitResponse.kt} (63%)
 copy wallet/src/main/java/net/taler/wallet/{WalletApp.kt => 
backend/RequestManager.kt} (52%)
 delete mode 100644 
wallet/src/main/java/net/taler/wallet/backend/WalletBackendService.kt

diff --git a/wallet/proguard-rules.pro b/wallet/proguard-rules.pro
index 9eda10b..f06a5aa 100644
--- a/wallet/proguard-rules.pro
+++ b/wallet/proguard-rules.pro
@@ -21,3 +21,6 @@
 #-renamesourcefileattribute SourceFile
 
 #noinspection ShrinkerUnresolvedReference
+
+-dontobfuscate
+-keep class net.taler.wallet.** {*;}
diff --git a/wallet/src/main/AndroidManifest.xml 
b/wallet/src/main/AndroidManifest.xml
index 0b0a5b6..96c2958 100644
--- a/wallet/src/main/AndroidManifest.xml
+++ b/wallet/src/main/AndroidManifest.xml
@@ -89,10 +89,6 @@
                 android:name="android.nfc.cardemulation.host_apdu_service"
                 android:resource="@xml/apduservice" />
         </service>
-
-        <service
-            android:name=".backend.WalletBackendService"
-            android:process=":WalletBackendService" />
     </application>
 
     <queries>
diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt 
b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
index 7a40b4b..e91b983 100644
--- a/wallet/src/main/java/net/taler/wallet/MainActivity.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
@@ -70,7 +70,6 @@ import java.net.URL
 import java.util.Locale.ROOT
 import javax.net.ssl.HttpsURLConnection
 
-
 class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener,
     OnPreferenceStartFragmentCallback {
 
diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt 
b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
index ed12533..bbd3ca3 100644
--- a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
@@ -24,15 +24,19 @@ import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.distinctUntilChanged
 import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
 import net.taler.common.Amount
 import net.taler.common.AmountParserException
 import net.taler.common.Event
-import net.taler.common.assertUiThread
 import net.taler.common.toEvent
 import net.taler.wallet.accounts.AccountManager
+import net.taler.wallet.backend.NotificationPayload
+import net.taler.wallet.backend.NotificationReceiver
+import net.taler.wallet.backend.VersionReceiver
 import net.taler.wallet.backend.WalletBackendApi
+import net.taler.wallet.backend.WalletCoreVersion
 import net.taler.wallet.balances.BalanceItem
 import net.taler.wallet.balances.BalanceResponse
 import net.taler.wallet.deposit.DepositManager
@@ -58,7 +62,9 @@ private val transactionNotifications = listOf(
     "withdraw-group-finished"
 )
 
-class MainViewModel(val app: Application) : AndroidViewModel(app) {
+class MainViewModel(
+    app: Application,
+) : AndroidViewModel(app), VersionReceiver, NotificationReceiver {
 
     private val mBalances = MutableLiveData<List<BalanceItem>>()
     val balances: LiveData<List<BalanceItem>> = 
mBalances.distinctUntilChanged()
@@ -70,32 +76,13 @@ class MainViewModel(val app: Application) : 
AndroidViewModel(app) {
     var merchantVersion: String? = null
         private set
 
-    private val api = WalletBackendApi(app) { payload ->
-        if (payload.optString("operation") == "init") {
-            val result = payload.getJSONObject("result")
-            val versions = result.getJSONObject("versionInfo")
-            exchangeVersion = versions.getString("exchange")
-            merchantVersion = versions.getString("merchant")
-        } else if (payload.getString("type") != "waiting-for-retry") { // 
ignore ping
-            Log.i(TAG, "Received notification from wallet-core: 
${payload.toString(2)}")
-            loadBalances()
-            if (payload.optString("type") in transactionNotifications) {
-                assertUiThread()
-                // TODO notification API should give us a currency to update
-                // update currently selected transaction list
-                transactionManager.loadTransactions()
-            }
-            // refresh pending ops and history with each notification
-            if (devMode.value == true) {
-                pendingOperationsManager.getPending()
-            }
-        }
-    }
+    private val api = WalletBackendApi(app, this, this)
 
     val withdrawManager = WithdrawManager(api, viewModelScope)
     val tipManager = TipManager(api, viewModelScope)
     val paymentManager = PaymentManager(api, viewModelScope)
-    val pendingOperationsManager: PendingOperationsManager = 
PendingOperationsManager(api)
+    val pendingOperationsManager: PendingOperationsManager =
+        PendingOperationsManager(api, viewModelScope)
     val transactionManager: TransactionManager = TransactionManager(api, 
viewModelScope)
     val refundManager = RefundManager(api, viewModelScope)
     val exchangeManager: ExchangeManager = ExchangeManager(api, viewModelScope)
@@ -117,9 +104,25 @@ class MainViewModel(val app: Application) : 
AndroidViewModel(app) {
     )
     val lastBackup: LiveData<Long> = mLastBackup
 
-    override fun onCleared() {
-        api.destroy()
-        super.onCleared()
+    override fun onVersionReceived(versionInfo: WalletCoreVersion) {
+        exchangeVersion = versionInfo.exchange
+        merchantVersion = versionInfo.merchant
+    }
+
+    override fun onNotificationReceived(payload: NotificationPayload) {
+        if (payload.type == "waiting-for-retry") return // ignore ping)
+        Log.i(TAG, "Received notification from wallet-core: $payload")
+
+        loadBalances()
+        if (payload.type in transactionNotifications) 
viewModelScope.launch(Dispatchers.Main) {
+            // TODO notification API should give us a currency to update
+            // update currently selected transaction list
+            transactionManager.loadTransactions()
+        }
+        // refresh pending ops and history with each notification
+        if (devMode.value == true) {
+            pendingOperationsManager.getPending()
+        }
     }
 
     @UiThread
@@ -174,22 +177,29 @@ class MainViewModel(val app: Application) : 
AndroidViewModel(app) {
 
     @UiThread
     fun dangerouslyReset() {
-        api.sendRequest("reset")
+        viewModelScope.launch {
+            api.sendRequest("reset")
+        }
         withdrawManager.testWithdrawalStatus.value = null
         mBalances.value = emptyList()
     }
 
     fun startTunnel() {
-        api.sendRequest("startTunnel")
+        viewModelScope.launch {
+            api.sendRequest("startTunnel")
+        }
     }
 
     fun stopTunnel() {
-        api.sendRequest("stopTunnel")
+        viewModelScope.launch {
+            api.sendRequest("stopTunnel")
+        }
     }
 
     fun tunnelResponse(resp: String) {
-        val respJson = JSONObject(resp)
-        api.sendRequest("tunnelResponse", respJson)
+        viewModelScope.launch {
+            api.sendRequest("tunnelResponse", JSONObject(resp))
+        }
     }
 
     @UiThread
diff --git a/wallet/src/main/java/net/taler/wallet/WalletApp.kt 
b/wallet/src/main/java/net/taler/wallet/WalletApp.kt
index 1076364..1384f76 100644
--- a/wallet/src/main/java/net/taler/wallet/WalletApp.kt
+++ b/wallet/src/main/java/net/taler/wallet/WalletApp.kt
@@ -19,7 +19,7 @@ package net.taler.wallet
 import android.app.Application
 import com.google.android.material.color.DynamicColors
 
-class WalletApp: Application() {
+class WalletApp : Application() {
     override fun onCreate() {
         super.onCreate()
         DynamicColors.applyToActivitiesIfAvailable(this)
diff --git a/wallet/src/main/java/net/taler/wallet/backend/ApiResponse.kt 
b/wallet/src/main/java/net/taler/wallet/backend/ApiResponse.kt
new file mode 100644
index 0000000..46eb2f0
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/backend/ApiResponse.kt
@@ -0,0 +1,61 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2023 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.backend
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.JsonObject
+
+@Serializable
+sealed class ApiMessage {
+
+    @Serializable
+    @SerialName("notification")
+    data class Notification(
+        val payload: NotificationPayload,
+    ) : ApiMessage()
+
+}
+
+@Serializable
+data class NotificationPayload(
+    val type: String,
+    val id: String? = null,
+)
+
+@Serializable
+sealed class ApiResponse : ApiMessage() {
+
+    abstract val id: Int
+    abstract val operation: String
+
+    @Serializable
+    @SerialName("response")
+    data class Response(
+        override val id: Int,
+        override val operation: String,
+        val result: JsonObject,
+    ) : ApiResponse()
+
+    @Serializable
+    @SerialName("error")
+    data class Error(
+        override val id: Int,
+        override val operation: String,
+        val error: JsonObject,
+    ) : ApiResponse()
+}
diff --git a/wallet/src/main/java/net/taler/wallet/backend/BackendManager.kt 
b/wallet/src/main/java/net/taler/wallet/backend/BackendManager.kt
new file mode 100644
index 0000000..ae338e8
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/backend/BackendManager.kt
@@ -0,0 +1,89 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2022 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.backend
+
+import android.util.Log
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
+import net.taler.qtart.TalerWalletCore
+import net.taler.wallet.BuildConfig
+import org.json.JSONObject
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+
+
+fun interface NotificationReceiver {
+    fun onNotificationReceived(payload: NotificationPayload)
+}
+
+class BackendManager(
+    private val notificationReceiver: NotificationReceiver,
+) {
+
+    companion object {
+        private const val TAG = "BackendManager"
+        private const val TAG_CORE = "taler-wallet-embedded"
+        val json = Json {
+            ignoreUnknownKeys = true
+        }
+    }
+
+    private val walletCore = TalerWalletCore()
+    private val requestManager = RequestManager()
+
+    init {
+        walletCore.setMessageHandler { onMessageReceived(it) }
+        if (BuildConfig.DEBUG) walletCore.setStdoutHandler {
+            Log.d(TAG_CORE, it)
+        }
+    }
+
+    fun run() {
+        walletCore.run()
+    }
+
+    suspend fun send(operation: String, args: JSONObject? = null): ApiResponse 
=
+        suspendCoroutine { cont ->
+            requestManager.addRequest(cont) { id ->
+                val request = JSONObject().apply {
+                    put("id", id)
+                    put("operation", operation)
+                    if (args != null) put("args", args)
+                }
+                Log.d(TAG, "sending message:\n${request.toString(2)}")
+                walletCore.sendRequest(request.toString())
+            }
+        }
+
+    private fun onMessageReceived(msg: String) {
+        Log.d(TAG, "message received: $msg")
+        when (val message = json.decodeFromString<ApiMessage>(msg)) {
+            is ApiMessage.Notification -> {
+                notificationReceiver.onNotificationReceived(message.payload)
+            }
+            is ApiResponse -> {
+                val id = message.id
+                val cont = requestManager.getAndRemoveContinuation(id)
+                if (cont == null) {
+                    Log.e(TAG, "wallet returned unknown request ID ($id)")
+                } else {
+                    cont.resume(message)
+                }
+            }
+        }
+    }
+}
diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalanceResponse.kt 
b/wallet/src/main/java/net/taler/wallet/backend/InitResponse.kt
similarity index 63%
copy from wallet/src/main/java/net/taler/wallet/balances/BalanceResponse.kt
copy to wallet/src/main/java/net/taler/wallet/backend/InitResponse.kt
index d1a111f..076af87 100644
--- a/wallet/src/main/java/net/taler/wallet/balances/BalanceResponse.kt
+++ b/wallet/src/main/java/net/taler/wallet/backend/InitResponse.kt
@@ -1,6 +1,6 @@
 /*
  * This file is part of GNU Taler
- * (C) 2020 Taler Systems S.A.
+ * (C) 2023 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
@@ -14,11 +14,25 @@
  * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-package net.taler.wallet.balances
+package net.taler.wallet.backend
 
 import kotlinx.serialization.Serializable
 
 @Serializable
-data class BalanceResponse(
-    val balances: List<BalanceItem>
+data class InitResponse(
+    val versionInfo: WalletCoreVersion,
+)
+
+fun interface VersionReceiver {
+    fun onVersionReceived(versionInfo: WalletCoreVersion)
+}
+
+@Serializable
+data class WalletCoreVersion(
+    val hash: String? = null,
+    val version: String,
+    val exchange: String,
+    val merchant: String,
+    val bank: String,
+    val devMode: Boolean,
 )
diff --git a/wallet/src/main/java/net/taler/wallet/WalletApp.kt 
b/wallet/src/main/java/net/taler/wallet/backend/RequestManager.kt
similarity index 52%
copy from wallet/src/main/java/net/taler/wallet/WalletApp.kt
copy to wallet/src/main/java/net/taler/wallet/backend/RequestManager.kt
index 1076364..041656e 100644
--- a/wallet/src/main/java/net/taler/wallet/WalletApp.kt
+++ b/wallet/src/main/java/net/taler/wallet/backend/RequestManager.kt
@@ -14,14 +14,31 @@
  * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-package net.taler.wallet
+package net.taler.wallet.backend
 
-import android.app.Application
-import com.google.android.material.color.DynamicColors
+import androidx.annotation.GuardedBy
+import java.util.concurrent.ConcurrentHashMap
+import kotlin.coroutines.Continuation
 
-class WalletApp: Application() {
-    override fun onCreate() {
-        super.onCreate()
-        DynamicColors.applyToActivitiesIfAvailable(this)
+class RequestManager {
+
+    @GuardedBy("this")
+    private val contMap = ConcurrentHashMap<Int, Continuation<ApiResponse>>()
+
+    @Volatile
+    @GuardedBy("this")
+    private var currentId = 0
+
+    @Synchronized
+    fun addRequest(cont: Continuation<ApiResponse>, block: (Int) -> Unit) {
+        val id = currentId++
+        contMap[id] = cont
+        block(id)
     }
+
+    @Synchronized
+    fun getAndRemoveContinuation(id: Int): Continuation<ApiResponse>? {
+        return contMap.remove(id)
+    }
+
 }
diff --git a/wallet/src/main/java/net/taler/wallet/backend/TalerErrorCode.kt 
b/wallet/src/main/java/net/taler/wallet/backend/TalerErrorCode.kt
index edcfd17..2242e33 100644
--- a/wallet/src/main/java/net/taler/wallet/backend/TalerErrorCode.kt
+++ b/wallet/src/main/java/net/taler/wallet/backend/TalerErrorCode.kt
@@ -3879,9 +3879,10 @@ enum class TalerErrorCode(val code: Int) {
 
 @OptIn(ExperimentalSerializationApi::class)
 @Serializer(forClass = TalerErrorCode::class)
-object TalerErrorCodeSerializer: KSerializer<TalerErrorCode> {
+object TalerErrorCodeSerializer : KSerializer<TalerErrorCode> {
 
-    override val descriptor = 
PrimitiveSerialDescriptor("TalerErrorCodeSerializer", PrimitiveKind.INT)
+    override val descriptor =
+        PrimitiveSerialDescriptor("TalerErrorCodeSerializer", 
PrimitiveKind.INT)
 
     override fun deserialize(decoder: Decoder): TalerErrorCode {
         val code = decoder.decodeInt()
diff --git a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt 
b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
index 8ec5873..06b8cee 100644
--- a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
+++ b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
@@ -17,130 +17,48 @@
 package net.taler.wallet.backend
 
 import android.app.Application
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.content.ServiceConnection
-import android.os.Handler
-import android.os.IBinder
-import android.os.Message
-import android.os.Messenger
-import android.util.Log
+import kotlinx.coroutines.DelicateCoroutinesApi
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import kotlinx.serialization.KSerializer
-import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.decodeFromJsonElement
 import net.taler.wallet.backend.TalerErrorCode.NONE
-import net.taler.wallet.backend.WalletBackendService.Companion.MSG_COMMAND
-import net.taler.wallet.backend.WalletBackendService.Companion.MSG_NOTIFY
-import net.taler.wallet.backend.WalletBackendService.Companion.MSG_REPLY
-import 
net.taler.wallet.backend.WalletBackendService.Companion.MSG_SUBSCRIBE_NOTIFY
 import org.json.JSONObject
-import java.lang.ref.WeakReference
-import java.util.LinkedList
-import java.util.concurrent.ConcurrentHashMap
-import java.util.concurrent.atomic.AtomicInteger
-import kotlin.coroutines.resume
-import kotlin.coroutines.suspendCoroutine
 
+const val WALLET_DB = "talerwalletdb-v30.json"
+
+@OptIn(DelicateCoroutinesApi::class)
 class WalletBackendApi(
-    private val app: Application,
-    private val notificationHandler: ((payload: JSONObject) -> Unit),
+    app: Application,
+    private val versionReceiver: VersionReceiver,
+    notificationReceiver: NotificationReceiver,
 ) {
-    private var walletBackendMessenger: Messenger? = null
-    private val queuedMessages = LinkedList<Message>()
-    private val handlers = ConcurrentHashMap<Int, (isError: Boolean, message: 
JSONObject) -> Unit>()
-    private var nextRequestID = AtomicInteger(0)
-    private val incomingMessenger = Messenger(IncomingHandler(this))
-
-    private val walletBackendConn = object : ServiceConnection {
-        override fun onServiceDisconnected(p0: ComponentName?) {
-            Log.w(TAG, "wallet backend service disconnected (crash?)")
-            walletBackendMessenger = null
-        }
 
-        override fun onServiceConnected(componentName: ComponentName?, binder: 
IBinder?) {
-            Log.i(TAG, "connected to wallet backend service")
-            val bm = Messenger(binder)
-            walletBackendMessenger = bm
-            pumpQueue(bm)
-            val msg = Message.obtain(null, MSG_SUBSCRIBE_NOTIFY)
-            msg.replyTo = incomingMessenger
-            bm.send(msg)
-        }
-    }
+    private val backendManager = BackendManager(notificationReceiver)
+    private val dbPath = "${app.filesDir}/${WALLET_DB}"
 
     init {
-        Intent(app, WalletBackendService::class.java).also { intent ->
-            app.bindService(intent, walletBackendConn, 
Context.BIND_AUTO_CREATE)
-        }
-    }
-
-    private class IncomingHandler(strongApi: WalletBackendApi) : Handler() {
-        private val weakApi = WeakReference(strongApi)
-        override fun handleMessage(msg: Message) {
-            val api = weakApi.get() ?: return
-            when (msg.what) {
-                MSG_REPLY -> {
-                    val requestID = msg.data.getInt("requestID", 0)
-                    val operation = msg.data.getString("operation", "??")
-                    Log.i(TAG, "got reply for operation $operation 
($requestID)")
-                    val h = api.handlers.remove(requestID)
-                    if (h == null) {
-                        Log.e(TAG, "request ID not associated with a handler")
-                        return
-                    }
-                    val response = msg.data.getString("response")
-                    if (response == null) {
-                        Log.e(TAG, "response did not contain response payload")
-                        return
-                    }
-                    val isError = msg.data.getBoolean("isError")
-                    val json = JSONObject(response)
-                    h(isError, json)
-                }
-                MSG_NOTIFY -> {
-                    val payloadStr = msg.data.getString("payload")
-                    if (payloadStr == null) {
-                        Log.e(TAG, "Notification had no payload: $msg")
-                    } else {
-                        val payload = JSONObject(payloadStr)
-                        api.notificationHandler.invoke(payload)
-                    }
-                }
-            }
+        GlobalScope.launch(Dispatchers.IO) {
+            backendManager.run()
+            sendInitMessage()
         }
     }
 
-    private fun pumpQueue(bm: Messenger) {
-        while (true) {
-            val msg = queuedMessages.pollFirst() ?: return
-            bm.send(msg)
+    private suspend fun sendInitMessage() {
+        request("init", InitResponse.serializer()) {
+            put("persistentStoragePath", dbPath)
+            put("logLevel", "INFO")
+        }.onSuccess { response ->
+            versionReceiver.onVersionReceived(response.versionInfo)
+        }.onError { error ->
+            error("Error on init message: $error")
         }
     }
 
-    fun sendRequest(
-        operation: String,
-        args: JSONObject? = null,
-        onResponse: (isError: Boolean, message: JSONObject) -> Unit = { _, _ 
-> },
-    ) {
-        val requestID = nextRequestID.incrementAndGet()
-        Log.i(TAG, "sending request for operation $operation 
($requestID)\n${args?.toString(2)}")
-        val msg = Message.obtain(null, MSG_COMMAND)
-        handlers[requestID] = onResponse
-        msg.replyTo = incomingMessenger
-        val data = msg.data
-        data.putString("operation", operation)
-        data.putInt("requestID", requestID)
-        if (args != null) {
-            data.putString("args", args.toString())
-        }
-        val bm = walletBackendMessenger
-        if (bm != null) {
-            bm.send(msg)
-        } else {
-            queuedMessages.add(msg)
-        }
+    suspend fun sendRequest(operation: String, args: JSONObject? = null): 
ApiResponse {
+        return backendManager.send(operation, args)
     }
 
     suspend inline fun <reified T> request(
@@ -148,36 +66,23 @@ class WalletBackendApi(
         serializer: KSerializer<T>? = null,
         noinline args: (JSONObject.() -> JSONObject)? = null,
     ): WalletResponse<T> = withContext(Dispatchers.Default) {
-        suspendCoroutine { cont ->
-            val json = Json {
-                ignoreUnknownKeys = true
-            }
-            sendRequest(operation, args?.invoke(JSONObject())) { isError, 
message ->
-                val response = try {
-                    if (isError) {
-                        val error =
-                            json.decodeFromString(TalerErrorInfo.serializer(), 
message.toString())
-                        WalletResponse.Error(error)
-                    } else {
-                        val t: T = serializer?.let {
-                            json.decodeFromString(serializer, 
message.toString())
-                        } ?: Unit as T
-                        WalletResponse.Success(t)
-                    }
-                } catch (e: Exception) {
-                    val info = TalerErrorInfo(NONE, "", e.toString())
-                    WalletResponse.Error(info)
+        val json = BackendManager.json
+        try {
+            when (val response = sendRequest(operation, 
args?.invoke(JSONObject()))) {
+                is ApiResponse.Response -> {
+                    val t: T = serializer?.let {
+                        json.decodeFromJsonElement(serializer, response.result)
+                    } ?: Unit as T
+                    WalletResponse.Success(t)
+                }
+                is ApiResponse.Error -> {
+                    val error: TalerErrorInfo = 
json.decodeFromJsonElement(response.error)
+                    WalletResponse.Error(error)
                 }
-                cont.resume(response)
             }
+        } catch (e: Exception) {
+            val info = TalerErrorInfo(NONE, "", e.toString())
+            WalletResponse.Error(info)
         }
     }
-
-    fun destroy() {
-        // FIXME: implement this!
-    }
-
-    companion object {
-        const val TAG = "WalletBackendApi"
-    }
 }
diff --git 
a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendService.kt 
b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendService.kt
deleted file mode 100644
index 6411b8b..0000000
--- a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendService.kt
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * This file is part of GNU Taler
- * (C) 2020 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.backend
-
-import android.app.Service
-import android.content.Intent
-import android.os.Handler
-import android.os.IBinder
-import android.os.Message
-import android.os.Messenger
-import android.os.RemoteException
-import android.util.Log
-import net.taler.qtart.TalerWalletCore
-import net.taler.wallet.BuildConfig
-import net.taler.wallet.HostCardEmulatorService
-import org.json.JSONObject
-import java.lang.ref.WeakReference
-import java.util.LinkedList
-import java.util.concurrent.ConcurrentHashMap
-import kotlin.system.exitProcess
-
-private const val TAG = "taler-wallet-backend"
-const val WALLET_DB = "talerwalletdb-v30.json"
-
-class RequestData(val clientRequestId: Int, val messenger: Messenger)
-
-
-class WalletBackendService : Service() {
-    /**
-     * Target we publish for clients to send messages to IncomingHandler.
-     */
-    private val messenger: Messenger = Messenger(IncomingHandler(this))
-
-    private val walletCore = TalerWalletCore()
-
-    private var initialized = false
-
-    private var nextRequestID = 1
-
-    private val requests = ConcurrentHashMap<Int, RequestData>()
-
-    private val subscribers = LinkedList<Messenger>()
-
-    override fun onCreate() {
-        Log.i(TAG, "onCreate in wallet backend service")
-
-        walletCore.setMessageHandler {
-            this@WalletBackendService.handleAkonoMessage(it)
-        }
-        if (BuildConfig.DEBUG) walletCore.setStdoutHandler {
-            Log.d(TAG, it)
-        }
-        walletCore.run()
-        sendInitMessage()
-        // runIntegrationTest()
-        super.onCreate()
-    }
-
-    private fun sendInitMessage() {
-        val msg = JSONObject()
-        msg.put("operation", "init")
-        val args = JSONObject()
-        msg.put("args", args)
-        args.put("persistentStoragePath", "${application.filesDir}/$WALLET_DB")
-        args.put("logLevel", "INFO")
-        Log.d(TAG, "init message: ${msg.toString(2)}")
-        walletCore.sendRequest(msg.toString())
-    }
-
-    /**
-     * Run the integration tests for wallet-core.
-     */
-    private fun runIntegrationTest() {
-        val msg = JSONObject()
-        msg.put("operation", "runIntegrationTest")
-        val args = JSONObject()
-        msg.put("args", args)
-        args.put("amountToWithdraw", "KUDOS:3")
-        args.put("amountToSpend", "KUDOS:1")
-        args.put("bankBaseUrl", 
"https://bank.demo.taler.net/demobanks/default/access-api/";)
-        args.put("exchangeBaseUrl", "https://exchange.demo.taler.net/";)
-        args.put("merchantBaseUrl", "https://backend.demo.taler.net/";)
-        args.put("merchantAuthToken", "secret-token:sandbox")
-        Log.d(TAG, "integration test message: ${msg.toString(2)}")
-        walletCore.sendRequest(msg.toString())
-    }
-
-    /**
-     * Handler of incoming messages from clients.
-     */
-    class IncomingHandler(
-        service: WalletBackendService,
-    ) : Handler() {
-
-        private val serviceWeakRef = WeakReference(service)
-
-        override fun handleMessage(msg: Message) {
-            val svc = serviceWeakRef.get() ?: return
-            if (!svc.initialized) Log.w(TAG, "Warning: Not yet initialized")
-            when (msg.what) {
-                MSG_COMMAND -> {
-                    val data = msg.data
-                    val serviceRequestID = svc.nextRequestID++
-                    val clientRequestID = data.getInt("requestID", 0)
-                    if (clientRequestID == 0) {
-                        Log.e(TAG, "client requestID missing")
-                        return
-                    }
-                    val args = data.getString("args")
-                    val argsObj = if (args == null) {
-                        JSONObject()
-                    } else {
-                        JSONObject(args)
-                    }
-                    val operation = data.getString("operation", "")
-                    if (operation == "") {
-                        Log.e(TAG, "client command missing")
-                        return
-                    }
-                    Log.i(TAG, "got request for operation $operation")
-                    val request = JSONObject()
-                    request.put("operation", operation)
-                    request.put("id", serviceRequestID)
-                    request.put("args", argsObj)
-                    svc.walletCore.sendRequest(request.toString())
-                    Log.i(
-                        TAG,
-                        "mapping service request ID $serviceRequestID to 
client request ID $clientRequestID"
-                    )
-                    svc.requests[serviceRequestID] = 
RequestData(clientRequestID, msg.replyTo)
-                }
-                MSG_SUBSCRIBE_NOTIFY -> {
-                    Log.i(TAG, "subscribing client")
-                    val r = msg.replyTo
-                    if (r == null) {
-                        Log.e(
-                            TAG,
-                            "subscriber did not specify replyTo object in 
MSG_SUBSCRIBE_NOTIFY"
-                        )
-                    } else {
-                        svc.subscribers.add(msg.replyTo)
-                    }
-                }
-                MSG_UNSUBSCRIBE_NOTIFY -> {
-                    Log.i(TAG, "unsubscribing client")
-                    svc.subscribers.remove(msg.replyTo)
-                }
-                else -> {
-                    Log.e(TAG, "unknown message from client")
-                    super.handleMessage(msg)
-                }
-            }
-        }
-    }
-
-    override fun onBind(p0: Intent?): IBinder? {
-        return messenger.binder
-    }
-
-    private fun sendNotify(payload: String) {
-        var rm: LinkedList<Messenger>? = null
-        for (s in subscribers) {
-            val m = Message.obtain(null, MSG_NOTIFY)
-            val b = m.data
-            b.putString("payload", payload)
-            try {
-                s.send(m)
-            } catch (e: RemoteException) {
-                if (rm == null) {
-                    rm = LinkedList()
-                }
-                rm.add(s)
-                subscribers.remove(s)
-            }
-        }
-        if (rm != null) {
-            for (s in rm) {
-                subscribers.remove(s)
-            }
-        }
-    }
-
-    private fun handleAkonoMessage(messageStr: String) {
-        val message = JSONObject(messageStr)
-        when (val type = message.getString("type")) {
-            "notification" -> {
-                val payload = message.getJSONObject("payload")
-                if (payload.optString("type") != "waiting-for-retry") {
-                    Log.v(TAG, "got back notification: ${message.toString(2)}")
-                }
-                sendNotify(payload.toString())
-            }
-            "tunnelHttp" -> {
-                Log.v(TAG, "got http tunnel request! ${message.toString(2)}")
-                Intent().also { intent ->
-                    intent.action = HostCardEmulatorService.HTTP_TUNNEL_REQUEST
-                    intent.putExtra("tunnelMessage", messageStr)
-                    application.sendBroadcast(intent)
-                }
-            }
-            "response" -> {
-                when (message.getString("operation")) {
-                    "init" -> {
-                        Log.d(TAG, "got response for init operation: 
${message.toString(2)}")
-                        initialized = true
-                        sendNotify(message.toString(2))
-                    }
-                    "reset" -> {
-                        Log.v(TAG, "got back message: ${message.toString(2)}")
-                        exitProcess(1)
-                    }
-                    else -> {
-                        Log.v(TAG, "got back response: ${message.toString(2)}")
-                        val payload = 
message.getJSONObject("result").toString(2)
-                        handleResponse(false, message, payload)
-                    }
-                }
-            }
-            "error" -> {
-                Log.v(TAG, "got back error: ${message.toString(2)}")
-                val payload = message.getJSONObject("error").toString(2)
-                handleResponse(true, message, payload)
-            }
-            else -> throw IllegalArgumentException("Unknown message type: 
$type")
-        }
-    }
-
-    private fun handleResponse(isError: Boolean, message: JSONObject, payload: 
String) {
-        val id = message.getInt("id")
-        val rId = requests[id]
-        if (rId == null) {
-            Log.e(TAG, "wallet returned unknown request ID ($id)")
-            return
-        }
-        val m = Message.obtain(null, MSG_REPLY)
-        val b = m.data
-        b.putInt("requestID", rId.clientRequestId)
-        b.putBoolean("isError", isError)
-        b.putString("response", payload)
-        b.putString("operation", message.getString("operation"))
-        rId.messenger.send(m)
-    }
-
-    companion object {
-        const val MSG_SUBSCRIBE_NOTIFY = 1
-        const val MSG_UNSUBSCRIBE_NOTIFY = 2
-        const val MSG_COMMAND = 3
-        const val MSG_REPLY = 4
-        const val MSG_NOTIFY = 5
-    }
-}
diff --git 
a/wallet/src/main/java/net/taler/wallet/pending/PendingOperationsManager.kt 
b/wallet/src/main/java/net/taler/wallet/pending/PendingOperationsManager.kt
index df778ed..f5079f6 100644
--- a/wallet/src/main/java/net/taler/wallet/pending/PendingOperationsManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/pending/PendingOperationsManager.kt
@@ -18,40 +18,51 @@ package net.taler.wallet.pending
 
 import android.util.Log
 import androidx.lifecycle.MutableLiveData
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.serialization.json.jsonArray
 import net.taler.wallet.TAG
+import net.taler.wallet.backend.ApiResponse
 import net.taler.wallet.backend.WalletBackendApi
 import org.json.JSONObject
 
 open class PendingOperationInfo(
     val type: String,
-    val detail: JSONObject
+    val detail: JSONObject,
 )
 
-class PendingOperationsManager(private val walletBackendApi: WalletBackendApi) 
{
+class PendingOperationsManager(
+    private val walletBackendApi: WalletBackendApi,
+    private val scope: CoroutineScope,
+) {
 
     val pendingOperations = MutableLiveData<List<PendingOperationInfo>>()
 
     internal fun getPending() {
-        walletBackendApi.sendRequest("getPendingOperations") { isError, result 
->
-            if (isError) {
-                Log.i(TAG, "got getPending error result: 
${result.toString(2)}")
-                return@sendRequest
+        scope.launch {
+            val response = walletBackendApi.sendRequest("getPendingOperations")
+            if (response is ApiResponse.Error) {
+                Log.i(TAG, "got getPending error result: ${response.error}")
+                return@launch
+            } else if (response is ApiResponse.Response) {
+                Log.i(TAG, "got getPending result")
+                val pendingList = mutableListOf<PendingOperationInfo>()
+                val pendingJson = 
response.result["pendingOperations"]?.jsonArray ?: return@launch
+                for (i in 0 until pendingJson.size) {
+                    val p = JSONObject(pendingJson[i].toString())
+                    val type = p.getString("type")
+                    pendingList.add(PendingOperationInfo(type, p))
+                }
+                Log.i(TAG, "Got ${pendingList.size} pending operations")
+                pendingOperations.postValue((pendingList))
             }
-            Log.i(TAG, "got getPending result")
-            val pendingList = mutableListOf<PendingOperationInfo>()
-            val pendingJson = result.getJSONArray("pendingOperations")
-            for (i in 0 until pendingJson.length()) {
-                val p = pendingJson.getJSONObject(i)
-                val type = p.getString("type")
-                pendingList.add(PendingOperationInfo(type, p))
-            }
-            Log.i(TAG, "Got ${pendingList.size} pending operations")
-            pendingOperations.postValue((pendingList))
         }
     }
 
     fun retryPendingNow() {
-        walletBackendApi.sendRequest("retryPendingNow")
+        scope.launch {
+            walletBackendApi.sendRequest("retryPendingNow")
+        }
     }
 
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]