diff options
| author | Kalle Lindström <karl.lindstrom@mullvad.net> | 2024-09-30 18:04:57 +0200 |
|---|---|---|
| committer | David Göransson <david.goransson@mullvad.net> | 2024-10-07 08:34:52 +0200 |
| commit | 1fe033aabeb67925955906dabb044c2622d2ccd6 (patch) | |
| tree | f51c3686beb5bc87d8184d812a0dbdd321e44dd4 /android/service/src | |
| parent | 6462dd1d00ea924d99deb27b1741e76e88045ec2 (diff) | |
| download | mullvadvpn-1fe033aabeb67925955906dabb044c2622d2ccd6.tar.xz mullvadvpn-1fe033aabeb67925955906dabb044c2622d2ccd6.zip | |
Update in app expiry notifications over time
Diffstat (limited to 'android/service/src')
4 files changed, 87 insertions, 6 deletions
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryAndroidNotification.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryAndroidNotification.kt index 0e8a1a528f..d6d18fd58a 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryAndroidNotification.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryAndroidNotification.kt @@ -39,7 +39,7 @@ private fun Notification.AccountExpiry.contentIntent(context: Context): PendingI private fun Resources.contentTitle(remainingTime: Duration): String = when { - remainingTime.isShorterThan(Duration.ZERO) -> { + remainingTime.isShorterThan(Duration.ZERO) || remainingTime == Duration.ZERO -> { getString(R.string.account_credit_has_expired) } remainingTime.standardDays >= 1 -> { diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryConstant.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryConstant.kt index 7ca2b33a22..49b891d55c 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryConstant.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryConstant.kt @@ -1,4 +1,15 @@ +@file:Suppress("MagicNumber") + package net.mullvad.mullvadvpn.service.notifications.accountexpiry -const val ACCOUNT_EXPIRY_POLL_INTERVAL: Long = 15 /* s */ * 1000 /* ms */ -const val ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD_DAYS = 3 +import kotlin.time.Duration.Companion.seconds +import org.joda.time.Duration + +val ACCOUNT_EXPIRY_POLL_INTERVAL = 15.seconds +val ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL: Duration = Duration.standardDays(1) +val ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD: Duration = Duration.standardDays(3) + +val ACCOUNT_EXPIRY_ANDROID_NOTIFICATION_UPDATE_INTERVAL_LONG: Duration = Duration.standardDays(1) +val ACCOUNT_EXPIRY_ANDROID_NOTIFICATION_UPDATE_INTERVAL_SHORT: Duration = Duration.standardHours(1) +val ACCOUNT_EXPIRY_ANDROID_NOTIFICATION_UPDATE_INTERVAL_SHORT_THRESHOLD: Duration = + Duration.standardDays(1) diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryNotificationProvider.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryNotificationProvider.kt index 3250054459..a6a2f80d06 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryNotificationProvider.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryNotificationProvider.kt @@ -55,8 +55,6 @@ class AccountExpiryNotificationProvider( } private fun Duration.isCloseToExpiry(): Boolean { - return isShorterThan( - Duration.standardDays(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD_DAYS.toLong()) - ) + return isShorterThan(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD) } } diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryTickerFlow.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryTickerFlow.kt new file mode 100644 index 0000000000..67c954a3ce --- /dev/null +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryTickerFlow.kt @@ -0,0 +1,72 @@ +package net.mullvad.mullvadvpn.service.notifications.accountexpiry + +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import org.joda.time.DateTime +import org.joda.time.Duration +import org.joda.time.Period + +fun expiryTickerFlow( + expiry: DateTime, + tickStart: Duration, + updateInterval: (expiry: DateTime) -> Duration, +): Flow<Period> = flow { + var currentUpdateInterval = updateInterval(expiry).millis + + expiry.millisFromNow().let { expiryMillis -> + if (expiryMillis <= 0) { + // Has expired. + emit(Period.ZERO) + return@flow + } else if (expiryMillis <= currentUpdateInterval) { + emit(Period(DateTime.now(), expiry)) + delay(expiry.millisFromNow()) + emit(Period.ZERO) + return@flow + } else if (expiryMillis > tickStart.millis) { + // Delay until the time we should start emitting. + delay(expiryMillis - tickStart.millis + 1) + } + } + + // Always emit at start of flow. + emit(Period(DateTime.now(), expiry)) + + // Delay until the next update interval. + delay(expiry.millisFromNow() % currentUpdateInterval) + + var delayCount = calculateDelaysNeeded(expiry.millisFromNow(), currentUpdateInterval) + + while (delayCount > 0) { + emit(Period(DateTime.now(), expiry)) + delay(currentUpdateInterval) + + val newUpdateInterval = updateInterval(expiry).millis + if (newUpdateInterval != currentUpdateInterval) { + delayCount = calculateDelaysNeeded(expiry.millisFromNow(), newUpdateInterval) + currentUpdateInterval = newUpdateInterval + } else { + delayCount -= 1 + } + } + + // We may have remaining time if the update interval wasn't a multiple of the remaining time. + delay(expiry.millisFromNow()) + + // We have now expired. + emit(Period.ZERO) +} + +// Calculate how many times we need to delay and and emit until the expiry time is reached. +// Note that the returned delays may add upp to less than the remaining time, for example +// if we have 100ms remaining and currentUpdateIntervalMillis is 40ms this function will return 2. +private fun calculateDelaysNeeded(millisUntilExpiry: Long, currentUpdateIntervalMillis: Long): Int { + return if (millisUntilExpiry <= 0) { + 0 + } else { + (millisUntilExpiry / currentUpdateIntervalMillis).toInt() + } +} + +private fun DateTime.millisFromNow(): Long = Duration(DateTime.now(), this).millis |
