summaryrefslogtreecommitdiffhomepage
path: root/android/app/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/app/src/main
parent6462dd1d00ea924d99deb27b1741e76e88045ec2 (diff)
downloadmullvadvpn-1fe033aabeb67925955906dabb044c2622d2ccd6.tar.xz
mullvadvpn-1fe033aabeb67925955906dabb044c2622d2ccd6.zip
Update in app expiry notifications over time
Diffstat (limited to 'android/app/src/main')
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationBanner.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/extensions/ResourcesExtensions.kt50
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/InAppNotificationController.kt10
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCase.kt30
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryNotificationUseCase.kt29
6 files changed, 61 insertions, 66 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationBanner.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationBanner.kt
index ebed6ea462..d20d9cfcd9 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationBanner.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationBanner.kt
@@ -36,7 +36,7 @@ import net.mullvad.mullvadvpn.lib.theme.color.warning
import net.mullvad.mullvadvpn.repository.InAppNotification
import net.mullvad.mullvadvpn.ui.VersionInfo
import net.mullvad.mullvadvpn.ui.notification.StatusLevel
-import org.joda.time.DateTime
+import org.joda.time.Period
@Preview
@Composable
@@ -48,7 +48,7 @@ private fun PreviewNotificationBanner() {
InAppNotification.UnsupportedVersion(
versionInfo = VersionInfo(currentVersion = "1.0", isSupported = false)
),
- InAppNotification.AccountExpiry(expiry = DateTime.now()),
+ InAppNotification.AccountExpiry(expiry = Period.ZERO),
InAppNotification.TunnelStateBlocked,
InAppNotification.NewDevice("Courageous Turtle"),
InAppNotification.TunnelStateError(
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/extensions/ResourcesExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/extensions/ResourcesExtensions.kt
index 367c1b54af..a130a16f4f 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/extensions/ResourcesExtensions.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/extensions/ResourcesExtensions.kt
@@ -2,39 +2,33 @@ package net.mullvad.mullvadvpn.compose.extensions
import android.content.res.Resources
import net.mullvad.mullvadvpn.R
-import org.joda.time.DateTime
-import org.joda.time.Duration
-import org.joda.time.PeriodType
+import org.joda.time.Period
-fun Resources.getExpiryQuantityString(accountExpiry: DateTime): String {
- val remainingTime = Duration(DateTime.now(), accountExpiry)
-
- return getExpiryQuantityString(this, accountExpiry, remainingTime)
-}
-
-private fun getExpiryQuantityString(
- resources: Resources,
- accountExpiry: DateTime,
- remainingTime: Duration,
-): String {
- if (remainingTime.isShorterThan(Duration.ZERO)) {
- return resources.getString(R.string.out_of_time)
+fun Resources.getExpiryQuantityString(accountExpiry: Period): String {
+ return if (accountExpiry.isNegative() || accountExpiry == Period.ZERO) {
+ getString(R.string.out_of_time)
+ } else if (accountExpiry.years > 0) {
+ getRemainingText(this, R.plurals.years_left, accountExpiry.years)
+ } else if (accountExpiry.months >= 3) {
+ getRemainingText(this, R.plurals.months_left, accountExpiry.months)
+ } else if (accountExpiry.months > 0 || accountExpiry.days >= 1) {
+ getRemainingText(this, R.plurals.days_left, accountExpiry.days)
} else {
- val remainingTimeInfo =
- remainingTime.toPeriodTo(accountExpiry, PeriodType.yearMonthDayTime())
-
- return if (remainingTimeInfo.years > 0) {
- getRemainingText(resources, R.plurals.years_left, remainingTimeInfo.years)
- } else if (remainingTimeInfo.months >= 3) {
- getRemainingText(resources, R.plurals.months_left, remainingTimeInfo.months)
- } else if (remainingTimeInfo.months > 0 || remainingTimeInfo.days >= 1) {
- getRemainingText(resources, R.plurals.days_left, remainingTime.standardDays.toInt())
- } else {
- resources.getString(R.string.less_than_a_day_left)
- }
+ getString(R.string.less_than_a_day_left)
}
}
private fun getRemainingText(resources: Resources, pluralId: Int, quantity: Int): String {
return resources.getQuantityString(pluralId, quantity, quantity)
}
+
+fun Period.isNegative() =
+ normalizedStandard().let {
+ it.years < 0 ||
+ it.months < 0 ||
+ it.weeks < 0 ||
+ it.days < 0 ||
+ it.minutes < 0 ||
+ it.seconds < 0 ||
+ it.millis < 0
+ }
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
index 48dada2d61..89c4e1e68d 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
@@ -30,7 +30,7 @@ import net.mullvad.mullvadvpn.repository.SplitTunnelingRepository
import net.mullvad.mullvadvpn.ui.MainActivity
import net.mullvad.mullvadvpn.ui.serviceconnection.AppVersionInfoRepository
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
-import net.mullvad.mullvadvpn.usecase.AccountExpiryNotificationUseCase
+import net.mullvad.mullvadvpn.usecase.AccountExpiryInAppNotificationUseCase
import net.mullvad.mullvadvpn.usecase.AvailableProvidersUseCase
import net.mullvad.mullvadvpn.usecase.EmptyPaymentUseCase
import net.mullvad.mullvadvpn.usecase.FilteredRelayListUseCase
@@ -141,7 +141,7 @@ val uiModule = module {
)
}
- single { AccountExpiryNotificationUseCase(get()) }
+ single { AccountExpiryInAppNotificationUseCase(get()) }
single { TunnelStateNotificationUseCase(get()) }
single { VersionNotificationUseCase(get(), BuildConfig.ENABLE_IN_APP_VERSION_NOTIFICATIONS) }
single { NewDeviceNotificationUseCase(get(), get()) }
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/InAppNotificationController.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/InAppNotificationController.kt
index 821fe769c2..45cfdd2bb1 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/InAppNotificationController.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/InAppNotificationController.kt
@@ -7,11 +7,11 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import net.mullvad.mullvadvpn.lib.model.ErrorState
import net.mullvad.mullvadvpn.ui.VersionInfo
-import net.mullvad.mullvadvpn.usecase.AccountExpiryNotificationUseCase
+import net.mullvad.mullvadvpn.usecase.AccountExpiryInAppNotificationUseCase
import net.mullvad.mullvadvpn.usecase.NewDeviceNotificationUseCase
import net.mullvad.mullvadvpn.usecase.TunnelStateNotificationUseCase
import net.mullvad.mullvadvpn.usecase.VersionNotificationUseCase
-import org.joda.time.DateTime
+import org.joda.time.Period
enum class StatusLevel {
Error,
@@ -38,7 +38,7 @@ sealed class InAppNotification {
override val priority: Long = 999
}
- data class AccountExpiry(val expiry: DateTime) : InAppNotification() {
+ data class AccountExpiry(val expiry: Period) : InAppNotification() {
override val statusLevel = StatusLevel.Warning
override val priority: Long = 1001
}
@@ -50,7 +50,7 @@ sealed class InAppNotification {
}
class InAppNotificationController(
- accountExpiryNotificationUseCase: AccountExpiryNotificationUseCase,
+ accountExpiryInAppNotificationUseCase: AccountExpiryInAppNotificationUseCase,
newDeviceNotificationUseCase: NewDeviceNotificationUseCase,
versionNotificationUseCase: VersionNotificationUseCase,
tunnelStateNotificationUseCase: TunnelStateNotificationUseCase,
@@ -61,7 +61,7 @@ class InAppNotificationController(
combine(
tunnelStateNotificationUseCase(),
versionNotificationUseCase(),
- accountExpiryNotificationUseCase(),
+ accountExpiryInAppNotificationUseCase(),
newDeviceNotificationUseCase(),
) { a, b, c, d ->
a + b + c + d
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCase.kt
new file mode 100644
index 0000000000..a216847355
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCase.kt
@@ -0,0 +1,30 @@
+package net.mullvad.mullvadvpn.usecase
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import net.mullvad.mullvadvpn.lib.shared.AccountRepository
+import net.mullvad.mullvadvpn.repository.InAppNotification
+import net.mullvad.mullvadvpn.service.notifications.accountexpiry.ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD
+import net.mullvad.mullvadvpn.service.notifications.accountexpiry.ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL
+import net.mullvad.mullvadvpn.service.notifications.accountexpiry.expiryTickerFlow
+
+class AccountExpiryInAppNotificationUseCase(private val accountRepository: AccountRepository) {
+
+ operator fun invoke(): Flow<List<InAppNotification>> =
+ accountRepository.accountData
+ .flatMapLatest { accountData ->
+ if (accountData != null) {
+ expiryTickerFlow(
+ expiry = accountData.expiryDate,
+ tickStart = ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD,
+ updateInterval = { ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL },
+ )
+ .map { expiresInPeriod -> InAppNotification.AccountExpiry(expiresInPeriod) }
+ } else {
+ flowOf<InAppNotification?>(null)
+ }
+ }
+ .map(::listOfNotNull)
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryNotificationUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryNotificationUseCase.kt
deleted file mode 100644
index d3490692f0..0000000000
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryNotificationUseCase.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package net.mullvad.mullvadvpn.usecase
-
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
-import net.mullvad.mullvadvpn.lib.model.AccountData
-import net.mullvad.mullvadvpn.lib.shared.AccountRepository
-import net.mullvad.mullvadvpn.repository.InAppNotification
-import net.mullvad.mullvadvpn.service.notifications.accountexpiry.ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD_DAYS
-import org.joda.time.DateTime
-
-class AccountExpiryNotificationUseCase(private val accountRepository: AccountRepository) {
- operator fun invoke(): Flow<List<InAppNotification>> =
- accountRepository.accountData
- .map(::accountExpiryNotification)
- .map(::listOfNotNull)
- .distinctUntilChanged()
-
- private fun accountExpiryNotification(accountData: AccountData?) =
- if (accountData != null && accountData.expiryDate.isCloseToExpiring()) {
- InAppNotification.AccountExpiry(accountData.expiryDate)
- } else null
-
- private fun DateTime.isCloseToExpiring(): Boolean {
- val threeDaysFromNow =
- DateTime.now().plusDays(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD_DAYS)
- return isBefore(threeDaysFromNow)
- }
-}