summaryrefslogtreecommitdiffhomepage
path: root/android/service/src/main
diff options
context:
space:
mode:
authorKalle Lindström <karl.lindstrom@mullvad.net>2024-09-30 18:04:57 +0200
committerDavid Göransson <david.goransson@mullvad.net>2024-10-07 08:34:52 +0200
commit1fe033aabeb67925955906dabb044c2622d2ccd6 (patch)
treef51c3686beb5bc87d8184d812a0dbdd321e44dd4 /android/service/src/main
parent6462dd1d00ea924d99deb27b1741e76e88045ec2 (diff)
downloadmullvadvpn-1fe033aabeb67925955906dabb044c2622d2ccd6.tar.xz
mullvadvpn-1fe033aabeb67925955906dabb044c2622d2ccd6.zip
Update in app expiry notifications over time
Diffstat (limited to 'android/service/src/main')
-rw-r--r--android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryAndroidNotification.kt2
-rw-r--r--android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryConstant.kt15
-rw-r--r--android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryNotificationProvider.kt4
-rw-r--r--android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryTickerFlow.kt72
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