MainViewModel.kt (4327B)
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.cashier 18 19 import android.app.Application 20 import android.util.Log 21 import androidx.lifecycle.AndroidViewModel 22 import androidx.lifecycle.LiveData 23 import androidx.lifecycle.MutableLiveData 24 import androidx.lifecycle.viewModelScope 25 import io.ktor.client.HttpClient 26 import io.ktor.client.engine.okhttp.OkHttp 27 import io.ktor.client.plugins.contentnegotiation.ContentNegotiation 28 import io.ktor.serialization.kotlinx.json.json 29 import kotlinx.coroutines.Dispatchers 30 import kotlinx.coroutines.launch 31 import kotlinx.serialization.json.Json 32 import net.taler.cashier.HttpHelper.makeJsonGetRequest 33 import net.taler.cashier.config.ConfigManager 34 import net.taler.cashier.withdraw.WithdrawManager 35 import net.taler.common.Amount 36 import net.taler.common.AmountParserException 37 import net.taler.lib.android.isOnline 38 39 private val TAG = MainViewModel::class.java.simpleName 40 41 class MainViewModel(private val app: Application) : AndroidViewModel(app) { 42 43 private val httpClient = HttpClient(OkHttp) { 44 engine { 45 config { 46 retryOnConnectionFailure(true) 47 } 48 } 49 expectSuccess = true 50 install(ContentNegotiation) { 51 json(Json { 52 encodeDefaults = false 53 ignoreUnknownKeys = true 54 }) 55 } 56 } 57 val configManager = ConfigManager(app, viewModelScope, httpClient) 58 59 private val mBalance = MutableLiveData<BalanceResult>() 60 val balance: LiveData<BalanceResult> = mBalance 61 62 internal val withdrawManager = WithdrawManager(app, this) 63 64 fun getBalance() = viewModelScope.launch(Dispatchers.IO) { 65 check(configManager.hasConfig()) { "No config to get balance" } 66 val config = configManager.config 67 val url = "${config.bankUrl}/accounts/${config.username}" 68 Log.d(TAG, "Checking balance at $url") 69 val result = when (val response = makeJsonGetRequest(url, config)) { 70 is HttpJsonResult.Success -> { 71 try { 72 val balanceObj = response.json.getJSONObject("balance") 73 val balanceAmount = balanceObj.getString("amount") 74 val debitThreshold = response.json.getString("debit_threshold") 75 val minCashout = if (response.json.has("min_cashout")) { 76 response.json.getString("min_cashout") 77 } else null 78 val positive = when (val creditDebitIndicator = 79 balanceObj.getString("credit_debit_indicator")) { 80 "credit" -> true 81 "debit" -> false 82 else -> throw AmountParserException("Unexpected credit_debit_indicator: $creditDebitIndicator") 83 } 84 BalanceResult.Success( 85 amount = SignedAmount(positive, Amount.fromJSONString(balanceAmount)), 86 debitThreshold = Amount.fromJSONString(debitThreshold), 87 minCashout = minCashout?.let { Amount.fromJSONString(it) } 88 89 ) 90 } catch (e: Exception) { 91 Log.e(TAG, "Error parsing balance", e) 92 BalanceResult.Error("Invalid amount:\n${response.json.toString(2)}") 93 } 94 } 95 is HttpJsonResult.Error -> { 96 if (app.isOnline()) BalanceResult.Error(response.msg) 97 else BalanceResult.Offline 98 } 99 } 100 mBalance.postValue(result) 101 } 102 103 fun lock() = viewModelScope.launch(Dispatchers.IO) { 104 configManager.lock() 105 } 106 107 }