diff options
| author | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2024-06-17 16:06:50 +0200 |
|---|---|---|
| committer | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2024-06-19 10:50:21 +0200 |
| commit | f31afd8ee3861d50d02a099afbbf181338072276 (patch) | |
| tree | 75ca652b057235d8af85e59a47f606f4799bcdb3 /android/app/src | |
| parent | 88c5d622d797faf99528f79a0907b101fa8f4cae (diff) | |
| download | mullvadvpn-f31afd8ee3861d50d02a099afbbf181338072276.tar.xz mullvadvpn-f31afd8ee3861d50d02a099afbbf181338072276.zip | |
Replace retry with exponential backoff with arrow schedule
Also clean up the code related to play purchase verification
Diffstat (limited to 'android/app/src')
8 files changed, 43 insertions, 64 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/PaymentConstant.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/PaymentConstant.kt index 1cf1c32b2c..ba2413d4fc 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/PaymentConstant.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/PaymentConstant.kt @@ -1,5 +1,7 @@ package net.mullvad.mullvadvpn.constant +import kotlin.time.Duration.Companion.seconds + const val VERIFICATION_MAX_ATTEMPTS = 4 -const val VERIFICATION_INITIAL_BACK_OFF_MILLISECONDS = 3000L -const val VERIFICATION_BACK_OFF_FACTOR = 3L +val VERIFICATION_INITIAL_BACK_OFF_DURATION = 3.seconds +const val VERIFICATION_BACK_OFF_FACTOR = 3.toDouble() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PaymentUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PaymentUseCase.kt index fbd662f95e..23f6c797b0 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PaymentUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PaymentUseCase.kt @@ -1,20 +1,24 @@ package net.mullvad.mullvadvpn.usecase import android.app.Activity +import arrow.core.Either +import arrow.core.right +import arrow.resilience.Schedule +import arrow.resilience.retryEither import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.transform import net.mullvad.mullvadvpn.constant.VERIFICATION_BACK_OFF_FACTOR -import net.mullvad.mullvadvpn.constant.VERIFICATION_INITIAL_BACK_OFF_MILLISECONDS +import net.mullvad.mullvadvpn.constant.VERIFICATION_INITIAL_BACK_OFF_DURATION import net.mullvad.mullvadvpn.constant.VERIFICATION_MAX_ATTEMPTS import net.mullvad.mullvadvpn.lib.payment.PaymentRepository import net.mullvad.mullvadvpn.lib.payment.model.PaymentAvailability import net.mullvad.mullvadvpn.lib.payment.model.ProductId import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult +import net.mullvad.mullvadvpn.lib.payment.model.VerificationError import net.mullvad.mullvadvpn.lib.payment.model.VerificationResult -import net.mullvad.mullvadvpn.util.retryWithExponentialBackOff interface PaymentUseCase { val paymentAvailability: Flow<PaymentAvailability?> @@ -26,7 +30,7 @@ interface PaymentUseCase { suspend fun resetPurchaseResult() - suspend fun verifyPurchases(onSuccessfulVerification: () -> Unit = {}) + suspend fun verifyPurchases(): Either<VerificationError, VerificationResult> } class PlayPaymentUseCase(private val paymentRepository: PaymentRepository) : PaymentUseCase { @@ -60,24 +64,19 @@ class PlayPaymentUseCase(private val paymentRepository: PaymentRepository) : Pay } @Suppress("ensure every public functions method is named 'invoke' with operator modifier") - override suspend fun verifyPurchases(onSuccessfulVerification: () -> Unit) { - paymentRepository - .verifyPurchases() - .retryWithExponentialBackOff( - maxAttempts = VERIFICATION_MAX_ATTEMPTS, - initialBackOffDelay = VERIFICATION_INITIAL_BACK_OFF_MILLISECONDS, - backOffDelayFactor = VERIFICATION_BACK_OFF_FACTOR - ) { - it is VerificationResult.Error - } - .collect { + override suspend fun verifyPurchases() = + Schedule.exponential<VerificationError>( + VERIFICATION_INITIAL_BACK_OFF_DURATION, + VERIFICATION_BACK_OFF_FACTOR + ) + .and(Schedule.recurs(VERIFICATION_MAX_ATTEMPTS.toLong())) + .retryEither { paymentRepository.verifyPurchases() } + .onRight { if (it == VerificationResult.Success) { // Update the payment availability after a successful verification. queryPaymentAvailability() - onSuccessfulVerification() } } - } private fun PurchaseResult?.shouldDelayLoading() = this is PurchaseResult.FetchingProducts || this is PurchaseResult.VerificationStarted @@ -107,7 +106,5 @@ class EmptyPaymentUseCase : PaymentUseCase { } @Suppress("ensure every public functions method is named 'invoke' with operator modifier") - override suspend fun verifyPurchases(onSuccessfulVerification: () -> Unit) { - // No op - } + override suspend fun verifyPurchases() = VerificationResult.NothingToVerify.right() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt index fbe44a5fea..13561aa7f8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt @@ -3,11 +3,7 @@ package net.mullvad.mullvadvpn.util import kotlinx.coroutines.Deferred -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.retryWhen inline fun <T1, T2, T3, T4, T5, T6, R> combine( flow: Flow<T1>, @@ -90,34 +86,3 @@ fun <T> Deferred<T>.getOrDefault(default: T) = } catch (e: IllegalStateException) { default } - -@Suppress("UNCHECKED_CAST") -suspend inline fun <T> Flow<T>.retryWithExponentialBackOff( - maxAttempts: Int, - initialBackOffDelay: Long, - backOffDelayFactor: Long, - crossinline predicate: (T) -> Boolean, -): Flow<T> = - map { - if (predicate(it)) { - throw ExceptionWrapper(it as Any) - } - it - } - .retryWhen { _, attempt -> - if (attempt >= maxAttempts) { - return@retryWhen false - } - val backOffDelay = initialBackOffDelay * backOffDelayFactor.pow(attempt.toInt()) - delay(backOffDelay) - true - } - .catch { - if (it is ExceptionWrapper) { - this.emit(it.item as T) - } else { - throw it - } - } - -class ExceptionWrapper(val item: Any) : Throwable() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/VerificationResultExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/VerificationResultExtensions.kt new file mode 100644 index 0000000000..d4484d50e6 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/VerificationResultExtensions.kt @@ -0,0 +1,8 @@ +package net.mullvad.mullvadvpn.util + +import arrow.core.Either +import net.mullvad.mullvadvpn.lib.payment.model.VerificationError +import net.mullvad.mullvadvpn.lib.payment.model.VerificationResult + +fun Either<VerificationError, VerificationResult>.isSuccess() = + getOrNull() == VerificationResult.Success diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt index a42003d6e2..7c2635b63a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt @@ -24,6 +24,7 @@ import net.mullvad.mullvadvpn.lib.payment.model.ProductId import net.mullvad.mullvadvpn.lib.shared.AccountRepository import net.mullvad.mullvadvpn.lib.shared.DeviceRepository import net.mullvad.mullvadvpn.usecase.PaymentUseCase +import net.mullvad.mullvadvpn.util.isSuccess import net.mullvad.mullvadvpn.util.toPaymentState import org.joda.time.DateTime @@ -90,8 +91,9 @@ class AccountViewModel( private fun verifyPurchases() { viewModelScope.launch { - paymentUseCase.verifyPurchases() - updateAccountExpiry() + if (paymentUseCase.verifyPurchases().isSuccess()) { + updateAccountExpiry() + } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt index b41a175dad..037c3243ab 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt @@ -32,6 +32,7 @@ import net.mullvad.mullvadvpn.usecase.PaymentUseCase import net.mullvad.mullvadvpn.usecase.SelectedLocationTitleUseCase import net.mullvad.mullvadvpn.util.combine import net.mullvad.mullvadvpn.util.daysFromNow +import net.mullvad.mullvadvpn.util.isSuccess import net.mullvad.mullvadvpn.util.toInAddress import net.mullvad.mullvadvpn.util.toOutAddress @@ -114,8 +115,8 @@ class ConnectViewModel( init { viewModelScope.launch { - paymentUseCase.verifyPurchases { - viewModelScope.launch { accountRepository.getAccountData() } + if (paymentUseCase.verifyPurchases().isSuccess()) { + accountRepository.getAccountData() } } viewModelScope.launch { deviceRepository.updateDevice() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt index 7784181466..d279792853 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt @@ -20,6 +20,7 @@ import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy import net.mullvad.mullvadvpn.lib.shared.DeviceRepository import net.mullvad.mullvadvpn.usecase.OutOfTimeUseCase import net.mullvad.mullvadvpn.usecase.PaymentUseCase +import net.mullvad.mullvadvpn.util.isSuccess import net.mullvad.mullvadvpn.util.toPaymentState class OutOfTimeViewModel( @@ -76,8 +77,9 @@ class OutOfTimeViewModel( private fun verifyPurchases() { viewModelScope.launch { - paymentUseCase.verifyPurchases() - updateAccountExpiry() + if (paymentUseCase.verifyPurchases().isSuccess()) { + updateAccountExpiry() + } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt index 4b61468f8e..9322ef9ce4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt @@ -21,6 +21,7 @@ import net.mullvad.mullvadvpn.lib.shared.AccountRepository import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy import net.mullvad.mullvadvpn.lib.shared.DeviceRepository import net.mullvad.mullvadvpn.usecase.PaymentUseCase +import net.mullvad.mullvadvpn.util.isSuccess import net.mullvad.mullvadvpn.util.toPaymentState class WelcomeViewModel( @@ -79,8 +80,9 @@ class WelcomeViewModel( private fun verifyPurchases() { viewModelScope.launch { - paymentUseCase.verifyPurchases() - updateAccountExpiry() + if (paymentUseCase.verifyPurchases().isSuccess()) { + updateAccountExpiry() + } } } |
