From a4796ec47d89a851b260b6fc195494547208a025 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 18 Mar 2020 14:24:41 -0300 Subject: Merge all three apps into one repository --- .../net/taler/merchantpos/config/ConfigManager.kt | 181 +++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt (limited to 'merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt') diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt new file mode 100644 index 0000000..edb8059 --- /dev/null +++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt @@ -0,0 +1,181 @@ +/* + * 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 + */ + +package net.taler.merchantpos.config + +import android.content.Context +import android.content.Context.MODE_PRIVATE +import android.util.Base64.NO_WRAP +import android.util.Base64.encodeToString +import android.util.Log +import androidx.annotation.UiThread +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.android.volley.Request.Method.GET +import com.android.volley.RequestQueue +import com.android.volley.Response.ErrorListener +import com.android.volley.Response.Listener +import com.android.volley.VolleyError +import com.android.volley.toolbox.JsonObjectRequest +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import net.taler.merchantpos.R +import org.json.JSONObject + +private const val SETTINGS_NAME = "taler-merchant-terminal" + +private const val SETTINGS_CONFIG_URL = "configUrl" +private const val SETTINGS_USERNAME = "username" +private const val SETTINGS_PASSWORD = "password" + +internal const val CONFIG_URL_DEMO = "https://docs.taler.net/_static/sample-pos-config.json" +internal const val CONFIG_USERNAME_DEMO = "" +internal const val CONFIG_PASSWORD_DEMO = "" + +private val TAG = ConfigManager::class.java.simpleName + +interface ConfigurationReceiver { + /** + * Returns null if the configuration was valid, or a error string for user display otherwise. + */ + suspend fun onConfigurationReceived(json: JSONObject, currency: String): String? +} + +class ConfigManager( + private val context: Context, + private val scope: CoroutineScope, + private val mapper: ObjectMapper, + private val queue: RequestQueue +) { + + private val prefs = context.getSharedPreferences(SETTINGS_NAME, MODE_PRIVATE) + private val configurationReceivers = ArrayList() + + var config = Config( + configUrl = prefs.getString(SETTINGS_CONFIG_URL, CONFIG_URL_DEMO)!!, + username = prefs.getString(SETTINGS_USERNAME, CONFIG_USERNAME_DEMO)!!, + password = prefs.getString(SETTINGS_PASSWORD, CONFIG_PASSWORD_DEMO)!! + ) + var merchantConfig: MerchantConfig? = null + private set + + private val mConfigUpdateResult = MutableLiveData() + val configUpdateResult: LiveData = mConfigUpdateResult + + fun addConfigurationReceiver(receiver: ConfigurationReceiver) { + configurationReceivers.add(receiver) + } + + @UiThread + fun fetchConfig(config: Config, save: Boolean, savePassword: Boolean = false) { + mConfigUpdateResult.value = null + val configToSave = if (save) { + if (savePassword) config else config.copy(password = "") + } else null + + val stringRequest = object : JsonObjectRequest(GET, config.configUrl, null, + Listener { onConfigReceived(it, configToSave) }, + ErrorListener { onNetworkError(it) } + ) { + // send basic auth header + override fun getHeaders(): MutableMap { + val credentials = "${config.username}:${config.password}" + val auth = ("Basic ${encodeToString(credentials.toByteArray(), NO_WRAP)}") + return mutableMapOf("Authorization" to auth) + } + } + queue.add(stringRequest) + } + + @UiThread + private fun onConfigReceived(json: JSONObject, config: Config?) { + val merchantConfig: MerchantConfig = try { + mapper.readValue(json.getString("config")) + } catch (e: Exception) { + Log.e(TAG, "Error parsing merchant config", e) + val msg = context.getString(R.string.config_error_malformed) + mConfigUpdateResult.value = ConfigUpdateResult.Error(msg) + return + } + + val params = mapOf("instance" to merchantConfig.instance) + val req = MerchantRequest(GET, merchantConfig, "config", params, null, + Listener { onMerchantConfigReceived(config, json, merchantConfig, it) }, + ErrorListener { onNetworkError(it) } + ) + queue.add(req) + } + + private fun onMerchantConfigReceived( + newConfig: Config?, + configJson: JSONObject, + merchantConfig: MerchantConfig, + json: JSONObject + ) = scope.launch(Dispatchers.Default) { + val currency = json.getString("currency") + + for (receiver in configurationReceivers) { + val result = try { + receiver.onConfigurationReceived(configJson, currency) + } catch (e: Exception) { + Log.e(TAG, "Error handling configuration by ${receiver::class.java.simpleName}", e) + context.getString(R.string.config_error_unknown) + } + if (result != null) { // error + mConfigUpdateResult.postValue(ConfigUpdateResult.Error(result)) + return@launch + } + } + newConfig?.let { + config = it + saveConfig(it) + } + this@ConfigManager.merchantConfig = merchantConfig.copy(currency = currency) + mConfigUpdateResult.postValue(ConfigUpdateResult.Success(currency)) + } + + fun forgetPassword() { + config = config.copy(password = "") + saveConfig(config) + merchantConfig = null + } + + private fun saveConfig(config: Config) { + prefs.edit() + .putString(SETTINGS_CONFIG_URL, config.configUrl) + .putString(SETTINGS_USERNAME, config.username) + .putString(SETTINGS_PASSWORD, config.password) + .apply() + } + + @UiThread + private fun onNetworkError(it: VolleyError?) { + val msg = context.getString( + if (it?.networkResponse?.statusCode == 401) R.string.config_auth_error + else R.string.config_error_network + ) + mConfigUpdateResult.value = ConfigUpdateResult.Error(msg) + } + +} + +sealed class ConfigUpdateResult { + data class Error(val msg: String) : ConfigUpdateResult() + data class Success(val currency: String) : ConfigUpdateResult() +} -- cgit v1.2.3