diff options
Diffstat (limited to 'cashier/src/main')
25 files changed, 303 insertions, 123 deletions
diff --git a/cashier/src/main/AndroidManifest.xml b/cashier/src/main/AndroidManifest.xml index 1d8b810..5230be9 100644 --- a/cashier/src/main/AndroidManifest.xml +++ b/cashier/src/main/AndroidManifest.xml @@ -1,13 +1,18 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - package="net.taler.cashier"> + xmlns:tools="http://schemas.android.com/tools"> + + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.NFC" /> <application android:allowBackup="true" + android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_descriptor" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" + android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" android:theme="@style/AppTheme" @@ -15,6 +20,7 @@ <activity android:name=".MainActivity" + android:exported="true" android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> diff --git a/cashier/src/main/java/net/taler/cashier/AboutDialogFragment.kt b/cashier/src/main/java/net/taler/cashier/AboutDialogFragment.kt index cdea792..90becbd 100644 --- a/cashier/src/main/java/net/taler/cashier/AboutDialogFragment.kt +++ b/cashier/src/main/java/net/taler/cashier/AboutDialogFragment.kt @@ -17,20 +17,14 @@ package net.taler.cashier import android.os.Bundle -import android.os.Handler import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.WindowManager -import android.widget.Button -import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.DialogFragment -import androidx.fragment.app.Fragment import net.taler.cashier.BuildConfig.VERSION_NAME import net.taler.cashier.config.VERSION_BANK import net.taler.cashier.databinding.FragmentAboutDialogBinding -import net.taler.cashier.databinding.FragmentBalanceBinding -import net.taler.lib.common.Version +import net.taler.common.Version class AboutDialogFragment : DialogFragment() { @@ -39,8 +33,8 @@ class AboutDialogFragment : DialogFragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { + savedInstanceState: Bundle?, + ): View { ui = FragmentAboutDialogBinding.inflate(layoutInflater, container, false) return ui.root } @@ -50,8 +44,10 @@ class AboutDialogFragment : DialogFragment() { ui.versionView.text = getString(R.string.about_version, VERSION_NAME) ui.bankVersionView.text = getString(R.string.about_supported_bank_api, VERSION_BANK.str()) - ui.licenseView.text = getString(R.string.about_license, getString(R.string.about_license_content)) - ui.copyrightView.text = getString(R.string.about_copyright, getString(R.string.about_copyright_holder)) + ui.licenseView.text = + getString(R.string.about_license, getString(R.string.about_license_content)) + ui.copyrightView.text = + getString(R.string.about_copyright, getString(R.string.about_copyright_holder)) ui.button.setOnClickListener { dismiss() } } diff --git a/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt b/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt index 002301c..4fd3143 100644 --- a/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt +++ b/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt @@ -32,10 +32,10 @@ import net.taler.cashier.BalanceFragmentDirections.Companion.actionBalanceFragme import net.taler.cashier.databinding.FragmentBalanceBinding import net.taler.cashier.withdraw.LastTransaction import net.taler.cashier.withdraw.WithdrawStatus +import net.taler.common.Amount import net.taler.common.exhaustive import net.taler.common.fadeIn import net.taler.common.fadeOut -import net.taler.lib.common.Amount sealed class BalanceResult { class Error(val msg: String) : BalanceResult() @@ -54,19 +54,19 @@ class BalanceFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, - ): View? { + ): View { setHasOptionsMenu(true) ui = FragmentBalanceBinding.inflate(layoutInflater, container, false) return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - withdrawManager.lastTransaction.observe(viewLifecycleOwner, { lastTransaction -> + withdrawManager.lastTransaction.observe(viewLifecycleOwner) { lastTransaction -> onLastTransaction(lastTransaction) - }) - viewModel.balance.observe(viewLifecycleOwner, { result -> + } + viewModel.balance.observe(viewLifecycleOwner) { result -> onBalanceUpdated(result) - }) + } ui.button5.setOnClickListener { onAmountButtonPressed(5) } ui.button10.setOnClickListener { onAmountButtonPressed(10) } ui.button20.setOnClickListener { onAmountButtonPressed(20) } @@ -81,9 +81,9 @@ class BalanceFragment : Fragment() { true } else false } - configManager.currency.observe(viewLifecycleOwner, { currency -> + configManager.currency.observe(viewLifecycleOwner) { currency -> ui.currencyView.text = currency - }) + } ui.confirmWithdrawalButton.setOnClickListener { onAmountConfirmed(getAmountFromView()) } } @@ -104,11 +104,13 @@ class BalanceFragment : Fragment() { } } + @Deprecated("Deprecated in Java") override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.balance, menu) super.onCreateOptionsMenu(menu, inflater) } + @Deprecated("Deprecated in Java") override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { R.id.action_reconfigure -> { findNavController().navigate(configManager.configDestination) @@ -164,13 +166,19 @@ class BalanceFragment : Fragment() { private fun onAmountConfirmed(amount: Amount) { if (amount.isZero()) { ui.amountView.error = getString(R.string.withdraw_error_zero) - } else if (!withdrawManager.hasSufficientBalance(amount)) { - ui.amountView.error = getString(R.string.withdraw_error_insufficient_balance) - } else { - ui.amountView.error = null - withdrawManager.withdraw(amount) - actionBalanceFragmentToTransactionFragment().let { - findNavController().navigate(it) + } else when (withdrawManager.hasSufficientBalance(amount)) { + true -> { + ui.amountView.error = null + withdrawManager.withdraw(amount) + actionBalanceFragmentToTransactionFragment().let { + findNavController().navigate(it) + } + } + false -> { + ui.amountView.error = getString(R.string.withdraw_error_insufficient_balance) + } + null -> { + ui.amountView.error = getString(R.string.withdraw_error_currency_mismatch) } } } diff --git a/cashier/src/main/java/net/taler/cashier/HttpHelper.kt b/cashier/src/main/java/net/taler/cashier/HttpHelper.kt index fd48b2d..69cc46f 100644 --- a/cashier/src/main/java/net/taler/cashier/HttpHelper.kt +++ b/cashier/src/main/java/net/taler/cashier/HttpHelper.kt @@ -50,11 +50,13 @@ object HttpHelper { Log.e(TAG, "Error retrieving $url", e) return HttpJsonResult.Error(0) } - return if (response.code == 200 && response.body != null) { + return if (response.code == 204) { + HttpJsonResult.Success(JSONObject()) + } else if (response.code in 200..299 && response.body != null) { val jsonObject = JSONObject(response.body!!.string()) HttpJsonResult.Success(jsonObject) } else { - Log.e(TAG, "Received status ${response.code} from $url expected 200") + Log.e(TAG, "Received status ${response.code} from $url expected 2xx") HttpJsonResult.Error(response.code, getErrorBody(response)) } } @@ -76,11 +78,13 @@ object HttpHelper { Log.e(TAG, "Error retrieving $url", e) return HttpJsonResult.Error(0) } - return if (response.code == 200 && response.body != null) { + return if (response.code == 204) { + HttpJsonResult.Success(JSONObject()) + } else if (response.code in 200..299 && response.body != null) { val jsonObject = JSONObject(response.body!!.string()) HttpJsonResult.Success(jsonObject) } else { - Log.e(TAG, "Received status ${response.code} from $url expected 200") + Log.e(TAG, "Received status ${response.code} from $url expected 2xx") HttpJsonResult.Error(response.code, getErrorBody(response)) } } diff --git a/cashier/src/main/java/net/taler/cashier/MainActivity.kt b/cashier/src/main/java/net/taler/cashier/MainActivity.kt index 2f4c4ec..aacc225 100644 --- a/cashier/src/main/java/net/taler/cashier/MainActivity.kt +++ b/cashier/src/main/java/net/taler/cashier/MainActivity.kt @@ -52,6 +52,7 @@ class MainActivity : AppCompatActivity() { } } + @Deprecated("Deprecated in Java") override fun onBackPressed() { if (!configManager.hasConfig() && nav.currentDestination?.id == R.id.configFragment) { // we are in the configuration screen and need a config to continue diff --git a/cashier/src/main/java/net/taler/cashier/MainViewModel.kt b/cashier/src/main/java/net/taler/cashier/MainViewModel.kt index 253c7d5..2196e78 100644 --- a/cashier/src/main/java/net/taler/cashier/MainViewModel.kt +++ b/cashier/src/main/java/net/taler/cashier/MainViewModel.kt @@ -24,17 +24,17 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import io.ktor.client.HttpClient import io.ktor.client.engine.okhttp.OkHttp -import io.ktor.client.features.json.JsonFeature -import io.ktor.client.features.json.serializer.KotlinxSerializer +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.serialization.kotlinx.json.json import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.serialization.json.Json import net.taler.cashier.HttpHelper.makeJsonGetRequest import net.taler.cashier.config.ConfigManager import net.taler.cashier.withdraw.WithdrawManager +import net.taler.common.Amount +import net.taler.common.AmountParserException import net.taler.common.isOnline -import net.taler.lib.common.Amount -import net.taler.lib.common.AmountParserException private val TAG = MainViewModel::class.java.simpleName @@ -46,12 +46,12 @@ class MainViewModel(private val app: Application) : AndroidViewModel(app) { retryOnConnectionFailure(true) } } - install(JsonFeature) { - serializer = KotlinxSerializer( - Json { - ignoreUnknownKeys = true - } - ) + expectSuccess = true + install(ContentNegotiation) { + json(Json { + encodeDefaults = false + ignoreUnknownKeys = true + }) } } val configManager = ConfigManager(app, viewModelScope, httpClient) @@ -77,7 +77,8 @@ class MainViewModel(private val app: Application) : AndroidViewModel(app) { "debit" -> false else -> throw AmountParserException("Unexpected credit_debit_indicator: $creditDebitIndicator") } - BalanceResult.Success(SignedAmount(positive, Amount.fromJSONString(balanceAmount))) + BalanceResult.Success(SignedAmount(positive, + Amount.fromJSONString(balanceAmount))) } catch (e: Exception) { Log.e(TAG, "Error parsing balance", e) BalanceResult.Error("Invalid amount:\n${response.json.toString(2)}") diff --git a/cashier/src/main/java/net/taler/cashier/Response.kt b/cashier/src/main/java/net/taler/cashier/Response.kt index 6a72604..e9db447 100644 --- a/cashier/src/main/java/net/taler/cashier/Response.kt +++ b/cashier/src/main/java/net/taler/cashier/Response.kt @@ -18,8 +18,8 @@ package net.taler.cashier import android.content.Context import android.util.Log -import io.ktor.client.call.receive -import io.ktor.client.features.ResponseException +import io.ktor.client.call.body +import io.ktor.client.plugins.ResponseException import io.ktor.http.HttpStatusCode import kotlinx.serialization.Serializable import net.taler.common.isOnline @@ -46,8 +46,7 @@ class Response<out T> private constructor( private suspend fun getExceptionString(e: ResponseException): String { val response = e.response return try { - Log.e("TEST", "TRY RECEIVE $response") - val error: Error = response.receive() + val error: Error = response.body() "Error ${error.code}: ${error.hint}" } catch (ex: Exception) { "Status code: ${response.status.value}" diff --git a/cashier/src/main/java/net/taler/cashier/SignedAmount.kt b/cashier/src/main/java/net/taler/cashier/SignedAmount.kt index e79acfd..45bc3af 100644 --- a/cashier/src/main/java/net/taler/cashier/SignedAmount.kt +++ b/cashier/src/main/java/net/taler/cashier/SignedAmount.kt @@ -16,15 +16,17 @@ package net.taler.cashier -import net.taler.lib.common.Amount +import net.taler.common.Amount data class SignedAmount( val positive: Boolean, val amount: Amount ) { - override fun toString(): String { - return if (positive) "$amount" else "-$amount" - } + override fun toString() = toString(showSymbol = true) + fun toString(showSymbol: Boolean) = amount.toString( + showSymbol = showSymbol, + negative = !positive, + ) } diff --git a/cashier/src/main/java/net/taler/cashier/config/ConfigFragment.kt b/cashier/src/main/java/net/taler/cashier/config/ConfigFragment.kt index e6ac249..3085bef 100644 --- a/cashier/src/main/java/net/taler/cashier/config/ConfigFragment.kt +++ b/cashier/src/main/java/net/taler/cashier/config/ConfigFragment.kt @@ -37,22 +37,23 @@ import net.taler.cashier.MainViewModel import net.taler.cashier.R import net.taler.cashier.databinding.FragmentConfigBinding import net.taler.common.exhaustive +import net.taler.common.showError -private const val URL_BANK_TEST = "https://bank.test.taler.net" -private const val URL_BANK_TEST_REGISTER = "$URL_BANK_TEST/accounts/register" +private const val URL_BANK_TEST = "https://bank.demo.taler.net" +private const val URL_BANK_TEST_REGISTER = "https://bank.demo.taler.net/webui/#/register" class ConfigFragment : Fragment() { private val viewModel: MainViewModel by activityViewModels() - private val configManager by lazy { viewModel.configManager} + private val configManager by lazy { viewModel.configManager } private lateinit var ui: FragmentConfigBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { + savedInstanceState: Bundle?, + ): View { ui = FragmentConfigBinding.inflate(inflater, container, false) return ui.root } @@ -73,9 +74,9 @@ class ConfigFragment : Fragment() { } ui.saveButton.setOnClickListener { val config = Config( - bankUrl = ui.urlView.editText!!.text.toString(), - username = ui.usernameView.editText!!.text.toString(), - password = ui.passwordView.editText!!.text.toString() + bankUrl = ui.urlView.editText!!.text.toString().trim(), + username = ui.usernameView.editText!!.text.toString().trim(), + password = ui.passwordView.editText!!.text.toString().trim() ) if (checkConfig(config)) { // show progress @@ -110,13 +111,15 @@ class ConfigFragment : Fragment() { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) // for some reason automatic restore isn't working at the moment!? - outState.putCharSequence("urlView", ui.urlView.editText?.text) - outState.putCharSequence("usernameView", ui.usernameView.editText?.text) - outState.putCharSequence("passwordView", ui.passwordView.editText?.text) + outState.putCharSequence("urlView", ui.urlView.editText?.text?.trim()) + outState.putCharSequence("usernameView", ui.usernameView.editText?.text?.trim()) + outState.putCharSequence("passwordView", ui.passwordView.editText?.text?.trim()) } private fun checkConfig(config: Config): Boolean { - if (!config.bankUrl.startsWith("https://")) { + if (!config.bankUrl.startsWith("https://") && + !config.bankUrl.startsWith("http://") + ) { ui.urlView.error = getString(R.string.config_bank_url_error) ui.urlView.requestFocus() return false @@ -144,8 +147,7 @@ class ConfigFragment : Fragment() { if (result.authError) { Snackbar.make(requireView(), R.string.config_error_auth, LENGTH_LONG).show() } else { - val str = getString(R.string.config_error, result.msg) - Snackbar.make(requireView(), str, LENGTH_LONG).show() + requireActivity().showError(getString(R.string.config_error), result.msg) } } }.exhaustive diff --git a/cashier/src/main/java/net/taler/cashier/config/ConfigManager.kt b/cashier/src/main/java/net/taler/cashier/config/ConfigManager.kt index 0718963..50b1faf 100644 --- a/cashier/src/main/java/net/taler/cashier/config/ConfigManager.kt +++ b/cashier/src/main/java/net/taler/cashier/config/ConfigManager.kt @@ -29,6 +29,7 @@ import androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionSc import androidx.security.crypto.MasterKeys import androidx.security.crypto.MasterKeys.AES256_GCM_SPEC import io.ktor.client.HttpClient +import io.ktor.client.call.body import io.ktor.client.request.get import io.ktor.client.request.header import io.ktor.http.HttpHeaders.Authorization @@ -37,12 +38,13 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import net.taler.cashier.BuildConfig import net.taler.cashier.Response import net.taler.cashier.Response.Companion.response +import net.taler.common.Version import net.taler.common.getIncompatibleStringOrNull -import net.taler.lib.common.Version -val VERSION_BANK = Version(0, 0, 0) +val VERSION_BANK = Version.parse(BuildConfig.BACKEND_API_VERSION)!! private const val PREF_NAME = "net.taler.cashier.prefs" private const val PREF_KEY_BANK_URL = "bankUrl" private const val PREF_KEY_USERNAME = "username" @@ -54,7 +56,7 @@ private val TAG = ConfigManager::class.java.simpleName class ConfigManager( private val app: Application, private val scope: CoroutineScope, - private val httpClient: HttpClient + private val httpClient: HttpClient, ) { val configDestination = ConfigFragmentDirections.actionGlobalConfigFragment() @@ -116,7 +118,7 @@ class ConfigManager( val url = "${config.bankUrl}/config" Log.d(TAG, "Checking config: $url") val configResponse = response { - httpClient.get(url) as ConfigResponse + httpClient.get(url).body<ConfigResponse>() } if (configResponse.isFailure) { configResponse @@ -126,7 +128,7 @@ class ConfigManager( val balanceResponse = response { val authUrl = "${config.bankUrl}/accounts/${config.username}" Log.d(TAG, "Checking auth: $authUrl") - httpClient.get<Unit>(authUrl) { + httpClient.get(authUrl) { header(Authorization, config.basicAuth) } } diff --git a/cashier/src/main/java/net/taler/cashier/withdraw/ErrorFragment.kt b/cashier/src/main/java/net/taler/cashier/withdraw/ErrorFragment.kt index 4f98847..c951bb8 100644 --- a/cashier/src/main/java/net/taler/cashier/withdraw/ErrorFragment.kt +++ b/cashier/src/main/java/net/taler/cashier/withdraw/ErrorFragment.kt @@ -22,7 +22,6 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer import androidx.navigation.fragment.findNavController import net.taler.cashier.MainViewModel import net.taler.cashier.R @@ -37,22 +36,22 @@ class ErrorFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { + savedInstanceState: Bundle?, + ): View { ui = FragmentErrorBinding.inflate(inflater, container, false) return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - withdrawManager.withdrawStatus.observe(viewLifecycleOwner, Observer { status -> - if (status == null) return@Observer + withdrawManager.withdrawStatus.observe(viewLifecycleOwner) { status -> + if (status == null) return@observe if (status is WithdrawStatus.Aborted) { ui.textView.setText(R.string.transaction_aborted) } else if (status is WithdrawStatus.Error) { ui.textView.text = status.msg } withdrawManager.completeTransaction() - }) + } ui.backButton.setOnClickListener { findNavController().popBackStack() } diff --git a/cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt b/cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt index ffb1539..0f606b8 100644 --- a/cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt +++ b/cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt @@ -50,21 +50,21 @@ class TransactionFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { ui = FragmentTransactionBinding.inflate(inflater, container, false) return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - withdrawManager.withdrawAmount.observe(viewLifecycleOwner, { amount -> + withdrawManager.withdrawAmount.observe(viewLifecycleOwner) { amount -> ui.amountView.text = amount?.toString() - }) - withdrawManager.withdrawResult.observe(viewLifecycleOwner, { result -> + } + withdrawManager.withdrawResult.observe(viewLifecycleOwner) { result -> onWithdrawResultReceived(result) - }) - withdrawManager.withdrawStatus.observe(viewLifecycleOwner, { status -> + } + withdrawManager.withdrawStatus.observe(viewLifecycleOwner) { status -> onWithdrawStatusChanged(status) - }) + } // change intro text depending on whether NFC is available or not val hasNfc = NfcManager.hasNfc(requireContext()) diff --git a/cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt b/cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt index 5d34bba..487475d 100644 --- a/cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt +++ b/cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt @@ -34,21 +34,19 @@ import net.taler.cashier.HttpJsonResult.Error import net.taler.cashier.HttpJsonResult.Success import net.taler.cashier.MainViewModel import net.taler.cashier.R +import net.taler.common.Amount import net.taler.common.QrCodeManager.makeQrCode import net.taler.common.isOnline -import net.taler.lib.common.Amount import org.json.JSONObject -import java.util.concurrent.TimeUnit.MINUTES import java.util.concurrent.TimeUnit.SECONDS private val TAG = WithdrawManager::class.java.simpleName private val INTERVAL = SECONDS.toMillis(1) -private val TIMEOUT = MINUTES.toMillis(2) class WithdrawManager( private val app: Application, - private val viewModel: MainViewModel + private val viewModel: MainViewModel, ) { private val scope get() = viewModel.viewModelScope @@ -73,11 +71,20 @@ class WithdrawManager( private val mLastTransaction = MutableLiveData<LastTransaction>() val lastTransaction: LiveData<LastTransaction> = mLastTransaction + /** + * Returns null if the given [amount] can't be compared to the balance + * e.g. due to mismatching currency. + */ @UiThread - fun hasSufficientBalance(amount: Amount): Boolean { + fun hasSufficientBalance(amount: Amount): Boolean? { val balanceResult = viewModel.balance.value if (balanceResult !is BalanceResult.Success) return false - return balanceResult.amount.positive && amount <= balanceResult.amount.amount + return try { + balanceResult.amount.positive && amount <= balanceResult.amount.amount + } catch (e: IllegalStateException) { + Log.e(TAG, "Error comparing amounts", e) + null + } } @UiThread @@ -119,7 +126,7 @@ class WithdrawManager( } } - private val timer: CountDownTimer = object : CountDownTimer(TIMEOUT, INTERVAL) { + private val timer: CountDownTimer = object : CountDownTimer(Long.MAX_VALUE, INTERVAL) { override fun onTick(millisUntilFinished: Long) { val result = withdrawResult.value if (result is WithdrawResult.Success) { @@ -146,31 +153,32 @@ class WithdrawManager( } private fun checkWithdrawStatus(withdrawalId: String) = scope.launch(Dispatchers.IO) { - val url = "${config.bankUrl}/accounts/${config.username}/withdrawals/${withdrawalId}" + val url = + "${config.bankUrl}/withdrawals/${withdrawalId}" Log.d(TAG, "Checking withdraw status at $url") val response = makeJsonGetRequest(url, config) if (response !is Success) return@launch // ignore errors and continue trying val oldStatus = mWithdrawStatus.value try { - when { - response.json.getBoolean("aborted") -> { + when(response.json.getString("status")) { + "selected" -> { + // only update status, if there's none, yet + // so we don't re-notify or overwrite newer status info + if (oldStatus == null) { + mWithdrawStatus.postValue(WithdrawStatus.SelectionDone(withdrawalId)) + } + } + "aborted" -> { cancelWithdrawStatusCheck() mWithdrawStatus.postValue(WithdrawStatus.Aborted) } - response.json.getBoolean("confirmation_done") -> { + "confirmed" -> { if (oldStatus !is WithdrawStatus.Success) { cancelWithdrawStatusCheck() mWithdrawStatus.postValue(WithdrawStatus.Success) viewModel.getBalance() } } - response.json.getBoolean("selection_done") -> { - // only update status, if there's none, yet - // so we don't re-notify or overwrite newer status info - if (oldStatus == null) { - mWithdrawStatus.postValue(WithdrawStatus.SelectionDone(withdrawalId)) - } - } } } catch (e: Exception) { mWithdrawStatus.postValue(WithdrawStatus.Error(e.toString())) @@ -197,7 +205,8 @@ class WithdrawManager( } private fun abort(withdrawalId: String) = scope.launch(Dispatchers.IO) { - val url = "${config.bankUrl}/accounts/${config.username}/withdrawals/${withdrawalId}/abort" + val url = + "${config.bankUrl}/accounts/${config.username}/withdrawals/${withdrawalId}/abort" Log.d(TAG, "Aborting withdrawal at $url") makeJsonPostRequest(url, JSONObject(), config) } @@ -254,5 +263,5 @@ sealed class WithdrawStatus { data class LastTransaction( val withdrawAmount: Amount, - val withdrawStatus: WithdrawStatus + val withdrawStatus: WithdrawStatus, ) diff --git a/cashier/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/cashier/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index ac94b34..0648fb4 100644 --- a/cashier/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/cashier/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,4 +2,5 @@ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <background android:drawable="@color/ic_launcher_background" /> <foreground android:drawable="@drawable/ic_launcher_foreground" /> + <monochrome android:drawable="@drawable/ic_launcher_foreground" /> </adaptive-icon>
\ No newline at end of file diff --git a/cashier/src/main/res/values-de/strings.xml b/cashier/src/main/res/values-de/strings.xml index d53e3d4..626d909 100644 --- a/cashier/src/main/res/values-de/strings.xml +++ b/cashier/src/main/res/values-de/strings.xml @@ -2,21 +2,21 @@ <resources> <string name="config_button_save">Speichern</string> <string name="config_bank_url_error">Die Adresse ist ungültig.</string> - <string name="config_username_error">Bitte geben Sie Ihren Benutzernamen ein</string> - <string name="config_error">Fehler beim Abrufen der Konfiguration: %s</string> - <string name="config_error_auth">Benutzername oder Passwort ungültig</string> + <string name="config_username_error">Bitte geben Sie Ihren Benutzernamen ein!</string> + <string name="config_error">Fehler beim Abrufen der Konfiguration</string> + <string name="config_error_auth">Benutzername oder Passwort ungültig!</string> <string name="balance_current_label">Aktueller Saldo</string> <string name="balance_error">FEHLER: %s</string> <string name="balance_offline">Offline. Bitte Verbindung zum Internet herstellen.</string> <string name="action_reconfigure">Neukonfiguration</string> <string name="action_lock">Sperren</string> <string name="withdraw_input_amount">Betrag</string> - <string name="withdraw_into">Wie viel E-Cash sollte abgehoben werden\?</string> + <string name="withdraw_into">Welcher Betrag soll abgehoben werden\?</string> <string name="withdraw_error_zero">Geben Sie einen positiven Betrag ein!</string> <string name="withdraw_error_insufficient_balance">Unzureichendes Guthaben</string> - <string name="withdraw_error_fetch">Fehler bei der Kommunikation mit der Bank: %s</string> + <string name="withdraw_error_fetch">Fehler bei der Verbindung mit der Bank: %s</string> <string name="withdraw_button_confirm">Abheben</string> - <string name="transaction_intro_nfc">Scannen Sie mit der Taler Wallet App\nden Code oder benutzen Sie NFC.\nDann erhalten Sie</string> + <string name="transaction_intro_nfc">Scannen Sie zum Abheben den Code oder kontaktlos mit NFC:</string> <string name="config_password">Passwort</string> <string name="transaction_confirm">Bestätigen</string> <string name="transaction_abort">Abbrechen</string> @@ -24,19 +24,20 @@ <string name="transaction_button_back">Zurück</string> <string name="transaction_last_success">Letzte Transaktion: %s abgehoben</string> <string name="transaction_last_error">Letzte Transaktion: Fehler</string> - <string name="app_name">Taler Kassierer</string> + <string name="app_name">Taler Cashier</string> <string name="config_bank_url">Bank API Adresse</string> <string name="config_demo_hint">Zum Testen können Sie <![CDATA[<a href="%s">ein Testkonto bei der Demobank erstellen</a>]]>.</string> - <string name="transaction_intro">Scannen Sie den Code\nmit der Taler Wallet.\nDann erhalten Sie</string> + <string name="transaction_intro">Scannen Sie zum Abheben den Code mit dem Taler-Wallet:</string> <string name="transaction_intro_scanned">Bitte bestätigen Sie die Transaktion!</string> <string name="transaction_last_aborted">Letzte Transaktion: Abgebrochen</string> <string name="config_username">Benutzername</string> - <string name="withdraw_error_timeout">Es wurde noch keine Abhebung ausgelöst. Bitte versuchen Sie es erneut.</string> - <string name="action_about">Über</string> - <string name="ok">OK</string> + <string name="withdraw_error_timeout">Bitte lassen Sie ein Wallet die Abhebung auslösen.</string> + <string name="action_about">Info</string> + <string name="ok">Bestätigen</string> <string name="about_title">GNU Taler Kassierer</string> <string name="about_license">Lizenz: %s</string> <string name="about_version">Version: %s</string> <string name="about_copyright">Copyright: %s</string> - <string name="about_supported_bank_api">Bank API Version: %s</string> -</resources> + <string name="about_supported_bank_api">Bankschnittstellen-Version: %s</string> + <string name="withdraw_error_currency_mismatch">Fehler: Die Bank hat eine andere Währung gemeldet</string> +</resources>
\ No newline at end of file diff --git a/cashier/src/main/res/values-es/strings.xml b/cashier/src/main/res/values-es/strings.xml index af42d1e..c9ecc6b 100644 --- a/cashier/src/main/res/values-es/strings.xml +++ b/cashier/src/main/res/values-es/strings.xml @@ -3,11 +3,11 @@ <string name="config_username">Nombre de usuario</string> <string name="config_password">Contraseña</string> <string name="app_name">Cajero Taler</string> - <string name="config_bank_url">Banco dirección API</string> + <string name="config_bank_url">Dirección API del banco</string> <string name="config_button_save">Guardar</string> <string name="config_bank_url_error">La dirección es inválida.</string> <string name="config_username_error">Por favor inserta tu nombre de usuario!</string> - <string name="config_error">Error recuperando la configuración : %s</string> + <string name="config_error">Error recuperando la configuración</string> <string name="config_error_auth">Usuario o contraseña inválido!</string> <string name="config_demo_hint">Para realizar pruebas, puedes <a href=%s>crear una cuenta de test en el banco demo</a>.</string> <string name="balance_current_label">Balance actual</string> @@ -26,7 +26,7 @@ <string name="withdraw_button_confirm">Retirar</string> <string name="transaction_intro">Escanear código con la App de cartera Taler para retirar:</string> <string name="transaction_intro_nfc">Escanear código o usar NFC con la App de cartera Taler para retirar:</string> - <string name="transaction_intro_scanned">Por favor confirma la transacción!</string> + <string name="transaction_intro_scanned">¡Por favor confirma la transacción!</string> <string name="transaction_confirm">Confirmar</string> <string name="transaction_abort">Abortar</string> <string name="transaction_aborted">Transacción abortada</string> @@ -39,4 +39,5 @@ <string name="about_license">Licencia: %s</string> <string name="about_copyright">Copyright: %s</string> <string name="about_supported_bank_api">Banco Versión API: %s</string> + <string name="withdraw_error_currency_mismatch">Error: El banco reportó una divisa diferente</string> </resources>
\ No newline at end of file diff --git a/cashier/src/main/res/values-fi/strings.xml b/cashier/src/main/res/values-fi/strings.xml new file mode 100644 index 0000000..030008f --- /dev/null +++ b/cashier/src/main/res/values-fi/strings.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="config_bank_url">Pankin API-osoite</string> + <string name="config_username">Käyttäjänimi</string> + <string name="config_password">Salasana</string> + <string name="config_bank_url_error">Osoite on virheellinen.</string> + <string name="config_username_error">Anna käyttäjätunnuksesi!</string> + <string name="app_name">Taler kassa</string> + <string name="config_button_save">Tallenna</string> + <string name="config_error">Virhe noudettaessa määritystä</string> + <string name="config_error_auth">Virheellinen käyttäjätunnus tai salasana!</string> + <string name="balance_current_label">Nykyinen saldo</string> + <string name="balance_error">ERROR: %s</string> + <string name="balance_offline">Offline-tilassa. Muodosta yhteys Internetiin.</string> + <string name="ok">OK</string> + <string name="action_reconfigure">Määritä uudelleen</string> + <string name="action_lock">Lukitse</string> + <string name="action_about">Noin</string> + <string name="withdraw_input_amount">Summa</string> + <string name="withdraw_into">Kuinka paljon sähköistä käteistä pitäisi nostaa?</string> + <string name="withdraw_error_zero">Anna positiivinen summa!</string> + <string name="withdraw_error_currency_mismatch">Virhe: Pankki ilmoitti toisen valuutan</string> + <string name="withdraw_error_fetch">Virhe kommunikoitaessa pankin kanssa: %s</string> + <string name="withdraw_error_timeout">Mikään lompakko ei yrittänyt nostaa. Yritä uudelleen.</string> + <string name="withdraw_button_confirm">Nosta</string> + <string name="transaction_intro">Skannaa koodi Taler-lompakkosovelluksella nostaaksesi:</string> + <string name="transaction_intro_nfc">Skannaa koodi tai käytä NFC:tä Taler-lompakkosovelluksen kanssa nostaaksesi:</string> + <string name="transaction_intro_scanned">Vahvista tapahtuma!</string> + <string name="transaction_confirm">Vahvista</string> + <string name="transaction_abort">Keskeytä</string> + <string name="transaction_aborted">Toiminta keskeytetty</string> + <string name="transaction_button_back">Takaisin</string> + <string name="transaction_last_success">Viimeisin tapahtuma: %s nostettu</string> + <string name="transaction_last_aborted">Viimeisin tapahtuma: Keskeytetty</string> + <string name="transaction_last_error">Viimeisin tapahtuma: epäonnistui</string> + <string name="about_title">GNU Taler Kassa</string> + <string name="about_version">Versio: %s</string> + <string name="about_license">Lisenssi: %s</string> + <string name="about_copyright">Tekijänoikeus: %s</string> + <string name="about_supported_bank_api">Pankin API-versio: %s</string> + <string name="config_demo_hint">Testausta varten voit <a href=%s>luoda testitilin demopankkiin</a>.</string> + <string name="withdraw_error_insufficient_balance">Riittämätön saldo</string> +</resources>
\ No newline at end of file diff --git a/cashier/src/main/res/values-fr/strings.xml b/cashier/src/main/res/values-fr/strings.xml index ab46377..10ba6ea 100644 --- a/cashier/src/main/res/values-fr/strings.xml +++ b/cashier/src/main/res/values-fr/strings.xml @@ -7,7 +7,7 @@ <string name="config_button_save">Enregistrer</string> <string name="config_bank_url_error">L\'adresse est invalide.</string> <string name="config_username_error">Veuillez saisir votre identifiant !</string> - <string name="config_error">Erreur lors de la récupération de la configuration : %s</string> + <string name="config_error">Erreur lors de la récupération de la configuration</string> <string name="config_error_auth">Identifiant ou mot de passe invalide !</string> <string name="config_demo_hint" tools:ignore="StringFormatInvalid">Pour les tests, vous pouvez <a href=%s>créer un compte de test dans la banque de démonstration</a> .</string> <string name="balance_current_label">Solde actuel</string> @@ -32,4 +32,12 @@ <string name="transaction_last_success">Dernière transaction : %s retiré</string> <string name="transaction_last_aborted">Dernière transaction : Annulée</string> <string name="transaction_last_error">Dernière transaction : Échec</string> + <string name="ok">OK</string> + <string name="action_about">À propos</string> + <string name="about_version">Version : %s</string> + <string name="about_license">Licence : %s</string> + <string name="about_copyright">Droits d\'auteur : %s</string> + <string name="about_supported_bank_api">Version Bank API : %s</string> + <string name="about_title">GNU Taler Cashier</string> + <string name="withdraw_error_currency_mismatch">Erreur : La banque a déclaré une devise différente</string> </resources>
\ No newline at end of file diff --git a/cashier/src/main/res/values-ru/strings.xml b/cashier/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000..8d6fff9 --- /dev/null +++ b/cashier/src/main/res/values-ru/strings.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Taler Кассир</string> + <string name="config_bank_url">API адрес банка</string> + <string name="config_username">Имя пользователя</string> + <string name="config_password">Пароль</string> + <string name="config_button_save">Сохранить</string> + <string name="config_error_auth">Неправильное имя пользователя или пароль!</string> + <string name="config_demo_hint">Для тестирования, вы можете <a href=%s>создать тестовую учётную запист в демонстрационном банке</a>.</string> + <string name="balance_current_label">Теущий баланс</string> + <string name="balance_error">ОШИБКА: %s</string> + <string name="balance_offline">Нет связи. Пожалуйста подключитесь к интернету.</string> + <string name="action_reconfigure">Переконфигурировать</string> + <string name="action_lock">Заблокировать</string> + <string name="action_about">О программе</string> + <string name="withdraw_input_amount">Количество</string> + <string name="ok">OK</string> + <string name="config_bank_url_error">Адрес неправильный.</string> + <string name="config_username_error">Пожалуйста введите ваше имя пользователя!</string> + <string name="config_error">Ошибка при получении конфигурации</string> + <string name="withdraw_into">Сколько e-cash должно быть списано?</string> + <string name="withdraw_error_zero">Введите позитивное количество!</string> + <string name="withdraw_error_insufficient_balance">Недостаточный баланс</string> + <string name="withdraw_error_currency_mismatch">Ошибка: Банк использует другую валюту</string> + <string name="withdraw_error_fetch">Ошибка при связи с банком: %s</string> + <string name="withdraw_error_timeout">Никакой кошелёк не попробоавл списать. Пожалуйста попробуйте ещё раз.</string> + <string name="withdraw_button_confirm">Списание</string> + <string name="transaction_intro">Отсканируйте код приложением Кошелька Taler чтобы списать:</string> + <string name="transaction_intro_nfc">Отсканируйте код или используйте NFC в приложении Кошелька Taler чтобы списать:</string> + <string name="transaction_intro_scanned">Пожалуйста подтвердите транзакцию!</string> + <string name="transaction_confirm">Подтвердить</string> + <string name="transaction_abort">Подробности</string> + <string name="transaction_aborted">Транзакция прервана</string> + <string name="transaction_button_back">Перейти назад</string> + <string name="transaction_last_success">Последняя Транзакция: %s списано</string> + <string name="transaction_last_aborted">Последняя Транзакция: Прервана</string> + <string name="transaction_last_error">Последняя Транзакция: Неуспешна</string> + <string name="about_title">Кассир GNU Taler</string> + <string name="about_version">Версия: %s</string> + <string name="about_license">Лицензия: %s</string> + <string name="about_copyright">Авторские права: %s</string> + <string name="about_supported_bank_api">Версия API банка: %s</string> +</resources>
\ No newline at end of file diff --git a/cashier/src/main/res/values-sv/strings.xml b/cashier/src/main/res/values-sv/strings.xml index 1794aa2..967cedb 100644 --- a/cashier/src/main/res/values-sv/strings.xml +++ b/cashier/src/main/res/values-sv/strings.xml @@ -5,7 +5,7 @@ <string name="config_password">Lösenord</string> <string name="config_button_save">Spara</string> <string name="config_username_error">Vänligen ange ditt användarnamn!</string> - <string name="config_error">Fel vid hämtning av konfiguration: %s</string> + <string name="config_error">Fel vid hämtning av konfiguration</string> <string name="config_error_auth">Ogiltigt användarnamn eller lösenord!</string> <string name="balance_current_label">Nuvarande saldo</string> <string name="balance_error">FEL: %s</string> @@ -39,4 +39,5 @@ <string name="config_bank_url_error">Adressen är ogiltig.</string> <string name="config_demo_hint">För testning kan du <a href=%s>skapa ett testkonto i demobanken</a> .</string> <string name="withdraw_error_fetch">Fel vid kommunikation med bank: %s</string> + <string name="withdraw_error_currency_mismatch">Fel: Banken har rapporterat en annan valuta</string> </resources>
\ No newline at end of file diff --git a/cashier/src/main/res/values-tr/strings.xml b/cashier/src/main/res/values-tr/strings.xml new file mode 100644 index 0000000..09db26f --- /dev/null +++ b/cashier/src/main/res/values-tr/strings.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="config_bank_url">Banka API adresi</string> + <string name="config_username">Kullanıcı adı</string> + <string name="config_password">Şifre</string> + <string name="config_button_save">Kaydet</string> + <string name="config_bank_url_error">Bu adres geçersiz.</string> + <string name="config_username_error">Lütfen kullanıcı adınızı giriniz!</string> + <string name="app_name">Taler Kasiyer</string> + <string name="config_error">Konfigürasyon alınırken hata oluştu</string> + <string name="config_error_auth">Geçersiz kullanıcı adı yada şifre!</string> + <string name="config_demo_hint">Test için <a href=%s>demo bankasında bir test hesabı oluşturabilirsiniz</a>.</string> + <string name="balance_current_label">Mevcut bakiye</string> + <string name="balance_error">HATA:%s</string> + <string name="balance_offline">Çevrimdışı. Lütfen internete bağlanın.</string> + <string name="ok">Tamam</string> + <string name="action_reconfigure">Yeniden yapılandırın</string> + <string name="action_lock">Kilitle</string> + <string name="action_about">Hakkında</string> + <string name="withdraw_into">Ne kadar e-nakit çekilmelidir\?</string> + <string name="withdraw_error_zero">Pozitif tutarı girin!</string> + <string name="withdraw_input_amount">Tutar</string> + <string name="withdraw_error_insufficient_balance">Yetersiz bakiye</string> + <string name="withdraw_error_fetch">Banka ile iletişimde hata: %s</string> + <string name="withdraw_error_timeout">Hiçbir cüzdan para çekmeye çalışmadı. Lütfen tekrar deneyin.</string> + <string name="withdraw_button_confirm">Para çek</string> + <string name="transaction_intro">Para çekmek için Taler cüzdan uygulamasıyla kodu tarayın:</string> + <string name="transaction_intro_nfc">Para çekmek için Taler cüzdan uygulamasıyla kodu tarayın veya NFC\'yi kullanın:</string> + <string name="transaction_intro_scanned">Lütfen işlemi onaylayın!</string> + <string name="transaction_confirm">Onayla</string> + <string name="transaction_abort">Durdur</string> + <string name="transaction_aborted">İşlem durduruldu</string> + <string name="transaction_button_back">Geri git</string> + <string name="transaction_last_success">Son İşlem: %s çekildi</string> + <string name="transaction_last_aborted">Son İşlem: Durduruldu</string> + <string name="transaction_last_error">Son İşlem: Başarısız</string> + <string name="about_title">GNU Taler Kasiyer</string> + <string name="about_license">Lisans: %s</string> + <string name="about_copyright">Telif Hakkı: %s</string> + <string name="about_supported_bank_api">Banka API Sürümü: %s</string> + <string name="about_version">Sürüm: %s</string> + <string name="withdraw_error_currency_mismatch">Hata: Banka farklı bir para birimi bildirdi</string> +</resources>
\ No newline at end of file diff --git a/cashier/src/main/res/values/strings.xml b/cashier/src/main/res/values/strings.xml index 4c000d8..8e28d28 100644 --- a/cashier/src/main/res/values/strings.xml +++ b/cashier/src/main/res/values/strings.xml @@ -7,7 +7,7 @@ <string name="config_button_save">Save</string> <string name="config_bank_url_error">The address is invalid.</string> <string name="config_username_error">Please enter your username!</string> - <string name="config_error">Error retrieving configuration: %s</string> + <string name="config_error">Error retrieving configuration</string> <string name="config_error_auth">Invalid username or password!</string> <string name="config_error_offline" translatable="false">@string/balance_offline</string> <string name="config_demo_hint">For testing, you can <![CDATA[<a href="%s">create a test account at the demo bank</a>]]>.</string> @@ -26,6 +26,7 @@ <string name="withdraw_into">How much e-cash should be withdrawn?</string> <string name="withdraw_error_zero">Enter positive amount!</string> <string name="withdraw_error_insufficient_balance">Insufficient balance</string> + <string name="withdraw_error_currency_mismatch">Error: Bank reported a different currency</string> <string name="withdraw_error_fetch">Error communicating with bank: %s</string> <string name="withdraw_error_timeout">No wallet tried to withdraw. Please try again.</string> <string name="withdraw_error_offline" translatable="false">@string/balance_offline</string> diff --git a/cashier/src/main/res/xml/backup_descriptor.xml b/cashier/src/main/res/xml/backup_descriptor.xml index a298494..c5d3bc7 100644 --- a/cashier/src/main/res/xml/backup_descriptor.xml +++ b/cashier/src/main/res/xml/backup_descriptor.xml @@ -15,5 +15,6 @@ --> <full-backup-content> - + <!-- will not be able to decrypt this, causing crash --> + <exclude domain="sharedpref" path="secret_settings.xml"/> </full-backup-content> diff --git a/cashier/src/main/res/xml/data_extraction_rules.xml b/cashier/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..de53be5 --- /dev/null +++ b/cashier/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<data-extraction-rules> + <cloud-backup> + <exclude domain="sharedpref" path="secret_settings.xml"/> + </cloud-backup> +</data-extraction-rules> diff --git a/cashier/src/main/res/xml/network_security_config.xml b/cashier/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..81cd5ce --- /dev/null +++ b/cashier/src/main/res/xml/network_security_config.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?> +<network-security-config /> |