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/app/src/main | |
| parent | 6462dd1d00ea924d99deb27b1741e76e88045ec2 (diff) | |
| download | mullvadvpn-1fe033aabeb67925955906dabb044c2622d2ccd6.tar.xz mullvadvpn-1fe033aabeb67925955906dabb044c2622d2ccd6.zip | |
Update in app expiry notifications over time
Diffstat (limited to 'android/app/src/main')
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) - } -} |
