summaryrefslogtreecommitdiff
path: root/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
diff options
context:
space:
mode:
Diffstat (limited to 'cashier/src/main/java/net/taler/cashier/MainViewModel.kt')
-rw-r--r--cashier/src/main/java/net/taler/cashier/MainViewModel.kt148
1 files changed, 148 insertions, 0 deletions
diff --git a/cashier/src/main/java/net/taler/cashier/MainViewModel.kt b/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
new file mode 100644
index 0000000..3874038
--- /dev/null
+++ b/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
@@ -0,0 +1,148 @@
+/*
+ * 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.cashier
+
+import android.annotation.SuppressLint
+import android.app.Application
+import android.util.Log
+import androidx.annotation.UiThread
+import androidx.annotation.WorkerThread
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import androidx.security.crypto.EncryptedSharedPreferences
+import androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV
+import androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
+import androidx.security.crypto.MasterKeys
+import androidx.security.crypto.MasterKeys.AES256_GCM_SPEC
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import net.taler.cashier.Amount.Companion.fromStringSigned
+import net.taler.cashier.HttpHelper.makeJsonGetRequest
+import net.taler.cashier.withdraw.WithdrawManager
+
+private val TAG = MainViewModel::class.java.simpleName
+
+private const val PREF_NAME = "net.taler.cashier.prefs"
+private const val PREF_KEY_BANK_URL = "bankUrl"
+private const val PREF_KEY_USERNAME = "username"
+private const val PREF_KEY_PASSWORD = "password"
+private const val PREF_KEY_CURRENCY = "currency"
+
+class MainViewModel(private val app: Application) : AndroidViewModel(app) {
+
+ val configDestination = ConfigFragmentDirections.actionGlobalConfigFragment()
+
+ private val masterKeyAlias = MasterKeys.getOrCreate(AES256_GCM_SPEC)
+ private val prefs = EncryptedSharedPreferences.create(
+ PREF_NAME, masterKeyAlias, app, AES256_SIV, AES256_GCM
+ )
+
+ internal var config = Config(
+ bankUrl = prefs.getString(PREF_KEY_BANK_URL, "")!!,
+ username = prefs.getString(PREF_KEY_USERNAME, "")!!,
+ password = prefs.getString(PREF_KEY_PASSWORD, "")!!
+ )
+
+ private val mCurrency = MutableLiveData<String>(
+ prefs.getString(PREF_KEY_CURRENCY, null)
+ )
+ internal val currency: LiveData<String> = mCurrency
+
+ private val mConfigResult = MutableLiveData<ConfigResult>()
+ val configResult: LiveData<ConfigResult> = mConfigResult
+
+ private val mBalance = MutableLiveData<BalanceResult>()
+ val balance: LiveData<BalanceResult> = mBalance
+
+ internal val withdrawManager = WithdrawManager(app, this)
+
+ fun hasConfig() = config.bankUrl.isNotEmpty()
+ && config.username.isNotEmpty()
+ && config.password.isNotEmpty()
+
+ /**
+ * Start observing [configResult] after calling this to get the result async.
+ * Warning: Ignore null results that are used to reset old results.
+ */
+ @UiThread
+ fun checkAndSaveConfig(config: Config) {
+ mConfigResult.value = null
+ viewModelScope.launch(Dispatchers.IO) {
+ val url = "${config.bankUrl}/accounts/${config.username}/balance"
+ Log.d(TAG, "Checking config: $url")
+ val result = when (val response = makeJsonGetRequest(url, config)) {
+ is HttpJsonResult.Success -> {
+ val balance = response.json.getString("balance")
+ val amount = fromStringSigned(balance)!!
+ mCurrency.postValue(amount.currency)
+ prefs.edit().putString(PREF_KEY_CURRENCY, amount.currency).apply()
+ // save config
+ saveConfig(config)
+ ConfigResult(true)
+ }
+ is HttpJsonResult.Error -> {
+ val authError = response.statusCode == 401
+ ConfigResult(false, authError)
+ }
+ }
+ mConfigResult.postValue(result)
+ }
+ }
+
+ @WorkerThread
+ @SuppressLint("ApplySharedPref")
+ private fun saveConfig(config: Config) {
+ this.config = config
+ prefs.edit()
+ .putString(PREF_KEY_BANK_URL, config.bankUrl)
+ .putString(PREF_KEY_USERNAME, config.username)
+ .putString(PREF_KEY_PASSWORD, config.password)
+ .commit()
+ }
+
+ fun getBalance() = viewModelScope.launch(Dispatchers.IO) {
+ check(hasConfig()) { "No config to get balance" }
+ val url = "${config.bankUrl}/accounts/${config.username}/balance"
+ Log.d(TAG, "Checking balance at $url")
+ val result = when (val response = makeJsonGetRequest(url, config)) {
+ is HttpJsonResult.Success -> {
+ val balance = response.json.getString("balance")
+ fromStringSigned(balance)?.let { BalanceResult.Success(it) } ?: BalanceResult.Error
+ }
+ is HttpJsonResult.Error -> {
+ if (app.isOnline()) BalanceResult.Error
+ else BalanceResult.Offline
+ }
+ }
+ mBalance.postValue(result)
+ }
+
+ fun lock() {
+ saveConfig(config.copy(password = ""))
+ }
+
+}
+
+data class Config(
+ val bankUrl: String,
+ val username: String,
+ val password: String
+)
+
+class ConfigResult(val success: Boolean, val authError: Boolean = false)