summaryrefslogtreecommitdiffhomepage
path: root/android/lib
diff options
context:
space:
mode:
authorKalle Lindström <karl.lindstrom@mullvad.net>2025-06-09 17:16:45 +0200
committerKalle Lindström <karl.lindstrom@mullvad.net>2025-06-19 09:36:26 +0200
commitcdc2c4d700cd41f37cf4b1607a0396019b1fbee5 (patch)
tree10fbfcebf826159d515b5599634ce5b7be256f54 /android/lib
parent1748d8a7b3e8044f6b2718d25903bf1b38afad4e (diff)
downloadmullvadvpn-cdc2c4d700cd41f37cf4b1607a0396019b1fbee5.tar.xz
mullvadvpn-cdc2c4d700cd41f37cf4b1607a0396019b1fbee5.zip
Use AlarmManager for notifications
Instead of scheduling system notifications from a flow we now schedule them independently from the app lifecycle via AlarmManager. This is done so that for example an expiry notification that the user dismissed won't get redisplayed if the app process gets killed and then restarted. When the account exiry time is fetched we schedule an alarm that will show a notification 3 days before the account time expires. This alarm then also schedules a new alarm to show the following notification and so on. To make this work this PR also introduces two new broadcast receivers; one on boot received listener and one on time time/timezone changed listener. Beause Android clears alarms when the devices is rebooted/the time is changed we need these listeners to re-trigger the alarm. To enable the broadcast receivers to re-trigger the alarm we also have to persist the expiry time in the DataStore preferences.
Diffstat (limited to 'android/lib')
-rw-r--r--android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt4
-rw-r--r--android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt5
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/AccountData.kt8
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Notification.kt1
-rw-r--r--android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/AccountRepository.kt7
5 files changed, 18 insertions, 7 deletions
diff --git a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt
index bac6e22277..f306c9f833 100644
--- a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt
+++ b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt
@@ -389,7 +389,9 @@ class ManagementService(
suspend fun getAccountData(
accountNumber: AccountNumber
): Either<GetAccountDataError, AccountData> =
- Either.catch { grpc.getAccountData(StringValue.of(accountNumber.value)).toDomain() }
+ Either.catch {
+ grpc.getAccountData(StringValue.of(accountNumber.value)).toDomain(accountNumber)
+ }
.onLeft { Logger.e("Get account data error") }
.mapLeft(GetAccountDataError::Unknown)
diff --git a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt
index feb181f01d..f3037a8c1a 100644
--- a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt
+++ b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt
@@ -613,9 +613,10 @@ internal fun ManagementInterface.DeviceState.toDomain(): DeviceState =
else -> throw NullPointerException("Device state is null")
}
-internal fun ManagementInterface.AccountData.toDomain(): AccountData =
+internal fun ManagementInterface.AccountData.toDomain(accountNumber: AccountNumber): AccountData =
AccountData(
- AccountId(UUID.fromString(id)),
+ id = AccountId(UUID.fromString(id)),
+ accountNumber = accountNumber,
expiryDate = Instant.ofEpochSecond(expiry.seconds).atDefaultZone(),
)
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/AccountData.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/AccountData.kt
index b1746e1bcc..3c869ad3e0 100644
--- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/AccountData.kt
+++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/AccountData.kt
@@ -2,4 +2,10 @@ package net.mullvad.mullvadvpn.lib.model
import java.time.ZonedDateTime
-data class AccountData(val id: AccountId, val expiryDate: ZonedDateTime)
+data class AccountData(
+ val id: AccountId,
+ val accountNumber: AccountNumber,
+ val expiryDate: ZonedDateTime,
+) {
+ companion object
+}
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Notification.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Notification.kt
index 6b073988a3..65805bb74e 100644
--- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Notification.kt
+++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Notification.kt
@@ -17,7 +17,6 @@ sealed interface Notification {
data class AccountExpiry(
override val channelId: NotificationChannelId,
override val actions: List<NotificationAction.AccountExpiry>,
- val websiteAuthToken: WebsiteAuthToken?,
val durationUntilExpiry: Duration,
) : Notification {
override val ongoing: Boolean = false
diff --git a/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/AccountRepository.kt b/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/AccountRepository.kt
index a0edc2faa6..2e922a0895 100644
--- a/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/AccountRepository.kt
+++ b/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/AccountRepository.kt
@@ -9,7 +9,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.stateIn
@@ -41,7 +40,7 @@ class AccountRepository(
val accountData: StateFlow<AccountData?> =
merge(
- managementService.deviceState.filterNotNull().map { deviceState ->
+ managementService.deviceState.map { deviceState ->
when (deviceState) {
is DeviceState.LoggedIn -> {
managementService.getAccountData(deviceState.accountNumber).getOrNull()
@@ -90,4 +89,8 @@ class AccountRepository(
internal suspend fun onVoucherRedeemed(newExpiry: ZonedDateTime) {
accountData.value?.copy(expiryDate = newExpiry)?.let { _mutableAccountDataCache.emit(it) }
}
+
+ fun resetIsNewAccount() {
+ _isNewAccount.value = false
+ }
}