summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKalle Lindström <karl.lindstrom@mullvad.net>2025-02-25 10:48:50 +0100
committerKalle Lindström <karl.lindstrom@mullvad.net>2025-02-26 09:25:17 +0100
commitda304ffedf8f8d98600f31f737cf92768c0323fa (patch)
treee8da9b9a861f3d3efbbbc39fb5160ec1cfeb67fa
parent5025db74b34cfb3536c43f89f3407ffc0d97ae73 (diff)
downloadmullvadvpn-da304ffedf8f8d98600f31f737cf92768c0323fa.tar.xz
mullvadvpn-da304ffedf8f8d98600f31f737cf92768c0323fa.zip
Remove Joda Time and use java.time package instead
Joda Time has been superseded by the Java 8 java.time package which has more or less the same API. This commit removes all usage of Joda Time and replaces it with the java.time classes. This is done so that we can remove the dependency on Joda Time.
-rw-r--r--android/app/build.gradle.kts1
-rw-r--r--android/app/proguard-rules.pro6
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt17
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt6
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationBanner.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt5
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/extensions/ResourcesExtensions.kt8
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/AccountUiStatePreviewParameterProvider.kt9
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DevicePreviewData.kt8
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt6
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectUiState.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/provider/MullvadFileProvider.kt6
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/InAppNotificationController.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCase.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DateExtensions.kt15
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt5
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt5
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/AccountExpiryNotificationProviderTest.kt33
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt2
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCaseTest.kt29
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/NewDeviceNotificationUseCaseTest.kt4
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt14
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModelTest.kt8
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt6
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt6
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VoucherDialogViewModelTest.kt6
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt6
-rw-r--r--android/gradle/libs.versions.toml2
-rw-r--r--android/lib/common/build.gradle.kts1
-rw-r--r--android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CommonStringExtensions.kt14
-rw-r--r--android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/DateExtensions.kt20
-rw-r--r--android/lib/daemon-grpc/build.gradle.kts1
-rw-r--r--android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt11
-rw-r--r--android/lib/model/build.gradle.kts1
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/AccountData.kt4
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Device.kt4
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Notification.kt2
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RedeemVoucherSuccess.kt4
-rw-r--r--android/lib/shared/build.gradle.kts1
-rw-r--r--android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/AccountRepository.kt4
-rw-r--r--android/service/build.gradle.kts1
-rw-r--r--android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryAndroidNotification.kt12
-rw-r--r--android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryConstant.kt8
-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/AccountExpiryTicker.kt22
-rw-r--r--android/test/e2e/build.gradle.kts1
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/router/packetCapture/Packet.kt10
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/router/packetCapture/Stream.kt28
-rw-r--r--android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/serializer/NanoSecondsTimestampSerializer.kt14
-rw-r--r--android/test/mockapi/build.gradle.kts1
-rw-r--r--android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountExpiryMockApiTest.kt8
-rw-r--r--android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountHistoryMockApiTest.kt4
-rw-r--r--android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LoginMockApiTest.kt8
-rw-r--r--android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LogoutMockApiTest.kt4
-rw-r--r--android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt15
-rw-r--r--android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/TooManyDevicesMockApiTest.kt4
-rw-r--r--android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/DateTimeUtils.kt15
-rw-r--r--android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt10
63 files changed, 244 insertions, 228 deletions
diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 4a78a198a2..ec59d0a5d3 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -399,7 +399,6 @@ dependencies {
implementation(libs.compose.destinations)
ksp(libs.compose.destinations.ksp)
- implementation(libs.jodatime)
implementation(libs.kermit)
implementation(libs.koin)
implementation(libs.koin.android)
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
index b72bed1d05..94c8f48bda 100644
--- a/android/app/proguard-rules.pro
+++ b/android/app/proguard-rules.pro
@@ -13,12 +13,6 @@
-keep class java.net.InetSocketAddress { *; }
-keep class java.util.ArrayList { *; }
-# Joda Time
--dontwarn org.joda.convert.**
--dontwarn org.joda.time.**
--keep class org.joda.time.** { *; }
--keep interface org.joda.time.** { *; }
-
# grpc
-keep class io.grpc.okhttp.OkHttpChannelBuilder { *; }
-keep class mullvad_daemon.management_interface.** { *; }
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt
index 0cdc8b8fe7..f1a81d4d91 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt
@@ -10,6 +10,9 @@ import io.mockk.every
import io.mockk.mockk
import io.mockk.unmockkAll
import io.mockk.verify
+import java.time.Duration
+import java.time.Instant
+import java.time.ZonedDateTime
import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension
import net.mullvad.mullvadvpn.compose.setContentWithTheme
import net.mullvad.mullvadvpn.compose.state.ConnectUiState
@@ -30,8 +33,6 @@ import net.mullvad.mullvadvpn.lib.model.TunnelEndpoint
import net.mullvad.mullvadvpn.lib.model.TunnelState
import net.mullvad.mullvadvpn.repository.InAppNotification
import net.mullvad.mullvadvpn.ui.VersionInfo
-import org.joda.time.DateTime
-import org.joda.time.Duration
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -556,7 +557,7 @@ class ConnectScreenTest {
fun testAccountExpiredNotification() {
composeExtension.use {
// Arrange
- val expiryDate = DateTime(2020, 11, 11, 10, 10)
+ val expiryDate = ZonedDateTime.parse("2020-11-11T10:10Z")
initScreen(
state =
ConnectUiState(
@@ -567,7 +568,9 @@ class ConnectScreenTest {
deviceName = "",
daysLeftUntilExpiry = null,
inAppNotification =
- InAppNotification.AccountExpiry(Duration(DateTime.now(), expiryDate)),
+ InAppNotification.AccountExpiry(
+ Duration.between(Instant.now(), expiryDate)
+ ),
isPlayBuild = false,
)
)
@@ -612,7 +615,7 @@ class ConnectScreenTest {
composeExtension.use {
// Arrange
val mockedClickHandler: () -> Unit = mockk(relaxed = true)
- val expiryDate = DateTime(2020, 11, 11, 10, 10)
+ val expiryDate = ZonedDateTime.parse("2020-11-11T10:10Z")
initScreen(
onManageAccountClick = mockedClickHandler,
state =
@@ -624,7 +627,9 @@ class ConnectScreenTest {
deviceName = "",
daysLeftUntilExpiry = null,
inAppNotification =
- InAppNotification.AccountExpiry(Duration(DateTime.now(), expiryDate)),
+ InAppNotification.AccountExpiry(
+ Duration.between(Instant.now(), expiryDate)
+ ),
isPlayBuild = false,
),
)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt
index 3d591bafba..29fa7415bb 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt
@@ -78,7 +78,7 @@ fun ScaffoldWithTopBarAndDeviceName(
isIconAndLogoVisible: Boolean = true,
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
deviceName: String?,
- timeLeft: Int?,
+ timeLeft: Long?,
content: @Composable (PaddingValues) -> Unit,
) {
Scaffold(
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt
index 91cb49ae53..e7a58d998b 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt
@@ -332,7 +332,7 @@ fun MullvadTopBarWithDeviceName(
iconTintColor: Color,
isIconAndLogoVisible: Boolean = true,
deviceName: String?,
- daysLeftUntilExpiry: Int?,
+ daysLeftUntilExpiry: Long?,
) {
Column {
MullvadTopBar(
@@ -383,8 +383,8 @@ fun MullvadTopBarWithDeviceName(
if (daysLeftUntilExpiry >= 0) {
pluralStringResource(
id = R.plurals.days,
- daysLeftUntilExpiry,
- daysLeftUntilExpiry,
+ daysLeftUntilExpiry.toInt(),
+ daysLeftUntilExpiry.toInt(),
)
} else {
stringResource(id = R.string.out_of_time)
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 f15a054b7d..4f527a94c5 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
@@ -30,6 +30,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
+import java.time.Duration
import net.mullvad.mullvadvpn.compose.component.MullvadTopBar
import net.mullvad.mullvadvpn.compose.test.NOTIFICATION_BANNER
import net.mullvad.mullvadvpn.compose.test.NOTIFICATION_BANNER_ACTION
@@ -43,7 +44,6 @@ 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.Duration
@Preview
@Composable
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt
index a9859c318a..93b29e5185 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt
@@ -29,6 +29,7 @@ import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.spec.DestinationStyle
+import java.util.concurrent.TimeUnit
import net.mullvad.mullvadvpn.BuildConfig
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.button.PrimaryButton
@@ -46,7 +47,6 @@ import net.mullvad.mullvadvpn.lib.model.RedeemVoucherError
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.viewmodel.VoucherDialogViewModel
-import org.joda.time.DateTimeConstants
import org.koin.androidx.compose.koinViewModel
@Preview(device = Devices.TV_720p)
@@ -157,9 +157,10 @@ fun RedeemVoucherDialog(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
+ TimeUnit.DAYS.toSeconds(1)
if (state.voucherState is VoucherDialogState.Success) {
val days: Int =
- (state.voucherState.addedTime / DateTimeConstants.SECONDS_PER_DAY).toInt()
+ (state.voucherState.addedTime / TimeUnit.DAYS.toSeconds(1)).toInt()
val message =
stringResource(
R.string.added_to_your_account,
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 9611897e76..28459c9a5d 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
@@ -1,16 +1,16 @@
package net.mullvad.mullvadvpn.compose.extensions
import android.content.res.Resources
+import java.time.Duration
import net.mullvad.mullvadvpn.R
-import org.joda.time.Duration
private const val DAYS_IN_STANDARD_YEAR = 365
fun Resources.getExpiryQuantityString(accountExpiry: Duration): String {
- val days = accountExpiry.standardDays.toInt()
- val years = (accountExpiry.standardDays / DAYS_IN_STANDARD_YEAR).toInt()
+ val days = accountExpiry.toDays().toInt()
+ val years = (accountExpiry.toDays() / DAYS_IN_STANDARD_YEAR).toInt()
- return if (accountExpiry.millis <= 0) {
+ return if (accountExpiry.toMillis() <= 0) {
getString(R.string.out_of_time)
} else if (years > 1) {
getRemainingText(this, R.plurals.years_left, years)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/AccountUiStatePreviewParameterProvider.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/AccountUiStatePreviewParameterProvider.kt
index c15e8b2a53..f40d0697ab 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/AccountUiStatePreviewParameterProvider.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/AccountUiStatePreviewParameterProvider.kt
@@ -1,6 +1,8 @@
package net.mullvad.mullvadvpn.compose.preview
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import java.time.ZonedDateTime
+import java.time.format.DateTimeFormatter
import net.mullvad.mullvadvpn.compose.state.PaymentState
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.payment.model.PaymentProduct
@@ -8,7 +10,6 @@ import net.mullvad.mullvadvpn.lib.payment.model.PaymentStatus
import net.mullvad.mullvadvpn.lib.payment.model.ProductId
import net.mullvad.mullvadvpn.lib.payment.model.ProductPrice
import net.mullvad.mullvadvpn.viewmodel.AccountUiState
-import org.joda.time.DateTime
class AccountUiStatePreviewParameterProvider : PreviewParameterProvider<AccountUiState> {
override val values =
@@ -16,7 +17,11 @@ class AccountUiStatePreviewParameterProvider : PreviewParameterProvider<AccountU
AccountUiState(
deviceName = "Test Name",
accountNumber = AccountNumber("1234123412341234"),
- accountExpiry = DateTime.parse("2050-12-01"),
+ accountExpiry =
+ ZonedDateTime.parse(
+ "2050-12-01T00:00:00.000Z",
+ DateTimeFormatter.ISO_ZONED_DATE_TIME,
+ ),
showSitePayment = true,
billingPaymentState =
PaymentState.PaymentAvailable(
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DevicePreviewData.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DevicePreviewData.kt
index b87974d18b..82bf05ba64 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DevicePreviewData.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DevicePreviewData.kt
@@ -1,9 +1,10 @@
package net.mullvad.mullvadvpn.compose.preview
+import java.time.ZonedDateTime
+import java.time.format.DateTimeFormatter
import net.mullvad.mullvadvpn.compose.state.DeviceItemUiState
import net.mullvad.mullvadvpn.lib.model.Device
import net.mullvad.mullvadvpn.lib.model.DeviceId
-import org.joda.time.DateTime
internal object DevicePreviewData {
fun generateDevices(count: Int) =
@@ -16,10 +17,11 @@ internal object DevicePreviewData {
Device(
id = DeviceId.fromString(id),
name = name ?: "Device $index-${id.take(DEVICE_SUFFIX_LENGTH)}",
- creationDate = DEVICE_CREATION_DATE.plusMonths(index),
+ creationDate = DEVICE_CREATION_DATE.plusMonths(index.toLong()),
)
}
private const val DEVICE_SUFFIX_LENGTH = 4
private const val UUID = "12345678-1234-5678-1234-567812345678"
-private val DEVICE_CREATION_DATE = DateTime.parse("2024-05-27")
+private val DEVICE_CREATION_DATE =
+ ZonedDateTime.parse("2024-05-27T00:00+00:00", DateTimeFormatter.ISO_ZONED_DATE_TIME)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt
index ddfb535faf..bd3d8b8bc5 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt
@@ -38,6 +38,7 @@ import com.ramcosta.composedestinations.generated.destinations.VerificationPendi
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.result.NavResult
import com.ramcosta.composedestinations.result.ResultRecipient
+import java.time.ZonedDateTime
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.button.ExternalButton
@@ -57,13 +58,12 @@ import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle
import net.mullvad.mullvadvpn.compose.util.SecureScreenWhileInView
import net.mullvad.mullvadvpn.compose.util.createCopyToClipboardHandle
import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately
+import net.mullvad.mullvadvpn.lib.common.util.toExpiryDateString
import net.mullvad.mullvadvpn.lib.payment.model.ProductId
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
-import net.mullvad.mullvadvpn.util.toExpiryDateString
import net.mullvad.mullvadvpn.viewmodel.AccountUiState
import net.mullvad.mullvadvpn.viewmodel.AccountViewModel
-import org.joda.time.DateTime
import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)
@@ -256,7 +256,7 @@ private fun AccountNumberRow(accountNumber: String, onCopyAccountNumber: (String
}
@Composable
-private fun PaidUntilRow(accountExpiry: DateTime?) {
+private fun PaidUntilRow(accountExpiry: ZonedDateTime?) {
Column(modifier = Modifier.fillMaxWidth()) {
Text(
style = MaterialTheme.typography.labelMedium,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt
index 34ac8d98b7..0a9504f46c 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt
@@ -56,6 +56,7 @@ import net.mullvad.mullvadvpn.compose.state.DeviceListUiState
import net.mullvad.mullvadvpn.compose.transitions.DefaultTransition
import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle
import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately
+import net.mullvad.mullvadvpn.lib.common.util.formatDate
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.model.Device
import net.mullvad.mullvadvpn.lib.model.DeviceId
@@ -64,7 +65,6 @@ import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.lib.theme.color.selected
import net.mullvad.mullvadvpn.lib.theme.typeface.listItemSubText
import net.mullvad.mullvadvpn.lib.theme.typeface.listItemText
-import net.mullvad.mullvadvpn.util.formatDate
import net.mullvad.mullvadvpn.viewmodel.DeviceListSideEffect
import net.mullvad.mullvadvpn.viewmodel.DeviceListViewModel
import org.koin.androidx.compose.koinViewModel
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectUiState.kt
index 98b5219785..4e17b6918b 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectUiState.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectUiState.kt
@@ -11,7 +11,7 @@ data class ConnectUiState(
val showLocation: Boolean,
val inAppNotification: InAppNotification?,
val deviceName: String?,
- val daysLeftUntilExpiry: Int?,
+ val daysLeftUntilExpiry: Long?,
val isPlayBuild: Boolean,
) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/provider/MullvadFileProvider.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/provider/MullvadFileProvider.kt
index 729fe5f2c2..b070f7c646 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/provider/MullvadFileProvider.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/provider/MullvadFileProvider.kt
@@ -5,9 +5,9 @@ import android.content.Intent
import android.net.Uri
import androidx.core.content.FileProvider
import java.io.File
+import java.time.ZonedDateTime
+import java.time.format.DateTimeFormatter
import net.mullvad.mullvadvpn.R
-import org.joda.time.DateTime
-import org.joda.time.format.ISODateTimeFormat
// https://developer.android.com/reference/androidx/core/content/FileProvider
// From link: It is possible to use FileProvider directly instead of extending it. However, this is
@@ -51,6 +51,6 @@ fun Context.createCacheFile(directory: ProviderCacheDirectory, fileName: String)
}
fun createShareLogFileName(): String {
- val datetime = ISODateTimeFormat.basicOrdinalDateTimeNoMillis().print(DateTime.now())
+ val datetime = DateTimeFormatter.ofPattern("yyyyDDD'T'HHmmssZ").format(ZonedDateTime.now())
return "mullvad_log-${datetime}.txt"
}
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 0fcee60bed..0e3e004f0b 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
@@ -1,5 +1,6 @@
package net.mullvad.mullvadvpn.repository
+import java.time.Duration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
@@ -12,7 +13,6 @@ import net.mullvad.mullvadvpn.usecase.NewChangelogNotificationUseCase
import net.mullvad.mullvadvpn.usecase.NewDeviceNotificationUseCase
import net.mullvad.mullvadvpn.usecase.TunnelStateNotificationUseCase
import net.mullvad.mullvadvpn.usecase.VersionNotificationUseCase
-import org.joda.time.Duration
enum class StatusLevel {
Error,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCase.kt
index 8fd97dc63d..081c632255 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCase.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCase.kt
@@ -14,11 +14,11 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
+import net.mullvad.mullvadvpn.lib.common.util.millisFromNow
import net.mullvad.mullvadvpn.lib.model.ErrorStateCause
import net.mullvad.mullvadvpn.lib.model.TunnelState
import net.mullvad.mullvadvpn.lib.shared.AccountRepository
import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy
-import org.joda.time.DateTime
class OutOfTimeUseCase(
private val connectionProxy: ConnectionProxy,
@@ -60,7 +60,7 @@ class OutOfTimeUseCase(
.flatMapLatest {
if (it != null) {
flow {
- val millisUntilExpiry = it.expiryDate.millis - DateTime.now().millis
+ val millisUntilExpiry = it.expiryDate.millisFromNow()
if (millisUntilExpiry > 0) {
emit(false)
delay(millisUntilExpiry)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DateExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DateExtensions.kt
deleted file mode 100644
index e11434257a..0000000000
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DateExtensions.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package net.mullvad.mullvadvpn.util
-
-import java.text.DateFormat
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.DurationUnit
-import org.joda.time.DateTime
-import org.joda.time.format.ISODateTimeFormat
-
-fun DateTime.formatDate(): String = ISODateTimeFormat.date().print(this)
-
-fun DateTime.toExpiryDateString(): String =
- DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).format(this.toDate())
-
-fun DateTime.daysFromNow() =
- (toInstant().millis - DateTime.now().toInstant().millis).milliseconds.toInt(DurationUnit.DAYS)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt
index 8297375ee3..8dd4253553 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt
@@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.viewmodel
import android.app.Activity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import java.time.ZonedDateTime
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -27,7 +28,6 @@ import net.mullvad.mullvadvpn.lib.shared.DeviceRepository
import net.mullvad.mullvadvpn.usecase.PaymentUseCase
import net.mullvad.mullvadvpn.util.isSuccess
import net.mullvad.mullvadvpn.util.toPaymentState
-import org.joda.time.DateTime
class AccountViewModel(
private val accountRepository: AccountRepository,
@@ -155,7 +155,7 @@ class AccountViewModel(
data class AccountUiState(
val deviceName: String?,
val accountNumber: AccountNumber?,
- val accountExpiry: DateTime?,
+ val accountExpiry: ZonedDateTime?,
val showSitePayment: Boolean,
val billingPaymentState: PaymentState? = null,
val showLogoutLoading: Boolean = false,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt
index 6e8b3f9009..d505c44179 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt
@@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.state.ConnectUiState
+import net.mullvad.mullvadvpn.lib.common.util.daysFromNow
import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect
import net.mullvad.mullvadvpn.lib.model.ConnectError
import net.mullvad.mullvadvpn.lib.model.DeviceState
@@ -34,7 +35,6 @@ import net.mullvad.mullvadvpn.usecase.OutOfTimeUseCase
import net.mullvad.mullvadvpn.usecase.PaymentUseCase
import net.mullvad.mullvadvpn.usecase.SelectedLocationTitleUseCase
import net.mullvad.mullvadvpn.util.combine
-import net.mullvad.mullvadvpn.util.daysFromNow
import net.mullvad.mullvadvpn.util.isSuccess
import net.mullvad.mullvadvpn.util.withPrev
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt
index 25a8a47da3..088b5f3ae2 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt
@@ -24,6 +24,7 @@ import net.mullvad.mullvadvpn.compose.state.LoginState.Idle
import net.mullvad.mullvadvpn.compose.state.LoginState.Loading
import net.mullvad.mullvadvpn.compose.state.LoginState.Success
import net.mullvad.mullvadvpn.compose.state.LoginUiState
+import net.mullvad.mullvadvpn.lib.common.util.isBeforeNowInstant
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.model.LoginAccountError
import net.mullvad.mullvadvpn.lib.shared.AccountRepository
@@ -137,7 +138,9 @@ class LoginViewModel(
viewModelScope.launch(dispatcher) {
// Find if user is out of time
val isOutOfTimeDeferred = async {
- accountRepository.accountData.mapNotNull { it?.expiryDate?.isBeforeNow }.first()
+ accountRepository.accountData
+ .mapNotNull { it?.expiryDate?.isBeforeNowInstant() }
+ .first()
}
// Always show successful login for some time.
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt
index 0ed85c94cd..fe0acd83c1 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt
@@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.selects.onTimeout
import kotlinx.coroutines.selects.select
import net.mullvad.mullvadvpn.constant.ACCOUNT_EXPIRY_TIMEOUT_MS
+import net.mullvad.mullvadvpn.lib.common.util.isBeforeNowInstant
import net.mullvad.mullvadvpn.lib.model.DeviceState
import net.mullvad.mullvadvpn.lib.shared.AccountRepository
import net.mullvad.mullvadvpn.lib.shared.DeviceRepository
@@ -72,7 +73,7 @@ class SplashViewModel(
onTimeout(ACCOUNT_EXPIRY_TIMEOUT_MS) { null }
}
- return if (accountData != null && accountData.expiryDate.isBeforeNow) {
+ return if (accountData != null && accountData.expiryDate.isBeforeNowInstant()) {
SplashUiSideEffect.NavigateToOutOfTime
} else {
SplashUiSideEffect.NavigateToConnect
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt
index 67e0f7a54d..0e91390262 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt
@@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.compose.state.WelcomeUiState
+import net.mullvad.mullvadvpn.lib.common.util.isAfterNowInstant
import net.mullvad.mullvadvpn.lib.model.WebsiteAuthToken
import net.mullvad.mullvadvpn.lib.shared.AccountRepository
import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy
@@ -66,7 +67,7 @@ class WelcomeViewModel(
private fun hasAddedTimeEffect() =
accountRepository.accountData
.filterNotNull()
- .filter { it.expiryDate.minusHours(MIN_HOURS_PAST_ACCOUNT_EXPIRY).isAfterNow }
+ .filter { it.expiryDate.minusHours(MIN_HOURS_PAST_ACCOUNT_EXPIRY).isAfterNowInstant() }
.onEach { paymentUseCase.resetPurchaseResult() }
.map { UiSideEffect.OpenConnectScreen }
@@ -125,6 +126,6 @@ class WelcomeViewModel(
}
companion object {
- private const val MIN_HOURS_PAST_ACCOUNT_EXPIRY = 20
+ private const val MIN_HOURS_PAST_ACCOUNT_EXPIRY: Long = 20
}
}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/AccountExpiryNotificationProviderTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/AccountExpiryNotificationProviderTest.kt
index d830f407ab..9a3672515d 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/AccountExpiryNotificationProviderTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/AccountExpiryNotificationProviderTest.kt
@@ -5,6 +5,8 @@ import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.mockk
import io.mockk.unmockkAll
+import java.time.Duration
+import java.time.ZonedDateTime
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlin.time.Duration.Companion.minutes
@@ -25,8 +27,6 @@ import net.mullvad.mullvadvpn.lib.shared.AccountRepository
import net.mullvad.mullvadvpn.lib.shared.DeviceRepository
import net.mullvad.mullvadvpn.service.notifications.accountexpiry.ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD
import net.mullvad.mullvadvpn.service.notifications.accountexpiry.AccountExpiryNotificationProvider
-import org.joda.time.DateTime
-import org.joda.time.Duration
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -80,7 +80,9 @@ class AccountExpiryNotificationProviderTest {
@Test
fun `should emit notification if expiry time is shorter than expiry warning threshold`() =
runTest {
- setExpiry(DateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusDays(1))
+ setExpiry(
+ ZonedDateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusDays(1)
+ )
provider.notifications.test {
assertTrue(awaitItem() is Notify)
expectNoEvents()
@@ -90,7 +92,7 @@ class AccountExpiryNotificationProviderTest {
@Test
fun `should emit cancel notification if user account is new`() = runTest {
isNewDevice.value = true
- setExpiry(DateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusDays(1))
+ setExpiry(ZonedDateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusDays(1))
provider.notifications.test {
assertTrue(awaitItem() is Cancel)
expectNoEvents()
@@ -100,7 +102,7 @@ class AccountExpiryNotificationProviderTest {
@Test
fun `should emit cancel notification if user account is logged out`() = runTest {
setIsLoggedIn(false)
- setExpiry(DateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusDays(1))
+ setExpiry(ZonedDateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusDays(1))
provider.notifications.test {
assertTrue(awaitItem() is Cancel)
expectNoEvents()
@@ -117,7 +119,7 @@ class AccountExpiryNotificationProviderTest {
@Test
fun `should emit zero duration notification when remaining time runs out`() = runTest {
- setExpiry(DateTime.now().plus(Duration.standardSeconds(60)))
+ setExpiry(ZonedDateTime.now().plus(Duration.ofSeconds(60)))
provider.notifications.test {
assertTrue(awaitItem() is Notify)
expectNoEvents()
@@ -135,7 +137,10 @@ class AccountExpiryNotificationProviderTest {
@Test
fun `should emit notification when update interval is passed`() = runTest {
setExpiry(
- DateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusDays(1).plusHours(1)
+ ZonedDateTime.now()
+ .plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD)
+ .minusDays(1)
+ .plusHours(1)
)
provider.notifications.test {
assertTrue(awaitItem() is Notify)
@@ -152,12 +157,14 @@ class AccountExpiryNotificationProviderTest {
@Test
fun `should cancel existing notification if more time is added to account`() = runTest {
- setExpiry(DateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusDays(1))
+ setExpiry(ZonedDateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusDays(1))
provider.notifications.test {
assertTrue(awaitItem() is Notify)
expectNoEvents()
- setExpiry(DateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).plusDays(1))
+ setExpiry(
+ ZonedDateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).plusDays(1)
+ )
assertTrue(awaitItem() is Cancel)
expectNoEvents()
}
@@ -165,12 +172,14 @@ class AccountExpiryNotificationProviderTest {
@Test
fun `should not cancel existing notification if too little time is added`() = runTest {
- setExpiry(DateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusDays(1))
+ setExpiry(ZonedDateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusDays(1))
provider.notifications.test {
assertTrue(awaitItem() is Notify)
expectNoEvents()
- setExpiry(DateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusHours(1))
+ setExpiry(
+ ZonedDateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).minusHours(1)
+ )
assertTrue(awaitItem() is Notify)
expectNoEvents()
}
@@ -184,7 +193,7 @@ class AccountExpiryNotificationProviderTest {
is Notify -> awaitItem.value
}
- private fun setExpiry(expiryDateTime: DateTime): DateTime {
+ private fun setExpiry(expiryDateTime: ZonedDateTime): ZonedDateTime {
val expiry = AccountData(mockk(relaxed = true), expiryDateTime)
accountData.value = expiry
return expiryDateTime
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt
index c8b27f2e6f..9db14ad914 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt
@@ -5,6 +5,7 @@ import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.mockk
import io.mockk.unmockkAll
+import java.time.Duration
import kotlin.test.assertEquals
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -21,7 +22,6 @@ import net.mullvad.mullvadvpn.usecase.NewChangelogNotificationUseCase
import net.mullvad.mullvadvpn.usecase.NewDeviceNotificationUseCase
import net.mullvad.mullvadvpn.usecase.TunnelStateNotificationUseCase
import net.mullvad.mullvadvpn.usecase.VersionNotificationUseCase
-import org.joda.time.Duration
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCaseTest.kt
index 316d12addd..68b29790ac 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCaseTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCaseTest.kt
@@ -7,6 +7,8 @@ import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.mockk
import io.mockk.unmockkAll
+import java.time.Duration
+import java.time.ZonedDateTime
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlin.time.Duration.Companion.seconds
@@ -19,8 +21,6 @@ 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 org.joda.time.DateTime
-import org.joda.time.Duration
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -33,7 +33,7 @@ class AccountExpiryInAppNotificationUseCaseTest {
private lateinit var accountExpiryInAppNotificationUseCase:
AccountExpiryInAppNotificationUseCase
- private lateinit var notificationThreshold: DateTime
+ private lateinit var notificationThreshold: ZonedDateTime
@BeforeEach
fun setup() {
@@ -45,7 +45,7 @@ class AccountExpiryInAppNotificationUseCaseTest {
accountExpiryInAppNotificationUseCase =
AccountExpiryInAppNotificationUseCase(accountRepository)
- notificationThreshold = DateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD)
+ notificationThreshold = ZonedDateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD)
}
@AfterEach
@@ -102,7 +102,7 @@ class AccountExpiryInAppNotificationUseCaseTest {
// Set expiry to to be in the final update interval.
val inLastUpdate =
- DateTime.now()
+ ZonedDateTime.now()
.plus(ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL)
.minusSeconds(1)
val expiry = setExpiry(inLastUpdate)
@@ -113,34 +113,37 @@ class AccountExpiryInAppNotificationUseCaseTest {
expectNoEvents()
// Advance past the delay before the while loop:
- advanceTimeBy(ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL.millis)
+ advanceTimeBy(ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL.toMillis())
// Advance past the delay after the while loop:
- advanceTimeBy(ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL.millis)
+ advanceTimeBy(ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL.toMillis())
assertEquals(Duration.ZERO, getExpiryNotificationDuration(expectMostRecentItem()))
expectNoEvents()
// Make sure we reset the list of notifications emitted when new time is added
- setExpiry(DateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).plusDays(1))
+ setExpiry(
+ ZonedDateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD).plusDays(1)
+ )
assertEquals(emptyList(), expectMostRecentItem())
}
}
- private fun setExpiry(expiryDateTime: DateTime): DateTime {
+ private fun setExpiry(expiryDateTime: ZonedDateTime): ZonedDateTime {
val expiry = AccountData(mockk(relaxed = true), expiryDateTime)
accountExpiry.value = expiry
return expiryDateTime
}
// Assert that we got a single AccountExpiry notification and that the expiry duration is within
- // the expected range (checking exact duration value is not possible since we use DateTime.now)
+ // the expected range (checking exact duration value is not possible since we use
+ // ZonedDateTime.now)
private fun assertExpiryNotificationDuration(
- expiry: DateTime,
+ expiry: ZonedDateTime,
notifications: List<InAppNotification>,
) {
val notificationDuration = getExpiryNotificationDuration(notifications)
- val expiresFromNow = Duration(DateTime.now(), expiry)
+ val expiresFromNow = Duration.between(ZonedDateTime.now(), expiry)
assertTrue(expiresFromNow <= notificationDuration)
- assertTrue(expiresFromNow.plus(Duration.standardSeconds(5)) > notificationDuration)
+ assertTrue(expiresFromNow.plus(Duration.ofSeconds(5)) > notificationDuration)
}
private fun getExpiryNotificationDuration(notifications: List<InAppNotification>): Duration {
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/NewDeviceNotificationUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/NewDeviceNotificationUseCaseTest.kt
index 32cc48f2c5..2c97ea36a1 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/NewDeviceNotificationUseCaseTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/NewDeviceNotificationUseCaseTest.kt
@@ -5,6 +5,7 @@ import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.unmockkAll
+import java.time.ZonedDateTime
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlinx.coroutines.flow.MutableStateFlow
@@ -18,7 +19,6 @@ import net.mullvad.mullvadvpn.lib.model.DeviceState
import net.mullvad.mullvadvpn.lib.shared.DeviceRepository
import net.mullvad.mullvadvpn.repository.InAppNotification
import net.mullvad.mullvadvpn.repository.NewDeviceRepository
-import org.joda.time.DateTime
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -35,7 +35,7 @@ class NewDeviceNotificationUseCaseTest {
Device(
id = DeviceId.fromString(UUID),
name = deviceName,
- creationDate = DateTime.now(),
+ creationDate = ZonedDateTime.now(),
),
)
)
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt
index f8ecefbffd..4fd3ba08d1 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt
@@ -4,6 +4,7 @@ import app.cash.turbine.test
import io.mockk.every
import io.mockk.mockk
import io.mockk.unmockkAll
+import java.time.ZonedDateTime
import kotlin.test.assertEquals
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.minutes
@@ -26,7 +27,6 @@ import net.mullvad.mullvadvpn.lib.model.ErrorStateCause
import net.mullvad.mullvadvpn.lib.model.TunnelState
import net.mullvad.mullvadvpn.lib.shared.AccountRepository
import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy
-import org.joda.time.DateTime
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -91,7 +91,7 @@ class OutOfTimeUseCaseTest {
scope.runTest {
// Arrange
val expiredAccountExpiry =
- AccountData(mockk(relaxed = true), DateTime.now().plusDays(1))
+ AccountData(mockk(relaxed = true), ZonedDateTime.now().plusDays(1))
val tunnelStateChanges =
listOf(
TunnelState.Disconnected(),
@@ -120,7 +120,7 @@ class OutOfTimeUseCaseTest {
scope.runTest {
// Arrange
val expiredAccountExpiry =
- AccountData(mockk(relaxed = true), DateTime.now().minusDays(1))
+ AccountData(mockk(relaxed = true), ZonedDateTime.now().minusDays(1))
// Act, Assert
outOfTimeUseCase.isOutOfTime.test {
assertEquals(null, awaitItem())
@@ -134,7 +134,7 @@ class OutOfTimeUseCaseTest {
scope.runTest {
// Arrange
val notExpiredAccountExpiry =
- AccountData(mockk(relaxed = true), DateTime.now().plusDays(1))
+ AccountData(mockk(relaxed = true), ZonedDateTime.now().plusDays(1))
// Act, Assert
outOfTimeUseCase.isOutOfTime.test {
@@ -149,7 +149,7 @@ class OutOfTimeUseCaseTest {
scope.runTest {
// Arrange
val expiredAccountExpiry =
- AccountData(mockk(relaxed = true), DateTime.now().plusSeconds(100))
+ AccountData(mockk(relaxed = true), ZonedDateTime.now().plusSeconds(100))
// Act, Assert
outOfTimeUseCase.isOutOfTime.test {
// Initial event
@@ -173,7 +173,7 @@ class OutOfTimeUseCaseTest {
scope.runTest {
// Arrange
val initialAccountExpiry =
- AccountData(mockk(relaxed = true), DateTime.now().plusSeconds(100))
+ AccountData(mockk(relaxed = true), ZonedDateTime.now().plusSeconds(100))
val updatedExpiry =
AccountData(mockk(relaxed = true), initialAccountExpiry.expiryDate.plusDays(30))
@@ -203,7 +203,7 @@ class OutOfTimeUseCaseTest {
scope.runTest {
// Arrange
val initialAccountExpiry =
- AccountData(mockk(relaxed = true), DateTime.now().plusSeconds(100))
+ AccountData(mockk(relaxed = true), ZonedDateTime.now().plusSeconds(100))
val updatedExpiry =
AccountData(mockk(relaxed = true), initialAccountExpiry.expiryDate.plusDays(30))
// Act, Assert
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModelTest.kt
index 0a9d2f36f9..74a6bbd414 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModelTest.kt
@@ -9,6 +9,7 @@ import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkAll
+import java.time.ZonedDateTime
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlinx.coroutines.flow.MutableStateFlow
@@ -28,7 +29,6 @@ import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult
import net.mullvad.mullvadvpn.lib.shared.AccountRepository
import net.mullvad.mullvadvpn.lib.shared.DeviceRepository
import net.mullvad.mullvadvpn.usecase.PaymentUseCase
-import org.joda.time.DateTime
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -42,7 +42,11 @@ class AccountViewModelTest {
private val mockPaymentUseCase: PaymentUseCase = mockk(relaxed = true)
private val dummyDevice =
- Device(id = DeviceId.fromString(UUID), name = "fake_name", creationDate = DateTime.now())
+ Device(
+ id = DeviceId.fromString(UUID),
+ name = "fake_name",
+ creationDate = ZonedDateTime.now(),
+ )
private val dummyAccountNumber: AccountNumber = AccountNumber(DUMMY_DEVICE_NAME)
private val deviceState: MutableStateFlow<DeviceState?> =
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt
index 1a80597066..a2be07e0a6 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt
@@ -11,6 +11,7 @@ import io.mockk.coVerify
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.mockk
+import java.time.ZonedDateTime
import kotlin.test.assertIs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -29,7 +30,6 @@ import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.model.LoginAccountError
import net.mullvad.mullvadvpn.lib.shared.AccountRepository
import net.mullvad.mullvadvpn.usecase.InternetAvailableUseCase
-import org.joda.time.DateTime
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -133,7 +133,9 @@ class LoginViewModelTest {
val sideEffects = loginViewModel.uiSideEffect.testIn(backgroundScope)
coEvery { mockedAccountRepository.login(any()) } returns Unit.right()
coEvery { mockedAccountRepository.accountData } returns
- MutableStateFlow(AccountData(mockk(relaxed = true), DateTime.now().plusDays(3)))
+ MutableStateFlow(
+ AccountData(mockk(relaxed = true), ZonedDateTime.now().plusDays(3))
+ )
// Act, Assert
uiStates.skipDefaultItem()
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt
index 6c1ff9e1a2..bfaed10629 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt
@@ -31,8 +31,6 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState
import net.mullvad.mullvadvpn.usecase.OutOfTimeUseCase
import net.mullvad.mullvadvpn.usecase.PaymentUseCase
-import org.joda.time.DateTime
-import org.joda.time.ReadableInstant
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -132,10 +130,6 @@ class OutOfTimeViewModelTest {
@Test
fun `when OutOfTimeUseCase returns false uiSideEffect should emit OpenConnectScreen`() =
runTest {
- // Arrange
- val mockExpiryDate: DateTime = mockk()
- every { mockExpiryDate.isAfter(any<ReadableInstant>()) } returns true
-
// Act, Assert
viewModel.uiSideEffect.test {
outOfTimeFlow.value = false
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VoucherDialogViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VoucherDialogViewModelTest.kt
index 3bd96de3cb..e5ae99d872 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VoucherDialogViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VoucherDialogViewModelTest.kt
@@ -8,6 +8,7 @@ import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.unmockkAll
+import java.time.ZonedDateTime
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlin.test.assertTrue
@@ -18,7 +19,6 @@ import net.mullvad.mullvadvpn.lib.model.RedeemVoucherError
import net.mullvad.mullvadvpn.lib.model.RedeemVoucherSuccess
import net.mullvad.mullvadvpn.lib.model.VoucherCode
import net.mullvad.mullvadvpn.lib.shared.VoucherRepository
-import org.joda.time.DateTime
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -50,7 +50,7 @@ class VoucherDialogViewModelTest {
// Arrange
val timeAdded = 0L
- val newExpiry = DateTime()
+ val newExpiry = ZonedDateTime.now()
coEvery { mockVoucherRepository.submitVoucher(parsedVoucher) } returns
RedeemVoucherSuccess(timeAdded, newExpiry).right()
@@ -89,7 +89,7 @@ class VoucherDialogViewModelTest {
every { mockVoucherSubmission.timeAdded } returns 0
coEvery {
mockVoucherRepository.submitVoucher(VoucherCode.fromString(voucher).getOrNull()!!)
- } returns RedeemVoucherSuccess(0, DateTime()).right()
+ } returns RedeemVoucherSuccess(0, ZonedDateTime.now()).right()
// Act, Assert
viewModel.uiState.test {
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt
index 3fe3804b8b..b2b59ba69e 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt
@@ -9,6 +9,7 @@ import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkAll
+import java.time.ZonedDateTime
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlinx.coroutines.cancel
@@ -32,7 +33,6 @@ import net.mullvad.mullvadvpn.lib.shared.DeviceRepository
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState
import net.mullvad.mullvadvpn.usecase.PaymentUseCase
-import org.joda.time.DateTime
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -146,7 +146,9 @@ class WelcomeViewModelTest {
@Test
fun `when user has added time then uiSideEffect should emit OpenConnectScreen`() = runTest {
// Arrange
- accountExpiryStateFlow.emit(AccountData(mockk(relaxed = true), DateTime().plusDays(1)))
+ accountExpiryStateFlow.emit(
+ AccountData(mockk(relaxed = true), ZonedDateTime.now().plusDays(1))
+ )
// Act, Assert
viewModel.uiSideEffect.test {
diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml
index da2a4b6c54..401c5681f7 100644
--- a/android/gradle/libs.versions.toml
+++ b/android/gradle/libs.versions.toml
@@ -64,7 +64,6 @@ commonsvalidator = "1.9.0"
# Upgrading to 12.0.1 results in task failing, DROID-1517
dependency-versions = "0.52.0"
detekt = "1.23.7"
-jodatime = "2.13.1"
kermit = "2.0.5"
konsist = "0.17.3"
ktfmt = "0.22.0"
@@ -160,7 +159,6 @@ mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" }
# Misc
commons-validator = { module = "commons-validator:commons-validator", version.ref = "commonsvalidator" }
-jodatime = { module = "joda-time:joda-time", version.ref = "jodatime" }
kermit = { module = "co.touchlab:kermit", version.ref = "kermit" }
konsist = { module = "com.lemonappdev:konsist", version.ref = "konsist" }
leakCanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }
diff --git a/android/lib/common/build.gradle.kts b/android/lib/common/build.gradle.kts
index c8554b52c7..24468d9815 100644
--- a/android/lib/common/build.gradle.kts
+++ b/android/lib/common/build.gradle.kts
@@ -34,7 +34,6 @@ dependencies {
implementation(libs.arrow)
implementation(libs.androidx.appcompat)
- implementation(libs.jodatime)
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kermit)
diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CommonStringExtensions.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CommonStringExtensions.kt
index 536fea3d24..e34bfa582e 100644
--- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CommonStringExtensions.kt
+++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CommonStringExtensions.kt
@@ -1,22 +1,8 @@
package net.mullvad.mullvadvpn.lib.common.util
-import org.joda.time.DateTime
-import org.joda.time.format.DateTimeFormat
-
-private const val EXPIRY_FORMAT = "YYYY-MM-dd HH:mm:ss z"
private const val BIG_DOT_CHAR = "●"
private const val SPACE_CHAR = ' '
-fun String.parseAsDateTime(): DateTime? {
- return try {
- DateTime.parse(this, DateTimeFormat.forPattern(EXPIRY_FORMAT))
- } catch (ex: IllegalArgumentException) {
- null
- } catch (ex: UnsupportedOperationException) {
- null
- }
-}
-
fun String.groupWithSpaces(groupCharSize: Int = 4): String {
return fold(StringBuilder()) { formattedText, nextDigit ->
if ((formattedText.length % (groupCharSize + 1)) == groupCharSize) {
diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/DateExtensions.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/DateExtensions.kt
new file mode 100644
index 0000000000..74fe9d6ba0
--- /dev/null
+++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/DateExtensions.kt
@@ -0,0 +1,20 @@
+package net.mullvad.mullvadvpn.lib.common.util
+
+import java.time.Duration
+import java.time.Instant
+import java.time.ZonedDateTime
+import java.time.format.DateTimeFormatter
+import java.time.format.FormatStyle
+
+fun ZonedDateTime.formatDate(): String = DateTimeFormatter.ISO_LOCAL_DATE.format(this)
+
+fun ZonedDateTime.toExpiryDateString(): String =
+ DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT).format(this)
+
+fun ZonedDateTime.millisFromNow(): Long = Duration.between(ZonedDateTime.now(), this).toMillis()
+
+fun ZonedDateTime.daysFromNow(): Long = Duration.between(ZonedDateTime.now(), this).toDays()
+
+fun ZonedDateTime.isBeforeNowInstant(): Boolean = toInstant().isBefore(Instant.now())
+
+fun ZonedDateTime.isAfterNowInstant(): Boolean = toInstant().isAfter(Instant.now())
diff --git a/android/lib/daemon-grpc/build.gradle.kts b/android/lib/daemon-grpc/build.gradle.kts
index 60e2073843..f3b8799f95 100644
--- a/android/lib/daemon-grpc/build.gradle.kts
+++ b/android/lib/daemon-grpc/build.gradle.kts
@@ -63,7 +63,6 @@ dependencies {
implementation(projects.lib.model)
implementation(projects.lib.talpid)
- implementation(libs.jodatime)
implementation(libs.kermit)
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.coroutines)
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 fe4cf11881..717bf401f4 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
@@ -5,6 +5,8 @@ package net.mullvad.mullvadvpn.lib.daemon.grpc.mapper
import io.grpc.ConnectivityState
import java.net.InetAddress
import java.net.InetSocketAddress
+import java.time.Instant
+import java.time.ZoneId
import java.util.UUID
import mullvad_daemon.management_interface.ManagementInterface
import net.mullvad.mullvadvpn.lib.daemon.grpc.GrpcConnectivityState
@@ -72,7 +74,6 @@ import net.mullvad.mullvadvpn.lib.model.WireguardConstraints
import net.mullvad.mullvadvpn.lib.model.WireguardEndpointData
import net.mullvad.mullvadvpn.lib.model.WireguardRelayEndpointData
import net.mullvad.mullvadvpn.lib.model.WireguardTunnelOptions
-import org.joda.time.Instant
internal fun ManagementInterface.TunnelState.toDomain(): TunnelState =
when (stateCase!!) {
@@ -570,8 +571,10 @@ internal fun ManagementInterface.Relay.toDomain(
} else false,
)
+private fun Instant.atDefaultZone() = atZone(ZoneId.systemDefault())
+
internal fun ManagementInterface.Device.toDomain(): Device =
- Device(DeviceId.fromString(id), name, Instant.ofEpochSecond(created.seconds).toDateTime())
+ Device(DeviceId.fromString(id), name, Instant.ofEpochSecond(created.seconds).atDefaultZone())
internal fun ManagementInterface.DeviceState.toDomain(): DeviceState =
when (state) {
@@ -587,13 +590,13 @@ internal fun ManagementInterface.DeviceState.toDomain(): DeviceState =
internal fun ManagementInterface.AccountData.toDomain(): AccountData =
AccountData(
AccountId(UUID.fromString(id)),
- expiryDate = Instant.ofEpochSecond(expiry.seconds).toDateTime(),
+ expiryDate = Instant.ofEpochSecond(expiry.seconds).atDefaultZone(),
)
internal fun ManagementInterface.VoucherSubmission.toDomain(): RedeemVoucherSuccess =
RedeemVoucherSuccess(
timeAdded = secondsAdded,
- newExpiryDate = Instant.ofEpochSecond(newExpiry.seconds).toDateTime(),
+ newExpiryDate = Instant.ofEpochSecond(newExpiry.seconds).atDefaultZone(),
)
internal fun ManagementInterface.SplitTunnelSettings.toDomain(): SplitTunnelSettings =
diff --git a/android/lib/model/build.gradle.kts b/android/lib/model/build.gradle.kts
index c961dcc32f..f83ff3d5d4 100644
--- a/android/lib/model/build.gradle.kts
+++ b/android/lib/model/build.gradle.kts
@@ -35,7 +35,6 @@ android {
}
dependencies {
- implementation(libs.jodatime)
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.arrow)
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 8a4182b2e5..b1746e1bcc 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
@@ -1,5 +1,5 @@
package net.mullvad.mullvadvpn.lib.model
-import org.joda.time.DateTime
+import java.time.ZonedDateTime
-data class AccountData(val id: AccountId, val expiryDate: DateTime)
+data class AccountData(val id: AccountId, val expiryDate: ZonedDateTime)
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Device.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Device.kt
index e8303f0eca..3eb92f2f8f 100644
--- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Device.kt
+++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Device.kt
@@ -1,12 +1,12 @@
package net.mullvad.mullvadvpn.lib.model
import android.os.Parcelable
+import java.time.ZonedDateTime
import kotlinx.parcelize.Parcelize
import net.mullvad.mullvadvpn.lib.model.extensions.startCase
-import org.joda.time.DateTime
@Parcelize
-data class Device(val id: DeviceId, private val name: String, val creationDate: DateTime) :
+data class Device(val id: DeviceId, private val name: String, val creationDate: ZonedDateTime) :
Parcelable {
fun displayName(): String = name.startCase()
}
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 acb8d74907..6b073988a3 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
@@ -1,6 +1,6 @@
package net.mullvad.mullvadvpn.lib.model
-import org.joda.time.Duration
+import java.time.Duration
sealed interface Notification {
val actions: List<NotificationAction>
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RedeemVoucherSuccess.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RedeemVoucherSuccess.kt
index 9c81042b8c..53fa414047 100644
--- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RedeemVoucherSuccess.kt
+++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RedeemVoucherSuccess.kt
@@ -1,5 +1,5 @@
package net.mullvad.mullvadvpn.lib.model
-import org.joda.time.DateTime
+import java.time.ZonedDateTime
-data class RedeemVoucherSuccess(val timeAdded: Long, val newExpiryDate: DateTime)
+data class RedeemVoucherSuccess(val timeAdded: Long, val newExpiryDate: ZonedDateTime)
diff --git a/android/lib/shared/build.gradle.kts b/android/lib/shared/build.gradle.kts
index 4be82a9eb2..7e2730a9e9 100644
--- a/android/lib/shared/build.gradle.kts
+++ b/android/lib/shared/build.gradle.kts
@@ -41,7 +41,6 @@ dependencies {
implementation(libs.kermit)
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.coroutines.android)
- implementation(libs.jodatime)
testImplementation(libs.kotlin.test)
testImplementation(libs.kotlinx.coroutines.test)
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 f6b146ecd3..a0edc2faa6 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
@@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.lib.shared
import arrow.core.Either
import arrow.core.raise.nullable
+import java.time.ZonedDateTime
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -21,7 +22,6 @@ import net.mullvad.mullvadvpn.lib.model.CreateAccountError
import net.mullvad.mullvadvpn.lib.model.DeviceState
import net.mullvad.mullvadvpn.lib.model.LoginAccountError
import net.mullvad.mullvadvpn.lib.model.WebsiteAuthToken
-import org.joda.time.DateTime
class AccountRepository(
private val managementService: ManagementService,
@@ -87,7 +87,7 @@ class AccountRepository(
suspend fun getWebsiteAuthToken(): WebsiteAuthToken? =
managementService.getWebsiteAuthToken().getOrNull()
- internal suspend fun onVoucherRedeemed(newExpiry: DateTime) {
+ internal suspend fun onVoucherRedeemed(newExpiry: ZonedDateTime) {
accountData.value?.copy(expiryDate = newExpiry)?.let { _mutableAccountDataCache.emit(it) }
}
}
diff --git a/android/service/build.gradle.kts b/android/service/build.gradle.kts
index 8c9f0034cb..7fd560aa43 100644
--- a/android/service/build.gradle.kts
+++ b/android/service/build.gradle.kts
@@ -79,5 +79,4 @@ dependencies {
implementation(libs.koin.android)
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.coroutines.android)
- implementation(libs.jodatime)
}
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 db9ee92ae7..a61561c8cc 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
@@ -5,12 +5,12 @@ import android.content.Context
import android.content.Intent
import android.content.res.Resources
import androidx.core.app.NotificationCompat
+import java.time.Duration
import net.mullvad.mullvadvpn.lib.common.constant.MAIN_ACTIVITY_CLASS
import net.mullvad.mullvadvpn.lib.common.util.SdkUtils
import net.mullvad.mullvadvpn.lib.common.util.createAccountUri
import net.mullvad.mullvadvpn.lib.model.Notification
import net.mullvad.mullvadvpn.service.R
-import org.joda.time.Duration
internal fun Notification.AccountExpiry.toNotification(context: Context) =
NotificationCompat.Builder(context, channelId.value)
@@ -40,19 +40,19 @@ private fun Notification.AccountExpiry.contentIntent(context: Context): PendingI
private fun Resources.contentTitle(remainingTime: Duration): String =
when {
- remainingTime.isShorterThan(Duration.ZERO) || remainingTime == Duration.ZERO -> {
+ remainingTime <= Duration.ZERO -> {
getString(R.string.account_credit_has_expired)
}
- remainingTime.standardDays >= 1 -> {
+ remainingTime.toDays() >= 1 -> {
getRemainingText(
R.plurals.account_credit_expires_in_days,
- remainingTime.standardDays.toInt(),
+ remainingTime.toDays().toInt(),
)
}
- remainingTime.standardHours >= 1 -> {
+ remainingTime.toHours() >= 1 -> {
getRemainingText(
R.plurals.account_credit_expires_in_hours,
- remainingTime.standardHours.toInt(),
+ remainingTime.toHours().toInt(),
)
}
else -> getString(R.string.account_credit_expires_in_a_few_minutes)
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 32190968b9..4ee9c6a533 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
@@ -2,10 +2,10 @@
package net.mullvad.mullvadvpn.service.notifications.accountexpiry
+import java.time.Duration
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_SYSTEM_NOTIFICATION_UPDATE_INTERVAL: Duration = Duration.standardDays(1)
-val ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD: Duration = Duration.standardDays(3)
+val ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL: Duration = Duration.ofDays(1)
+val ACCOUNT_EXPIRY_SYSTEM_NOTIFICATION_UPDATE_INTERVAL: Duration = Duration.ofDays(1)
+val ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD: Duration = Duration.ofDays(3)
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 b513b490e0..be0141c3f4 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
@@ -1,5 +1,6 @@
package net.mullvad.mullvadvpn.service.notifications.accountexpiry
+import java.time.ZonedDateTime
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -16,7 +17,6 @@ import net.mullvad.mullvadvpn.lib.shared.AccountRepository
import net.mullvad.mullvadvpn.lib.shared.DeviceRepository
import net.mullvad.mullvadvpn.service.constant.IS_PLAY_BUILD
import net.mullvad.mullvadvpn.service.notifications.NotificationProvider
-import org.joda.time.DateTime
class AccountExpiryNotificationProvider(
private val channelId: NotificationChannelId,
@@ -45,7 +45,7 @@ class AccountExpiryNotificationProvider(
}
private fun accountExpiryNotificationFlow(
- expiryDate: DateTime
+ expiryDate: ZonedDateTime
): Flow<NotificationUpdate<Notification.AccountExpiry>> =
AccountExpiryTicker.tickerFlow(
expiry = expiryDate,
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryTicker.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryTicker.kt
index 6add0a372a..cd872eee18 100644
--- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryTicker.kt
+++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryTicker.kt
@@ -1,10 +1,12 @@
package net.mullvad.mullvadvpn.service.notifications.accountexpiry
+import java.time.Duration
+import java.time.Instant
+import java.time.ZonedDateTime
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 net.mullvad.mullvadvpn.lib.common.util.millisFromNow
sealed interface AccountExpiryTicker {
data object NotWithinThreshold : AccountExpiryTicker
@@ -13,9 +15,9 @@ sealed interface AccountExpiryTicker {
companion object {
fun tickerFlow(
- expiry: DateTime,
+ expiry: ZonedDateTime,
tickStart: Duration,
- updateInterval: (expiry: DateTime) -> Duration,
+ updateInterval: (expiry: ZonedDateTime) -> Duration,
): Flow<AccountExpiryTicker> = flow {
expiry.millisFromNow().let { expiryMillis ->
if (expiryMillis <= 0) {
@@ -23,20 +25,20 @@ sealed interface AccountExpiryTicker {
emit(Tick(Duration.ZERO))
return@flow
}
- if (expiryMillis > tickStart.millis) {
+ if (expiryMillis > tickStart.toMillis()) {
// Emit NotWithinThreshold if no expiry notification should be provided.
emit(NotWithinThreshold)
// Delay until the time we should start emitting.
- delay(expiryMillis - tickStart.millis + 1)
+ delay(expiryMillis - tickStart.toMillis() + 1)
}
}
- var currentUpdateInterval = updateInterval(expiry).millis
+ var currentUpdateInterval = updateInterval(expiry).toMillis()
do {
- emit(Tick(Duration(DateTime.now(), expiry)))
+ emit(Tick(Duration.between(Instant.now(), expiry)))
delay(millisUntilNextUpdate(expiry.millisFromNow(), currentUpdateInterval))
- currentUpdateInterval = updateInterval(expiry).millis
+ currentUpdateInterval = updateInterval(expiry).toMillis()
} while (hasAnotherEmission(expiry.millisFromNow(), currentUpdateInterval))
// We may have remaining time if the update interval wasn't a multiple of the remaining
@@ -67,5 +69,3 @@ private fun calculateDelaysNeeded(
millisUntilExpiry: Long,
currentUpdateIntervalMillis: Long,
): Long = millisUntilExpiry.coerceAtLeast(0) / currentUpdateIntervalMillis
-
-private fun DateTime.millisFromNow(): Long = Duration(DateTime.now(), this).millis
diff --git a/android/test/e2e/build.gradle.kts b/android/test/e2e/build.gradle.kts
index 3f4244cde7..8bcc45ab77 100644
--- a/android/test/e2e/build.gradle.kts
+++ b/android/test/e2e/build.gradle.kts
@@ -155,7 +155,6 @@ dependencies {
implementation(libs.ktor.client.cio)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.ktor.client.content.negotiation)
- implementation(libs.jodatime)
androidTestUtil(libs.androidx.test.orchestrator)
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/router/packetCapture/Packet.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/router/packetCapture/Packet.kt
index 5c788e5710..cd6b2b855e 100644
--- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/router/packetCapture/Packet.kt
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/router/packetCapture/Packet.kt
@@ -1,23 +1,25 @@
package net.mullvad.mullvadvpn.test.e2e.router.packetCapture
+import java.time.ZonedDateTime
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mullvad.mullvadvpn.test.e2e.serializer.PacketSerializer
-import org.joda.time.DateTime
@Serializable(with = PacketSerializer::class)
sealed interface Packet {
- @SerialName("timestamp") val date: DateTime
+ @SerialName("timestamp") val date: ZonedDateTime
val fromPeer: Boolean
}
@Serializable
-data class RxPacket(@SerialName("timestamp") @Contextual override val date: DateTime) : Packet {
+data class RxPacket(@SerialName("timestamp") @Contextual override val date: ZonedDateTime) :
+ Packet {
@SerialName("from_peer") override val fromPeer: Boolean = false
}
@Serializable
-data class TxPacket(@SerialName("timestamp") @Contextual override val date: DateTime) : Packet {
+data class TxPacket(@SerialName("timestamp") @Contextual override val date: ZonedDateTime) :
+ Packet {
@SerialName("from_peer") override val fromPeer: Boolean = true
}
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/router/packetCapture/Stream.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/router/packetCapture/Stream.kt
index 564d0e25cb..68eb2783cc 100644
--- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/router/packetCapture/Stream.kt
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/router/packetCapture/Stream.kt
@@ -1,11 +1,11 @@
package net.mullvad.mullvadvpn.test.e2e.router.packetCapture
+import java.time.Duration
+import java.time.ZonedDateTime
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import net.mullvad.mullvadvpn.test.e2e.router.NetworkingProtocol
-import org.joda.time.DateTime
-import org.joda.time.Interval
@Serializable
data class Stream(
@@ -18,24 +18,26 @@ data class Stream(
@Transient val sourceHost = Host.fromString(sourceAddressAndPort)
@Transient val destinationHost = Host.fromString(destinationAddressAndPort)
- @Transient private val startDate: DateTime = packets.first().date
- @Transient private val endDate: DateTime = packets.last().date
- @Transient private val txStartDate: DateTime? = txPackets().firstOrNull()?.date
- @Transient private val txEndDate: DateTime? = txPackets().lastOrNull()?.date
- @Transient private val rxStartDate: DateTime? = rxPackets().firstOrNull()?.date
- @Transient private val rxEndDate: DateTime? = rxPackets().lastOrNull()?.date
+ @Transient private val startDate: ZonedDateTime = packets.first().date
+ @Transient private val endDate: ZonedDateTime = packets.last().date
+ @Transient private val txStartDate: ZonedDateTime? = txPackets().firstOrNull()?.date
+ @Transient private val txEndDate: ZonedDateTime? = txPackets().lastOrNull()?.date
+ @Transient private val rxStartDate: ZonedDateTime? = rxPackets().firstOrNull()?.date
+ @Transient private val rxEndDate: ZonedDateTime? = rxPackets().lastOrNull()?.date
- @Transient val interval = Interval(startDate, endDate)
+ @Transient val interval = Duration.between(startDate, endDate)
fun txPackets(): List<TxPacket> = packets.filterIsInstance<TxPacket>()
fun rxPackets(): List<RxPacket> = packets.filterIsInstance<RxPacket>()
- fun txInterval(): Interval? =
- if (txStartDate != null && txEndDate != null) Interval(txStartDate, txEndDate) else null
+ fun txInterval(): Duration? =
+ if (txStartDate != null && txEndDate != null) Duration.between(txStartDate, txEndDate)
+ else null
- fun rxInterval(): Interval? =
- if (rxStartDate != null && rxEndDate != null) Interval(rxStartDate, rxEndDate) else null
+ fun rxInterval(): Duration? =
+ if (rxStartDate != null && rxEndDate != null) Duration.between(rxStartDate, rxEndDate)
+ else null
init {
require(packets.isNotEmpty()) { "Stream must contain at least one packet" }
diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/serializer/NanoSecondsTimestampSerializer.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/serializer/NanoSecondsTimestampSerializer.kt
index d49ca6017d..26d5f05f5b 100644
--- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/serializer/NanoSecondsTimestampSerializer.kt
+++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/serializer/NanoSecondsTimestampSerializer.kt
@@ -1,23 +1,25 @@
package net.mullvad.mullvadvpn.test.e2e.serializer
+import java.time.Instant
+import java.time.ZoneId
+import java.time.ZonedDateTime
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
-import org.joda.time.DateTime
-object NanoSecondsTimestampSerializer : KSerializer<DateTime> {
+object NanoSecondsTimestampSerializer : KSerializer<ZonedDateTime> {
override val descriptor: SerialDescriptor =
- PrimitiveSerialDescriptor("DateTime", PrimitiveKind.LONG)
+ PrimitiveSerialDescriptor("ZonedDateTime", PrimitiveKind.LONG)
- override fun deserialize(decoder: Decoder): DateTime {
+ override fun deserialize(decoder: Decoder): ZonedDateTime {
val long = decoder.decodeLong()
- return DateTime(long / 1000)
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(long / 1000), ZoneId.systemDefault())
}
- override fun serialize(encoder: Encoder, value: DateTime) {
+ override fun serialize(encoder: Encoder, value: ZonedDateTime) {
throw NotImplementedError("Only interested in deserialization")
}
}
diff --git a/android/test/mockapi/build.gradle.kts b/android/test/mockapi/build.gradle.kts
index a224b8c53f..f0310107ac 100644
--- a/android/test/mockapi/build.gradle.kts
+++ b/android/test/mockapi/build.gradle.kts
@@ -72,7 +72,6 @@ dependencies {
implementation(libs.androidx.test.rules)
implementation(libs.androidx.test.uiautomator)
implementation(libs.kermit)
- implementation(libs.jodatime)
implementation(Dependencies.junitJupiterApi)
implementation(Dependencies.junit5AndroidTestExtensions)
implementation(Dependencies.junit5AndroidTestRunner)
diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountExpiryMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountExpiryMockApiTest.kt
index 2fe829841c..d89313fc45 100644
--- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountExpiryMockApiTest.kt
+++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountExpiryMockApiTest.kt
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn.test.mockapi
import androidx.test.uiautomator.By
+import java.time.ZonedDateTime
import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer
import net.mullvad.mullvadvpn.test.common.extension.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove
import net.mullvad.mullvadvpn.test.common.extension.dismissChangelogDialogIfShown
@@ -8,8 +9,7 @@ import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout
import net.mullvad.mullvadvpn.test.mockapi.constant.DEFAULT_DEVICE_LIST
import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_DEVICE_NAME_2
import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_2
-import net.mullvad.mullvadvpn.test.mockapi.util.currentUtcTimeWithOffsetZero
-import net.mullvad.mullvadvpn.util.toExpiryDateString
+import net.mullvad.mullvadvpn.test.mockapi.util.toExpiryDateString
import org.junit.jupiter.api.Test
class AccountExpiryMockApiTest : MockApiTest() {
@@ -18,7 +18,7 @@ class AccountExpiryMockApiTest : MockApiTest() {
fun testAccountExpiryDateUpdated() {
// Arrange
val validAccountNumber = "1234123412341234"
- val oldAccountExpiry = currentUtcTimeWithOffsetZero().plusMonths(1)
+ val oldAccountExpiry = ZonedDateTime.now().plusMonths(1)
apiDispatcher.apply {
expectedAccountNumber = validAccountNumber
accountExpiry = oldAccountExpiry
@@ -52,7 +52,7 @@ class AccountExpiryMockApiTest : MockApiTest() {
fun testAccountTimeExpiredWhileUsingTheAppShouldShowOutOfTimeScreen() {
// Arrange
val validAccountNumber = "1234123412341234"
- val oldAccountExpiry = currentUtcTimeWithOffsetZero().plusMonths(1)
+ val oldAccountExpiry = ZonedDateTime.now().plusMonths(1)
apiDispatcher.apply {
expectedAccountNumber = validAccountNumber
accountExpiry = oldAccountExpiry
diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountHistoryMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountHistoryMockApiTest.kt
index 7d4639019a..deb63c89b4 100644
--- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountHistoryMockApiTest.kt
+++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/AccountHistoryMockApiTest.kt
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn.test.mockapi
import androidx.test.uiautomator.By
+import java.time.ZonedDateTime
import net.mullvad.mullvadvpn.compose.test.LOGIN_INPUT_TEST_TAG
import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer
import net.mullvad.mullvadvpn.test.common.extension.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove
@@ -9,7 +10,6 @@ import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout
import net.mullvad.mullvadvpn.test.mockapi.constant.DEFAULT_DEVICE_LIST
import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_DEVICE_NAME_2
import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_2
-import net.mullvad.mullvadvpn.test.mockapi.util.currentUtcTimeWithOffsetZero
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test
@@ -21,7 +21,7 @@ class AccountHistoryMockApiTest : MockApiTest() {
val validAccountNumber = "1234123412341234"
apiDispatcher.apply {
expectedAccountNumber = validAccountNumber
- accountExpiry = currentUtcTimeWithOffsetZero().plusMonths(1)
+ accountExpiry = ZonedDateTime.now().plusMonths(1)
devices = DEFAULT_DEVICE_LIST.toMutableMap()
devicePendingToGetCreated = DUMMY_ID_2 to DUMMY_DEVICE_NAME_2
}
diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LoginMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LoginMockApiTest.kt
index f13e5070e2..5feee8abd8 100644
--- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LoginMockApiTest.kt
+++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LoginMockApiTest.kt
@@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.test.mockapi
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
+import java.time.ZonedDateTime
import net.mullvad.mullvadvpn.compose.test.LOGIN_TITLE_TEST_TAG
import net.mullvad.mullvadvpn.test.common.constant.DEFAULT_TIMEOUT
import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer
@@ -10,7 +11,6 @@ import net.mullvad.mullvadvpn.test.common.extension.dismissChangelogDialogIfShow
import net.mullvad.mullvadvpn.test.mockapi.constant.DEFAULT_DEVICE_LIST
import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_DEVICE_NAME_2
import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_2
-import net.mullvad.mullvadvpn.test.mockapi.util.currentUtcTimeWithOffsetZero
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
@@ -21,7 +21,7 @@ class LoginMockApiTest : MockApiTest() {
val validAccountNumber = "1234123412341234"
apiDispatcher.apply {
expectedAccountNumber = null
- accountExpiry = currentUtcTimeWithOffsetZero().plusDays(1)
+ accountExpiry = ZonedDateTime.now().plusDays(1)
}
app.launch(endpoint)
@@ -47,7 +47,7 @@ class LoginMockApiTest : MockApiTest() {
val validAccountNumber = "1234123412341234"
apiDispatcher.apply {
expectedAccountNumber = validAccountNumber
- accountExpiry = currentUtcTimeWithOffsetZero().plusDays(1)
+ accountExpiry = ZonedDateTime.now().plusDays(1)
devices = DEFAULT_DEVICE_LIST.toMutableMap()
devicePendingToGetCreated = DUMMY_ID_2 to DUMMY_DEVICE_NAME_2
}
@@ -70,7 +70,7 @@ class LoginMockApiTest : MockApiTest() {
val validAccountNumber = "1234123412341234"
apiDispatcher.apply {
expectedAccountNumber = validAccountNumber
- accountExpiry = currentUtcTimeWithOffsetZero().minusDays(1)
+ accountExpiry = ZonedDateTime.now().minusDays(1)
devices = DEFAULT_DEVICE_LIST.toMutableMap()
devicePendingToGetCreated = DUMMY_ID_2 to DUMMY_DEVICE_NAME_2
}
diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LogoutMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LogoutMockApiTest.kt
index 3df42ac0ce..da41d41066 100644
--- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LogoutMockApiTest.kt
+++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/LogoutMockApiTest.kt
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn.test.mockapi
import androidx.test.uiautomator.By
+import java.time.ZonedDateTime
import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer
import net.mullvad.mullvadvpn.test.common.extension.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove
import net.mullvad.mullvadvpn.test.common.extension.dismissChangelogDialogIfShown
@@ -8,7 +9,6 @@ import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout
import net.mullvad.mullvadvpn.test.mockapi.constant.DEFAULT_DEVICE_LIST
import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_DEVICE_NAME_2
import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_2
-import net.mullvad.mullvadvpn.test.mockapi.util.currentUtcTimeWithOffsetZero
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test
@@ -20,7 +20,7 @@ class LogoutMockApiTest : MockApiTest() {
val validAccountNumber = "1234123412341234"
apiDispatcher.apply {
expectedAccountNumber = validAccountNumber
- accountExpiry = currentUtcTimeWithOffsetZero().plusMonths(1)
+ accountExpiry = ZonedDateTime.now().plusMonths(1)
devices = DEFAULT_DEVICE_LIST.toMutableMap()
devicePendingToGetCreated = DUMMY_ID_2 to DUMMY_DEVICE_NAME_2
}
diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt
index e230152efe..f99cb48a06 100644
--- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt
+++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn.test.mockapi
import co.touchlab.kermit.Logger
+import java.time.ZonedDateTime
import net.mullvad.mullvadvpn.test.mockapi.constant.ACCOUNT_URL_PATH
import net.mullvad.mullvadvpn.test.mockapi.constant.AUTH_TOKEN_URL_PATH
import net.mullvad.mullvadvpn.test.mockapi.constant.CREATE_ACCOUNT_URL_PATH
@@ -10,20 +11,18 @@ import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_1
import net.mullvad.mullvadvpn.test.mockapi.util.accessTokenJsonResponse
import net.mullvad.mullvadvpn.test.mockapi.util.accountCreationJson
import net.mullvad.mullvadvpn.test.mockapi.util.accountInfoJson
-import net.mullvad.mullvadvpn.test.mockapi.util.currentUtcTimeWithOffsetZero
import net.mullvad.mullvadvpn.test.mockapi.util.deviceJson
import net.mullvad.mullvadvpn.test.mockapi.util.tooManyDevicesJsonResponse
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.RecordedRequest
import okio.Buffer
-import org.joda.time.DateTime
import org.json.JSONArray
class MockApiDispatcher : Dispatcher() {
var expectedAccountNumber: String? = null
- var accountExpiry: DateTime? = null
+ var accountExpiry: ZonedDateTime? = null
var devices: MutableMap<String, String>? = null
private val canAddDevices: Boolean
get() = (devices?.size ?: 0) < 5
@@ -84,7 +83,7 @@ class MockApiDispatcher : Dispatcher() {
.setBody(
accessTokenJsonResponse(
accessToken = DUMMY_ACCESS_TOKEN,
- expiry = currentUtcTimeWithOffsetZero().plusDays(1),
+ expiry = ZonedDateTime.now().plusDays(1),
)
.toString()
)
@@ -115,7 +114,7 @@ class MockApiDispatcher : Dispatcher() {
id = deviceId,
name = devices!![deviceId]!!, // Should always exist
publicKey = cachedKey,
- creationDate = currentUtcTimeWithOffsetZero().minusDays(1),
+ creationDate = ZonedDateTime.now().minusDays(1),
)
.toString()
)
@@ -136,7 +135,7 @@ class MockApiDispatcher : Dispatcher() {
id = devicePendingToGetCreated!!.first,
name = devicePendingToGetCreated!!.second,
publicKey = newKey,
- creationDate = currentUtcTimeWithOffsetZero().minusDays(1),
+ creationDate = ZonedDateTime.now().minusDays(1),
)
.toString()
)
@@ -158,7 +157,7 @@ class MockApiDispatcher : Dispatcher() {
id = entry.key,
name = entry.value,
publicKey = cachedKey,
- creationDate = currentUtcTimeWithOffsetZero().minusDays(index + 1),
+ creationDate = ZonedDateTime.now().minusDays((index + 1).toLong()),
)
)
}
@@ -174,7 +173,7 @@ class MockApiDispatcher : Dispatcher() {
.setBody(
accountCreationJson(
id = DUMMY_ID_1,
- expiry = DateTime(),
+ expiry = ZonedDateTime.now(),
accountNumber = expectedAccountNumber,
)
.toString()
diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/TooManyDevicesMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/TooManyDevicesMockApiTest.kt
index bde2a8f4dd..19674815c1 100644
--- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/TooManyDevicesMockApiTest.kt
+++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/TooManyDevicesMockApiTest.kt
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn.test.mockapi
import androidx.test.uiautomator.By
+import java.time.ZonedDateTime
import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaimer
import net.mullvad.mullvadvpn.test.common.extension.clickAllowOnNotificationPermissionPromptIfApiLevel33AndAbove
import net.mullvad.mullvadvpn.test.common.extension.dismissChangelogDialogIfShown
@@ -17,7 +18,6 @@ import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_3
import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_4
import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_5
import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID_6
-import net.mullvad.mullvadvpn.test.mockapi.util.currentUtcTimeWithOffsetZero
import org.junit.jupiter.api.Test
class TooManyDevicesMockApiTest : MockApiTest() {
@@ -27,7 +27,7 @@ class TooManyDevicesMockApiTest : MockApiTest() {
val validAccountNumber = "1234123412341234"
apiDispatcher.apply {
expectedAccountNumber = validAccountNumber
- accountExpiry = currentUtcTimeWithOffsetZero().plusMonths(1)
+ accountExpiry = ZonedDateTime.now().plusMonths(1)
devices =
mutableMapOf(
DUMMY_ID_1 to DUMMY_DEVICE_NAME_1,
diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/DateTimeUtils.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/DateTimeUtils.kt
index 81042f70d2..330fc15a81 100644
--- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/DateTimeUtils.kt
+++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/DateTimeUtils.kt
@@ -1,13 +1,14 @@
package net.mullvad.mullvadvpn.test.mockapi.util
-import org.joda.time.DateTime
-import org.joda.time.DateTimeZone
-import org.joda.time.format.DateTimeFormat
+import java.time.ZonedDateTime
+import java.time.format.DateTimeFormatter
+import java.time.format.FormatStyle
private const val STRICT_ISO8601_AND_RFC3339_PATTERN = "yyyy-MM-dd'T'HH:mm:ssZZ"
-fun currentUtcTimeWithOffsetZero() = DateTime.now(DateTimeZone.forOffsetHours(0))
-
-fun DateTime.formatStrictlyAccordingToIso8601AndRfc3339(): String {
- return toString(DateTimeFormat.forPattern(STRICT_ISO8601_AND_RFC3339_PATTERN))
+fun ZonedDateTime.formatStrictlyAccordingToIso8601AndRfc3339(): String {
+ return DateTimeFormatter.ofPattern(STRICT_ISO8601_AND_RFC3339_PATTERN).format(this)
}
+
+fun ZonedDateTime.toExpiryDateString(): String =
+ DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT).format(this)
diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt
index 2de385c7f8..a0e21d55ca 100644
--- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt
+++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt
@@ -1,9 +1,9 @@
package net.mullvad.mullvadvpn.test.mockapi.util
-import org.joda.time.DateTime
+import java.time.ZonedDateTime
import org.json.JSONObject
-fun accountInfoJson(id: String, expiry: DateTime) =
+fun accountInfoJson(id: String, expiry: ZonedDateTime) =
JSONObject().apply {
put("id", id)
put("expiry", expiry.formatStrictlyAccordingToIso8601AndRfc3339())
@@ -11,10 +11,10 @@ fun accountInfoJson(id: String, expiry: DateTime) =
put("can_add_devices", true)
}
-fun accountCreationJson(id: String, accountNumber: String, expiry: DateTime) =
+fun accountCreationJson(id: String, accountNumber: String, expiry: ZonedDateTime) =
accountInfoJson(id, expiry).apply { put("number", accountNumber) }
-fun deviceJson(id: String, name: String, publicKey: String, creationDate: DateTime) =
+fun deviceJson(id: String, name: String, publicKey: String, creationDate: ZonedDateTime) =
JSONObject().apply {
put("id", id)
put("name", name)
@@ -25,7 +25,7 @@ fun deviceJson(id: String, name: String, publicKey: String, creationDate: DateTi
put("ipv6_address", "fc00::1/128")
}
-fun accessTokenJsonResponse(accessToken: String, expiry: DateTime) =
+fun accessTokenJsonResponse(accessToken: String, expiry: ZonedDateTime) =
JSONObject().apply {
put("access_token", accessToken)
put("expiry", expiry.formatStrictlyAccordingToIso8601AndRfc3339())