From f0670e2f3936f0223c02e9ec0d0de52f31a3539f Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 11 Aug 2020 09:41:40 -0300 Subject: [pos] Improve coroutine-based merchant library access --- .../net/taler/merchantpos/config/ConfigManager.kt | 6 +- .../taler/merchantpos/history/HistoryManager.kt | 19 +++--- .../taler/merchantpos/payment/PaymentManager.kt | 72 +++++++++++----------- .../net/taler/merchantpos/refund/RefundManager.kt | 24 ++++---- 4 files changed, 59 insertions(+), 62 deletions(-) (limited to 'merchant-terminal/src/main/java') 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 index c0b01a2..67e3685 100644 --- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt +++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt @@ -30,6 +30,7 @@ import io.ktor.client.features.ClientRequestException import io.ktor.client.request.get import io.ktor.client.request.header import io.ktor.http.HttpHeaders.Authorization +import io.ktor.http.HttpStatusCode.Companion.Unauthorized import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -114,7 +115,7 @@ class ConfigManager( Log.e(TAG, "Error retrieving merchant config", e) val msg = if (e is ClientRequestException) { context.getString( - if (e.response.status.value == 401) R.string.config_auth_error + if (e.response.status == Unauthorized) R.string.config_auth_error else R.string.config_error_network ) } else { @@ -145,7 +146,7 @@ class ConfigManager( Log.e(TAG, "Error handling configuration by ${receiver::class.java.simpleName}", e) context.getString(R.string.config_error_unknown) } - if (result != null) { // error + if (result != null) { // error mConfigUpdateResult.postValue(ConfigUpdateResult.Error(result)) return } @@ -178,7 +179,6 @@ class ConfigManager( private fun onNetworkError(msg: String) = scope.launch(Dispatchers.Main) { mConfigUpdateResult.value = ConfigUpdateResult.Error(msg) } - } sealed class ConfigUpdateResult { diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt index aabe4cc..d880eaa 100644 --- a/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt +++ b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt @@ -20,8 +20,8 @@ import androidx.annotation.UiThread import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import net.taler.common.assertUiThread import net.taler.merchantlib.MerchantApi import net.taler.merchantlib.OrderHistoryEntry import net.taler.merchantpos.config.ConfigManager @@ -44,20 +44,19 @@ class HistoryManager( val items: LiveData = mItems @UiThread - internal fun fetchHistory() { + internal fun fetchHistory() = scope.launch { mIsLoading.value = true val merchantConfig = configManager.merchantConfig!! - scope.launch(Dispatchers.IO) { - api.getOrderHistory(merchantConfig).handle(::onHistoryError) { - mIsLoading.postValue(false) - mItems.postValue(HistoryResult.Success(it.orders)) - } + api.getOrderHistory(merchantConfig).handle(::onHistoryError) { + assertUiThread() + mIsLoading.value = false + mItems.value = HistoryResult.Success(it.orders) } } private fun onHistoryError(msg: String) { - mIsLoading.postValue(false) - mItems.postValue(HistoryResult.Error(msg)) + assertUiThread() + mIsLoading.value = false + mItems.value = HistoryResult.Error(msg) } - } diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt index 6bab0e6..b39355a 100644 --- a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt +++ b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt @@ -23,13 +23,14 @@ import androidx.annotation.UiThread import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import net.taler.common.Duration +import net.taler.common.assertUiThread import net.taler.merchantlib.CheckPaymentResponse import net.taler.merchantlib.MerchantApi import net.taler.merchantlib.PostOrderRequest -import net.taler.merchantlib.PostOrderResponse import net.taler.merchantpos.MainActivity.Companion.TAG import net.taler.merchantpos.R import net.taler.merchantpos.config.ConfigManager @@ -50,12 +51,16 @@ class PaymentManager( private val mPayment = MutableLiveData() val payment: LiveData = mPayment + private var checkJob: Job? = null - private val checkTimer = object : CountDownTimer(TIMEOUT, CHECK_INTERVAL) { + private val checkTimer: CountDownTimer = object : CountDownTimer(TIMEOUT, CHECK_INTERVAL) { override fun onTick(millisUntilFinished: Long) { val orderId = payment.value?.orderId if (orderId == null) cancel() - else checkPayment(orderId) + // only start new job if old one doesn't exist or is complete + else if (checkJob == null || checkJob?.isCompleted == true) { + checkJob = checkPayment(orderId) + } } override fun onFinish() { @@ -64,44 +69,39 @@ class PaymentManager( } @UiThread - fun createPayment(order: Order) { + fun createPayment(order: Order) = scope.launch { val merchantConfig = configManager.merchantConfig!! mPayment.value = Payment(order, order.summary, configManager.currency!!) - scope.launch(Dispatchers.IO) { - val request = PostOrderRequest( - contractTerms = order.toContractTerms(), - refundDelay = Duration(HOURS.toMillis(1)) - ) - val response = api.postOrder(merchantConfig, request) - response.handle(::onNetworkError, ::onOrderCreated) + val request = PostOrderRequest( + contractTerms = order.toContractTerms(), + refundDelay = Duration(HOURS.toMillis(1)) + ) + api.postOrder(merchantConfig, request).handle(::onNetworkError) { orderResponse -> + assertUiThread() + mPayment.value = mPayment.value!!.copy(orderId = orderResponse.orderId) + checkTimer.start() } } - private fun onOrderCreated(orderResponse: PostOrderResponse) = scope.launch(Dispatchers.Main) { - mPayment.value = mPayment.value!!.copy(orderId = orderResponse.orderId) - checkTimer.start() - } - - private fun checkPayment(orderId: String) { + private fun checkPayment(orderId: String) = scope.launch { val merchantConfig = configManager.merchantConfig!! - scope.launch(Dispatchers.IO) { - val response = api.checkOrder(merchantConfig, orderId) - response.handle(::onNetworkError, ::onPaymentChecked) - } - } - - private fun onPaymentChecked(response: CheckPaymentResponse) = scope.launch(Dispatchers.Main) { - val currentValue = requireNotNull(mPayment.value) - if (response.paid) { - mPayment.value = currentValue.copy(paid = true) - checkTimer.cancel() - } else if (currentValue.talerPayUri == null) { - response as CheckPaymentResponse.Unpaid - mPayment.value = currentValue.copy(talerPayUri = response.talerPayUri) + api.checkOrder(merchantConfig, orderId).handle(::onNetworkError) { response -> + assertUiThread() + if (!isActive) return@handle // don't continue if job was cancelled + val currentValue = requireNotNull(mPayment.value) + if (response.paid) { + mPayment.value = currentValue.copy(paid = true) + checkTimer.cancel() + } else if (currentValue.talerPayUri == null) { + response as CheckPaymentResponse.Unpaid + mPayment.value = currentValue.copy(talerPayUri = response.talerPayUri) + } } } - private fun onNetworkError(error: String) = scope.launch(Dispatchers.Main) { + private fun onNetworkError(error: String) { + assertUiThread() + Log.d(TAG, "Network error: $error") cancelPayment(error) } @@ -112,14 +112,14 @@ class PaymentManager( mPayment.value?.let { payment -> if (!payment.paid && payment.error != null) payment.orderId?.let { orderId -> Log.d(TAG, "Deleting cancelled and unpaid order $orderId") - scope.launch(Dispatchers.IO) { + scope.launch { api.deleteOrder(merchantConfig, orderId) } } } - mPayment.value = mPayment.value!!.copy(error = error) checkTimer.cancel() + checkJob?.isCancelled + checkJob = null } - } diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt index ea2d398..25c7c5e 100644 --- a/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt +++ b/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt @@ -20,9 +20,9 @@ import androidx.annotation.UiThread import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import net.taler.common.Amount +import net.taler.common.assertUiThread import net.taler.merchantlib.MerchantApi import net.taler.merchantlib.OrderHistoryEntry import net.taler.merchantlib.RefundRequest @@ -65,27 +65,25 @@ class RefundManager( } @UiThread - internal fun refund(item: OrderHistoryEntry, amount: Amount, reason: String) { + internal fun refund(item: OrderHistoryEntry, amount: Amount, reason: String) = scope.launch { val merchantConfig = configManager.merchantConfig!! val request = RefundRequest(amount, reason) - scope.launch(Dispatchers.IO) { - api.giveRefund(merchantConfig, item.orderId, request).handle(::onRefundError) { - val result = RefundResult.Success( - refundUri = it.talerRefundUri, - item = item, - amount = amount, - reason = reason - ) - mRefundResult.postValue(result) - } + api.giveRefund(merchantConfig, item.orderId, request).handle(::onRefundError) { + assertUiThread() + mRefundResult.value = RefundResult.Success( + refundUri = it.talerRefundUri, + item = item, + amount = amount, + reason = reason + ) } } @UiThread private fun onRefundError(msg: String) { + assertUiThread() if (msg.contains("2602")) { mRefundResult.postValue(RefundResult.AlreadyRefunded) } else mRefundResult.postValue(RefundResult.Error(msg)) } - } -- cgit v1.2.3