taler-android

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

WalletBackendApi.kt (5063B)


      1 /*
      2  * This file is part of GNU Taler
      3  * (C) 2020 Taler Systems S.A.
      4  *
      5  * GNU Taler is free software; you can redistribute it and/or modify it under the
      6  * terms of the GNU General Public License as published by the Free Software
      7  * Foundation; either version 3, or (at your option) any later version.
      8  *
      9  * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
     10  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11  * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License along with
     14  * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15  */
     16 
     17 package net.taler.wallet.backend
     18 
     19 import android.app.Application
     20 import kotlinx.coroutines.DelicateCoroutinesApi
     21 import kotlinx.coroutines.Dispatchers
     22 import kotlinx.coroutines.GlobalScope
     23 import kotlinx.coroutines.launch
     24 import kotlinx.coroutines.withContext
     25 import kotlinx.serialization.KSerializer
     26 import kotlinx.serialization.encodeToString
     27 import kotlinx.serialization.json.JsonObject
     28 import kotlinx.serialization.json.decodeFromJsonElement
     29 import net.taler.wallet.backend.TalerErrorCode.NONE
     30 import org.json.JSONObject
     31 import java.io.File
     32 
     33 private const val WALLET_DB = "talerwalletdb.sqlite3"
     34 
     35 @OptIn(DelicateCoroutinesApi::class)
     36 class WalletBackendApi(
     37     private val app: Application,
     38     private val initialConfig: WalletRunConfig,
     39     private val versionReceiver: VersionReceiver,
     40     notificationReceiver: NotificationReceiver,
     41 ) {
     42 
     43     private val backendManager = BackendManager(notificationReceiver)
     44 
     45     fun startWallet() {
     46         backendManager.run()
     47         GlobalScope.launch(Dispatchers.IO) {
     48             sendInitMessage()
     49         }
     50     }
     51 
     52     fun stopWallet() {
     53         GlobalScope.launch(Dispatchers.IO) {
     54             sendRequest("shutdown")
     55             backendManager.destroy()
     56         }
     57     }
     58 
     59     private suspend fun sendInitMessage() {
     60         val db = if (File(app.filesDir, "talerwalletdb.sql").isFile) {
     61             // can be removed after a reasonable migration period (2024-02-02)
     62             "${app.filesDir}/talerwalletdb.sql"
     63         } else {
     64             "${app.filesDir}/${WALLET_DB}"
     65         }
     66 
     67         request("init", InitResponse.serializer()) {
     68             put("persistentStoragePath", db)
     69             put("logLevel", "INFO")
     70             put("config", JSONObject(BackendManager.json.encodeToString(initialConfig)))
     71         }.onSuccess { response ->
     72             versionReceiver.onVersionReceived(response.versionInfo)
     73         }.onError { error ->
     74             // TODO expose this to the UI as it can happen when using an older DB version
     75             error("Error on init message: $error")
     76         }
     77     }
     78 
     79     suspend fun setWalletConfig(config: WalletRunConfig): WalletResponse<InitResponse> {
     80         return request("initWallet", InitResponse.serializer()) {
     81             put("config", JSONObject(BackendManager.json.encodeToString(config)))
     82         }
     83     }
     84 
     85     suspend fun sendRequest(operation: String, args: JSONObject? = null): ApiResponse {
     86         return backendManager.send(operation, args)
     87     }
     88 
     89     suspend inline fun <reified T> request(
     90         operation: String,
     91         serializer: KSerializer<T>? = null,
     92         noinline args: (JSONObject.() -> JSONObject)? = null,
     93     ): WalletResponse<T> = withContext(Dispatchers.Default) {
     94         val json = BackendManager.json
     95         try {
     96             when (val response = sendRequest(operation, args?.invoke(JSONObject()))) {
     97                 is ApiResponse.Response -> {
     98                     val t: T = serializer?.let {
     99                         json.decodeFromJsonElement(serializer, response.result)
    100                     } ?: Unit as T
    101                     WalletResponse.Success(t)
    102                 }
    103 
    104                 is ApiResponse.Error -> {
    105                     val error: TalerErrorInfo = json.decodeFromJsonElement(response.error)
    106                     WalletResponse.Error(error)
    107                 }
    108             }
    109         } catch (e: Exception) {
    110             val info = TalerErrorInfo(NONE, "", e.toString())
    111             WalletResponse.Error(info)
    112         }
    113     }
    114 
    115     // Returns raw JSON response instead of serialized object
    116     suspend inline fun rawRequest(
    117         operation: String,
    118         noinline args: (JSONObject.() -> JSONObject)? = null,
    119     ): WalletResponse<JsonObject> = withContext(Dispatchers.Default) {
    120         val json = BackendManager.json
    121         try {
    122             when (val response = sendRequest(operation, args?.invoke(JSONObject()))) {
    123                 is ApiResponse.Response -> {
    124                     WalletResponse.Success(response.result)
    125                 }
    126 
    127                 is ApiResponse.Error -> {
    128                     val error: TalerErrorInfo = json.decodeFromJsonElement(response.error)
    129                     WalletResponse.Error(error)
    130                 }
    131             }
    132         } catch (e: Exception) {
    133             val info = TalerErrorInfo(NONE, "", e.toString())
    134             WalletResponse.Error(info)
    135         }
    136     }
    137 }