summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2026-02-13 01:15:34 +0100
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2026-02-13 10:51:48 +0100
commitf03d6437ca6070407a2adbb1e50e6ba761e0c429 (patch)
treef7b72186cc3412bda83e1082c2edec7e2795f745
parent5cd0c476ee1f9509c42c1d3ef1ab5ec7475a583d (diff)
downloadmullvadvpn-f03d6437ca6070407a2adbb1e50e6ba761e0c429.tar.xz
mullvadvpn-f03d6437ca6070407a2adbb1e50e6ba761e0c429.zip
Refactor login into feature module
-rw-r--r--android/app/build.gradle.kts2
-rw-r--r--android/app/src/main/AndroidManifest.xml2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/MullvadApplication.kt8
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt10
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplashScreen.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/transitions/HomeTransition.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt12
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt6
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/receiver/ScheduleNotificationBootCompletedReceiver.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt1
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModel.kt4
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt4
-rw-r--r--android/lib/common-compose/src/main/kotlin/net/mullvad/mullvadvpn/common/compose/Keyboard.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/Keyboard.kt)2
-rw-r--r--android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/serviceconnection/EmptyServiceConnection.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/EmptyServiceConnection.kt)2
-rw-r--r--android/lib/feature/account/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreenTest.kt5
-rw-r--r--android/lib/feature/account/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountViewModelTest.kt6
-rw-r--r--android/lib/feature/login/impl/build.gradle.kts29
-rw-r--r--android/lib/feature/login/impl/src/main/AndroidManifest.xml4
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/CreateAccountConfirmationDialog.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateAccountConfirmationDialog.kt)7
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/LoginScreen.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt)111
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/LoginUiState.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/LoginUiState.kt)2
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/LoginUiStatePreviewParameterProvider.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/LoginUiStatePreviewParameterProvider.kt)5
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/LoginViewModel.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt)47
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/apiunreachable/ApiUnreachableInfoDialog.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/info/ApiUnreachableInfoDialog.kt)13
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/apiunreachable/ApiUnreachableUiState.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ApiUnreachableUiState.kt)4
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/apiunreachable/ApiUnreachableViewModel.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiUnreachableViewModel.kt)6
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/apiunreachable/SendEmail.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/SendEmail.kt)2
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DeviceListScreen.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt)20
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DeviceListUiState.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeviceListUiState.kt)2
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DeviceListUiStatePreviewParameterProvider.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DeviceListUiStatePreviewParameterProvider.kt)5
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DeviceListViewModel.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt)6
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DevicePreviewData.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DevicePreviewData.kt)3
-rw-r--r--android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/RemoveDeviceConfirmationDialog.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RemoveDeviceConfirmationDialog.kt)8
-rw-r--r--android/lib/feature/login/impl/src/test/java/net/mullvad/mullvadvpn/feature/login/impl/DeviceListViewModelTest.kt (renamed from android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModelTest.kt)10
-rw-r--r--android/lib/feature/login/impl/src/test/java/net/mullvad/mullvadvpn/feature/login/impl/LoginViewModelTest.kt (renamed from android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt)16
-rw-r--r--android/lib/feature/login/impl/src/test/java/net/mullvad/mullvadvpn/feature/login/impl/data/AccountData.kt12
-rw-r--r--android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/LoginTransition.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/transitions/LoginTransition.kt)23
-rw-r--r--android/lib/push-notification/build.gradle.kts22
-rw-r--r--android/lib/push-notification/src/main/AndroidManifest.xml4
-rw-r--r--android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/NotificationChannelFactory.kt (renamed from android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannelFactory.kt)2
-rw-r--r--android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/NotificationManager.kt (renamed from android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationManager.kt)8
-rw-r--r--android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/NotificationProvider.kt (renamed from android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationProvider.kt)2
-rw-r--r--android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/ScheduleNotificationAlarmUseCase.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/ScheduleNotificationAlarmUseCase.kt)4
-rw-r--r--android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/ShouldBeOnForegroundProvider.kt (renamed from android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ShouldBeOnForegroundProvider.kt)2
-rw-r--r--android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/accountexpiry/AccountExpiryAndroidNotification.kt (renamed from android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryAndroidNotification.kt)4
-rw-r--r--android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/accountexpiry/AccountExpiryNotificationProvider.kt (renamed from android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryNotificationProvider.kt)4
-rw-r--r--android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/receiver/NotificationAlarmReceiver.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/receiver/NotificationAlarmReceiver.kt)11
-rw-r--r--android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/tunnelstate/TunnelStateNotificationAction.kt (renamed from android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationAction.kt)6
-rw-r--r--android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/tunnelstate/TunnelStateNotificationProvider.kt (renamed from android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProvider.kt)6
-rw-r--r--android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/worker/ExpiryNotificationWorker.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/worker/ExpiryNotificationWorker.kt)10
-rw-r--r--android/service/build.gradle.kts1
-rw-r--r--android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt4
-rw-r--r--android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ForegroundNotificationManager.kt4
-rw-r--r--android/service/src/test/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProviderTest.kt1
-rw-r--r--android/settings.gradle.kts2
57 files changed, 282 insertions, 224 deletions
diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index bfdcd650b1..c89d94be8e 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -397,6 +397,7 @@ dependencies {
implementation(projects.lib.feature.customlist.impl)
implementation(projects.lib.feature.daita.impl)
implementation(projects.lib.feature.filter.impl)
+ implementation(projects.lib.feature.login.impl)
implementation(projects.lib.feature.managedevices.impl)
implementation(projects.lib.feature.multihop.impl)
implementation(projects.lib.feature.notification.impl)
@@ -408,6 +409,7 @@ dependencies {
implementation(projects.lib.feature.vpnsettings.impl)
implementation(projects.lib.map)
implementation(projects.lib.model)
+ implementation(projects.lib.pushNotification)
implementation(projects.lib.navigation)
implementation(projects.lib.payment)
implementation(projects.lib.repository)
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index b4fd266f2f..3d5137200e 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -274,7 +274,7 @@
</intent-filter>
</receiver>
<receiver
- android:name=".receiver.NotificationAlarmReceiver"
+ android:name=".lib.pushnotification.receiver.NotificationAlarmReceiver"
android:exported="false" />
<receiver
android:name=".receiver.AutoStartVpnBootCompletedReceiver"
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/MullvadApplication.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/MullvadApplication.kt
index 2fce5e116b..7992d2692f 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/MullvadApplication.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/MullvadApplication.kt
@@ -12,12 +12,12 @@ import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.di.ApplicationScope
import net.mullvad.mullvadvpn.di.KERMIT_FILE_LOG_DIR_NAME
import net.mullvad.mullvadvpn.di.appModule
+import net.mullvad.mullvadvpn.lib.pushnotification.NotificationChannelFactory
+import net.mullvad.mullvadvpn.lib.pushnotification.NotificationManager
+import net.mullvad.mullvadvpn.lib.pushnotification.ScheduleNotificationAlarmUseCase
+import net.mullvad.mullvadvpn.lib.pushnotification.accountexpiry.AccountExpiryNotificationProvider
import net.mullvad.mullvadvpn.lib.usecase.AccountExpiryNotificationActionUseCase
import net.mullvad.mullvadvpn.lib.usecase.NotificationAction
-import net.mullvad.mullvadvpn.service.notifications.NotificationChannelFactory
-import net.mullvad.mullvadvpn.service.notifications.NotificationManager
-import net.mullvad.mullvadvpn.service.notifications.accountexpiry.AccountExpiryNotificationProvider
-import net.mullvad.mullvadvpn.usecase.ScheduleNotificationAlarmUseCase
import net.mullvad.mullvadvpn.util.FileLogWriter
import org.koin.android.ext.android.getKoin
import org.koin.android.ext.koin.androidContext
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt
index b1c5f491c5..012ed00d57 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt
@@ -23,7 +23,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.compose.dropUnlessResumed
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.generated.NavGraphs
-import com.ramcosta.composedestinations.generated.destinations.LoginDestination
+import com.ramcosta.composedestinations.generated.login.destinations.LoginDestination
import com.ramcosta.composedestinations.generated.settings.destinations.SettingsDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import net.mullvad.mullvadvpn.R
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt
index bfbe5c0886..9e823c7682 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt
@@ -48,6 +48,11 @@ import com.ramcosta.composedestinations.generated.daita.destinations.DaitaDirect
import com.ramcosta.composedestinations.generated.daita.destinations.DaitaDirectOnlyInfoDestination
import com.ramcosta.composedestinations.generated.destinations.NoDaemonDestination
import com.ramcosta.composedestinations.generated.filter.destinations.FilterDestination
+import com.ramcosta.composedestinations.generated.login.destinations.ApiUnreachableInfoDestination
+import com.ramcosta.composedestinations.generated.login.destinations.CreateAccountConfirmationDestination
+import com.ramcosta.composedestinations.generated.login.destinations.DeviceListDestination
+import com.ramcosta.composedestinations.generated.login.destinations.LoginDestination
+import com.ramcosta.composedestinations.generated.login.destinations.RemoveDeviceConfirmationDestination
import com.ramcosta.composedestinations.generated.managedevices.destinations.ManageDevicesDestination
import com.ramcosta.composedestinations.generated.managedevices.destinations.ManageDevicesRemoveConfirmationDestination
import com.ramcosta.composedestinations.generated.multihop.destinations.MultihopDestination
@@ -94,12 +99,14 @@ annotation class MainGraph {
@ExternalDestination<ApiAccessListDestination>
@ExternalDestination<ApiAccessMethodDetailsDestination>
@ExternalDestination<ApiAccessMethodInfoDestination>
+ @ExternalDestination<ApiUnreachableInfoDestination>
@ExternalDestination<AppInfoDestination>
@ExternalDestination<AppearanceDestination>
@ExternalDestination<AutoConnectAndLockdownModeDestination>
@ExternalDestination<ChangelogDestination>
@ExternalDestination<ConnectOnStartupInfoDestination>
@ExternalDestination<ContentBlockersInfoDestination>
+ @ExternalDestination<CreateAccountConfirmationDestination>
@ExternalDestination<CreateCustomListDestination>
@ExternalDestination<CustomDnsInfoDestination>
@ExternalDestination<CustomListLocationsDestination>
@@ -111,6 +118,7 @@ annotation class MainGraph {
@ExternalDestination<DeleteApiAccessMethodConfirmationDestination>
@ExternalDestination<DeleteCustomListDestination>
@ExternalDestination<DeviceIpInfoDestination>
+ @ExternalDestination<DeviceListDestination>
@ExternalDestination<DiscardApiAccessChangesDestination>
@ExternalDestination<DiscardChangesDestination>
@ExternalDestination<DnsDestination>
@@ -121,6 +129,7 @@ annotation class MainGraph {
@ExternalDestination<ImportOverridesByTextDestination>
@ExternalDestination<Ipv6InfoDestination>
@ExternalDestination<LocalNetworkSharingInfoDestination>
+ @ExternalDestination<LoginDestination>
@ExternalDestination<MalwareInfoDestination>
@ExternalDestination<ManageDevicesDestination>
@ExternalDestination<ManageDevicesRemoveConfirmationDestination>
@@ -129,6 +138,7 @@ annotation class MainGraph {
@ExternalDestination<NotificationSettingsDestination>
@ExternalDestination<QuantumResistanceInfoDestination>
@ExternalDestination<RedeemVoucherDestination>
+ @ExternalDestination<RemoveDeviceConfirmationDestination>
@ExternalDestination<ReportProblemDestination>
@ExternalDestination<ReportProblemNoEmailDestination>
@ExternalDestination<ResetServerIpOverridesConfirmationDestination>
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt
index 4a4694c839..0dd1583928 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt
@@ -33,8 +33,8 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.generated.NavGraphs
-import com.ramcosta.composedestinations.generated.destinations.LoginDestination
import com.ramcosta.composedestinations.generated.destinations.SplashDestination
+import com.ramcosta.composedestinations.generated.login.destinations.LoginDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.launch
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplashScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplashScreen.kt
index 827a280190..4c87d0088e 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplashScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplashScreen.kt
@@ -21,9 +21,9 @@ import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.generated.NavGraphs
import com.ramcosta.composedestinations.generated.destinations.ConnectDestination
import com.ramcosta.composedestinations.generated.destinations.DeviceRevokedDestination
-import com.ramcosta.composedestinations.generated.destinations.LoginDestination
import com.ramcosta.composedestinations.generated.destinations.OutOfTimeDestination
import com.ramcosta.composedestinations.generated.destinations.PrivacyDisclaimerDestination
+import com.ramcosta.composedestinations.generated.login.destinations.LoginDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.common.compose.CollectSideEffectWithLifecycle
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/transitions/HomeTransition.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/transitions/HomeTransition.kt
index ab837f091d..91388630c3 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/transitions/HomeTransition.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/transitions/HomeTransition.kt
@@ -5,7 +5,7 @@ import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.fadeIn
import androidx.navigation.NavBackStackEntry
-import com.ramcosta.composedestinations.generated.destinations.LoginDestination
+import com.ramcosta.composedestinations.generated.login.destinations.LoginDestination
import com.ramcosta.composedestinations.spec.DestinationStyle
import com.ramcosta.composedestinations.utils.destination
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt
index 32855e88f6..e8f66b9518 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt
@@ -16,6 +16,12 @@ import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointOverride
import net.mullvad.mullvadvpn.lib.grpc.ManagementService
import net.mullvad.mullvadvpn.lib.model.BuildVersion
import net.mullvad.mullvadvpn.lib.model.NotificationChannel
+import net.mullvad.mullvadvpn.lib.pushnotification.NotificationChannelFactory
+import net.mullvad.mullvadvpn.lib.pushnotification.NotificationManager
+import net.mullvad.mullvadvpn.lib.pushnotification.NotificationProvider
+import net.mullvad.mullvadvpn.lib.pushnotification.ScheduleNotificationAlarmUseCase
+import net.mullvad.mullvadvpn.lib.pushnotification.accountexpiry.AccountExpiryNotificationProvider
+import net.mullvad.mullvadvpn.lib.pushnotification.tunnelstate.TunnelStateNotificationProvider
import net.mullvad.mullvadvpn.lib.repository.AccountRepository
import net.mullvad.mullvadvpn.lib.repository.AppObfuscationRepository
import net.mullvad.mullvadvpn.lib.repository.ConnectionProxy
@@ -27,12 +33,6 @@ import net.mullvad.mullvadvpn.lib.repository.UserPreferencesRepository
import net.mullvad.mullvadvpn.lib.repository.UserPreferencesSerializer
import net.mullvad.mullvadvpn.lib.usecase.AccountExpiryNotificationActionUseCase
import net.mullvad.mullvadvpn.repository.UserPreferences
-import net.mullvad.mullvadvpn.service.notifications.NotificationChannelFactory
-import net.mullvad.mullvadvpn.service.notifications.NotificationManager
-import net.mullvad.mullvadvpn.service.notifications.NotificationProvider
-import net.mullvad.mullvadvpn.service.notifications.accountexpiry.AccountExpiryNotificationProvider
-import net.mullvad.mullvadvpn.service.notifications.tunnelstate.TunnelStateNotificationProvider
-import net.mullvad.mullvadvpn.usecase.ScheduleNotificationAlarmUseCase
import org.koin.android.ext.koin.androidContext
import org.koin.core.module.dsl.createdAtStart
import org.koin.core.module.dsl.withOptions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
index 2af820d2a8..81e5e1366c 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
@@ -28,6 +28,9 @@ import net.mullvad.mullvadvpn.feature.account.impl.AccountViewModel
import net.mullvad.mullvadvpn.feature.addtime.impl.AddTimeViewModel
import net.mullvad.mullvadvpn.feature.autoconnect.impl.AutoConnectAndLockdownModeViewModel
import net.mullvad.mullvadvpn.feature.daita.impl.DaitaViewModel
+import net.mullvad.mullvadvpn.feature.login.impl.LoginViewModel
+import net.mullvad.mullvadvpn.feature.login.impl.apiunreachable.ApiUnreachableViewModel
+import net.mullvad.mullvadvpn.feature.login.impl.devicelist.DeviceListViewModel
import net.mullvad.mullvadvpn.feature.managedevices.impl.ManageDevicesViewModel
import net.mullvad.mullvadvpn.feature.notification.impl.NotificationSettingsViewModel
import net.mullvad.mullvadvpn.feature.problemreport.impl.ReportProblemViewModel
@@ -100,11 +103,8 @@ import net.mullvad.mullvadvpn.serveripoverride.impl.reset.ResetServerIpOverrides
import net.mullvad.mullvadvpn.ui.MainActivity
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
import net.mullvad.mullvadvpn.util.BackstackObserver
-import net.mullvad.mullvadvpn.viewmodel.ApiUnreachableViewModel
import net.mullvad.mullvadvpn.viewmodel.ConnectViewModel
-import net.mullvad.mullvadvpn.viewmodel.DeviceListViewModel
import net.mullvad.mullvadvpn.viewmodel.DeviceRevokedViewModel
-import net.mullvad.mullvadvpn.viewmodel.LoginViewModel
import net.mullvad.mullvadvpn.viewmodel.MullvadAppViewModel
import net.mullvad.mullvadvpn.viewmodel.OutOfTimeViewModel
import net.mullvad.mullvadvpn.viewmodel.PrivacyDisclaimerViewModel
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/receiver/ScheduleNotificationBootCompletedReceiver.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/receiver/ScheduleNotificationBootCompletedReceiver.kt
index f5a37c86e5..dd745c2ebb 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/receiver/ScheduleNotificationBootCompletedReceiver.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/receiver/ScheduleNotificationBootCompletedReceiver.kt
@@ -4,8 +4,8 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import co.touchlab.kermit.Logger
+import net.mullvad.mullvadvpn.lib.pushnotification.ScheduleNotificationAlarmUseCase
import net.mullvad.mullvadvpn.lib.repository.UserPreferencesRepository
-import net.mullvad.mullvadvpn.usecase.ScheduleNotificationAlarmUseCase
import net.mullvad.mullvadvpn.util.goAsync
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt
index a8e054e00a..ca6f4f9d84 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt
@@ -7,6 +7,7 @@ import android.content.pm.ServiceInfo
import android.os.Build
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
+import net.mullvad.mullvadvpn.lib.common.serviceconnection.EmptyServiceConnection
import net.mullvad.mullvadvpn.service.MullvadVpnService
class ServiceConnectionManager(private val context: Context) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModel.kt
index 14a8de9f6b..48e4322ce9 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModel.kt
@@ -12,10 +12,10 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.compose.state.DeviceRevokedUiState
import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT
+import net.mullvad.mullvadvpn.lib.pushnotification.ScheduleNotificationAlarmUseCase
+import net.mullvad.mullvadvpn.lib.pushnotification.accountexpiry.AccountExpiryNotificationProvider
import net.mullvad.mullvadvpn.lib.repository.AccountRepository
import net.mullvad.mullvadvpn.lib.repository.ConnectionProxy
-import net.mullvad.mullvadvpn.service.notifications.accountexpiry.AccountExpiryNotificationProvider
-import net.mullvad.mullvadvpn.usecase.ScheduleNotificationAlarmUseCase
class DeviceRevokedViewModel(
private val accountRepository: AccountRepository,
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt
index c71a6efa4b..a56bdb8121 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt
@@ -16,10 +16,10 @@ import kotlinx.coroutines.test.runTest
import net.mullvad.mullvadvpn.compose.state.DeviceRevokedUiState
import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.model.TunnelState
+import net.mullvad.mullvadvpn.lib.pushnotification.ScheduleNotificationAlarmUseCase
+import net.mullvad.mullvadvpn.lib.pushnotification.accountexpiry.AccountExpiryNotificationProvider
import net.mullvad.mullvadvpn.lib.repository.AccountRepository
import net.mullvad.mullvadvpn.lib.repository.ConnectionProxy
-import net.mullvad.mullvadvpn.service.notifications.accountexpiry.AccountExpiryNotificationProvider
-import net.mullvad.mullvadvpn.usecase.ScheduleNotificationAlarmUseCase
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/Keyboard.kt b/android/lib/common-compose/src/main/kotlin/net/mullvad/mullvadvpn/common/compose/Keyboard.kt
index ce2e9206cc..85830e6497 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/Keyboard.kt
+++ b/android/lib/common-compose/src/main/kotlin/net/mullvad/mullvadvpn/common/compose/Keyboard.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.compose.util
+package net.mullvad.mullvadvpn.common.compose
import android.content.Context
import androidx.compose.ui.text.input.KeyboardType
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/EmptyServiceConnection.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/serviceconnection/EmptyServiceConnection.kt
index 28819f7aa0..b83bfea2e5 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/EmptyServiceConnection.kt
+++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/serviceconnection/EmptyServiceConnection.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.ui.serviceconnection
+package net.mullvad.mullvadvpn.lib.common.serviceconnection
import android.content.ComponentName
import android.content.ServiceConnection
diff --git a/android/lib/feature/account/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreenTest.kt b/android/lib/feature/account/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreenTest.kt
index 41d30ea143..dccdd7269b 100644
--- a/android/lib/feature/account/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreenTest.kt
+++ b/android/lib/feature/account/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreenTest.kt
@@ -26,8 +26,7 @@ import org.koin.dsl.module
@ExperimentalTestApi
@OptIn(ExperimentalMaterial3Api::class)
class AccountScreenTest {
- @JvmField @RegisterExtension
- val composeExtension = createEdgeToEdgeComposeExtension()
+ @JvmField @RegisterExtension val composeExtension = createEdgeToEdgeComposeExtension()
private val addTimeViewModel: AddTimeViewModel = mockk(relaxed = true)
@@ -36,7 +35,7 @@ class AccountScreenTest {
MockKAnnotations.init(this)
loadKoinModules(module { viewModel { addTimeViewModel } })
every { addTimeViewModel.uiState } returns
- MutableStateFlow<Lc<Unit, AddTimeUiState>>(Lc.Loading(Unit))
+ MutableStateFlow<Lc<Unit, AddTimeUiState>>(Lc.Loading(Unit))
}
private fun ComposeContext.initScreen(
diff --git a/android/lib/feature/account/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountViewModelTest.kt b/android/lib/feature/account/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountViewModelTest.kt
index 6c8e1d9d6c..c94d097e9d 100644
--- a/android/lib/feature/account/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountViewModelTest.kt
+++ b/android/lib/feature/account/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountViewModelTest.kt
@@ -9,6 +9,9 @@ import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.unmockkAll
+import java.time.ZonedDateTime
+import kotlin.test.assertEquals
+import kotlin.test.assertIs
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import net.mullvad.mullvadvpn.lib.common.Lc
@@ -29,9 +32,6 @@ import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
-import java.time.ZonedDateTime
-import kotlin.test.assertEquals
-import kotlin.test.assertIs
@ExtendWith(TestCoroutineRule::class)
class AccountViewModelTest {
diff --git a/android/lib/feature/login/impl/build.gradle.kts b/android/lib/feature/login/impl/build.gradle.kts
new file mode 100644
index 0000000000..96ccad3540
--- /dev/null
+++ b/android/lib/feature/login/impl/build.gradle.kts
@@ -0,0 +1,29 @@
+plugins {
+ alias(libs.plugins.mullvad.android.library)
+ alias(libs.plugins.mullvad.android.library.feature.impl)
+ alias(libs.plugins.mullvad.android.library.compose)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.parcelize)
+ alias(libs.plugins.kotlin.ksp)
+}
+
+android {
+ namespace = "net.mullvad.mullvadvpn.feature.login.impl"
+ ksp { arg("compose-destinations.moduleName", "login") }
+}
+
+dependencies {
+ implementation(projects.lib.pushNotification)
+ implementation(projects.lib.repository)
+ implementation(projects.lib.usecase)
+ implementation(projects.lib.feature.managedevices.impl)
+ implementation(projects.lib.feature.problemreport.impl)
+ implementation(projects.lib.feature.settings.impl)
+
+ implementation(libs.koin.compose)
+ implementation(libs.arrow)
+
+ // Destinations
+ implementation(libs.compose.destinations)
+ ksp(libs.compose.destinations.ksp)
+}
diff --git a/android/lib/feature/login/impl/src/main/AndroidManifest.xml b/android/lib/feature/login/impl/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..8bdb7e14b3
--- /dev/null
+++ b/android/lib/feature/login/impl/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+</manifest>
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateAccountConfirmationDialog.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/CreateAccountConfirmationDialog.kt
index 9ef731ee5d..4cc5ecd460 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateAccountConfirmationDialog.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/CreateAccountConfirmationDialog.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.compose.dialog
+package net.mullvad.mullvadvpn.feature.login.impl
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -10,11 +10,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.ramcosta.composedestinations.annotation.Destination
+import com.ramcosta.composedestinations.annotation.ExternalModuleGraph
import com.ramcosta.composedestinations.result.EmptyResultBackNavigator
import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.spec.DestinationStyle
-import net.mullvad.mullvadvpn.R
-import net.mullvad.mullvadvpn.compose.screen.MainGraph
import net.mullvad.mullvadvpn.lib.ui.component.dialog.Confirmed
import net.mullvad.mullvadvpn.lib.ui.component.dialog.InfoConfirmationDialog
import net.mullvad.mullvadvpn.lib.ui.component.dialog.InfoConfirmationDialogTitleType
@@ -28,7 +27,7 @@ private fun PreviewCreateAccountConfirmationDialog() {
}
@Composable
-@Destination<MainGraph>(style = DestinationStyle.Dialog::class)
+@Destination<ExternalModuleGraph>(style = DestinationStyle.Dialog::class)
fun CreateAccountConfirmation(navigator: ResultBackNavigator<Confirmed>) {
InfoConfirmationDialog(
onResult = {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/LoginScreen.kt
index 31654af42d..690176e1d0 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/LoginScreen.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.compose.screen
+package net.mullvad.mullvadvpn.feature.login.impl
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
@@ -63,49 +63,37 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.compose.dropUnlessResumed
import com.ramcosta.composedestinations.annotation.Destination
-import com.ramcosta.composedestinations.generated.NavGraphs
-import com.ramcosta.composedestinations.generated.destinations.ApiUnreachableInfoDestination
-import com.ramcosta.composedestinations.generated.destinations.ConnectDestination
-import com.ramcosta.composedestinations.generated.destinations.CreateAccountConfirmationDestination
-import com.ramcosta.composedestinations.generated.destinations.DeviceListDestination
-import com.ramcosta.composedestinations.generated.destinations.OutOfTimeDestination
-import com.ramcosta.composedestinations.generated.destinations.WelcomeDestination
+import com.ramcosta.composedestinations.annotation.ExternalModuleGraph
+import com.ramcosta.composedestinations.generated.login.destinations.ApiUnreachableInfoDestination
+import com.ramcosta.composedestinations.generated.login.destinations.CreateAccountConfirmationDestination
+import com.ramcosta.composedestinations.generated.login.destinations.DeviceListDestination
import com.ramcosta.composedestinations.generated.settings.destinations.SettingsDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.result.ResultRecipient
import kotlinx.coroutines.launch
-import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.common.compose.CollectSideEffectWithLifecycle
+import net.mullvad.mullvadvpn.common.compose.accountNumberKeyboardType
import net.mullvad.mullvadvpn.common.compose.accountNumberVisualTransformation
import net.mullvad.mullvadvpn.common.compose.clickableAnnotatedString
import net.mullvad.mullvadvpn.common.compose.dropUnlessResumed
import net.mullvad.mullvadvpn.common.compose.showSnackbarImmediately
-import net.mullvad.mullvadvpn.compose.dialog.info.ApiUnreachableInfoDialogNavArgs
-import net.mullvad.mullvadvpn.compose.dialog.info.ApiUnreachableInfoDialogResult
-import net.mullvad.mullvadvpn.compose.dialog.info.LoginAction
-import net.mullvad.mullvadvpn.compose.preview.LoginUiStatePreviewParameterProvider
-import net.mullvad.mullvadvpn.compose.state.LoginState
-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.compose.state.LoginUiStateError
-import net.mullvad.mullvadvpn.compose.transitions.LoginTransition
-import net.mullvad.mullvadvpn.compose.util.OnNavResultValue
-import net.mullvad.mullvadvpn.compose.util.accountNumberKeyboardType
+import net.mullvad.mullvadvpn.core.OnNavResultValue
+import net.mullvad.mullvadvpn.core.animation.LoginTransition
+import net.mullvad.mullvadvpn.feature.login.impl.apiunreachable.ApiUnreachableInfoDialogNavArgs
+import net.mullvad.mullvadvpn.feature.login.impl.apiunreachable.ApiUnreachableInfoDialogResult
+import net.mullvad.mullvadvpn.feature.login.impl.apiunreachable.LoginAction
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithTopBar
import net.mullvad.mullvadvpn.lib.ui.component.dialog.Confirmed
import net.mullvad.mullvadvpn.lib.ui.component.textfield.mullvadWhiteTextFieldColors
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryButton
import net.mullvad.mullvadvpn.lib.ui.designsystem.VariantButton
+import net.mullvad.mullvadvpn.lib.ui.resource.R
import net.mullvad.mullvadvpn.lib.ui.tag.LOGIN_INPUT_TEST_TAG
import net.mullvad.mullvadvpn.lib.ui.tag.LOGIN_SCREEN_DELETE_ACCOUNT_HISTORY_TEST_TAG
import net.mullvad.mullvadvpn.lib.ui.tag.LOGIN_TITLE_TEST_TAG
import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme
import net.mullvad.mullvadvpn.lib.ui.theme.Dimens
-import net.mullvad.mullvadvpn.viewmodel.LoginUiSideEffect
-import net.mullvad.mullvadvpn.viewmodel.LoginViewModel
import org.koin.androidx.compose.koinViewModel
@Preview("Default|Loading.LoggingIn|Loading.CreatingAccount|LoginError|Success")
@@ -130,7 +118,7 @@ private fun PreviewLoginScreen(
private const val TOP_SPACER_WEIGHT = 1f
private const val BOTTOM_SPACER_WEIGHT = 3f
-@Destination<MainGraph>(style = LoginTransition::class)
+@Destination<ExternalModuleGraph>(style = LoginTransition::class)
@Composable
fun Login(
navigator: DestinationsNavigator,
@@ -176,25 +164,28 @@ fun Login(
CollectSideEffectWithLifecycle(vm.uiSideEffect) {
when (it) {
- LoginUiSideEffect.NavigateToWelcome ->
- navigator.navigate(WelcomeDestination) {
- launchSingleTop = true
- popUpTo(NavGraphs.main) { inclusive = true }
- }
- is LoginUiSideEffect.NavigateToConnect ->
- navigator.navigate(ConnectDestination) {
- launchSingleTop = true
- popUpTo(NavGraphs.main) { inclusive = true }
- }
+ LoginUiSideEffect.NavigateToWelcome -> {}
+ // TODO How to solve this?
+ // navigator.navigate(WelcomeDestination) {
+ // launchSingleTop = true
+ // popUpTo(NavGraphs.main) { inclusive = true }
+ // }
+ is LoginUiSideEffect.NavigateToConnect -> {}
+ // TODO How to solve this?
+ // navigator.navigate(ConnectDestination) {
+ // launchSingleTop = true
+ // popUpTo(NavGraphs.main) { inclusive = true }
+ // }
is LoginUiSideEffect.TooManyDevices ->
navigator.navigate(DeviceListDestination(it.accountNumber)) {
launchSingleTop = true
}
- LoginUiSideEffect.NavigateToOutOfTime ->
- navigator.navigate(OutOfTimeDestination) {
- launchSingleTop = true
- popUpTo(NavGraphs.main) { inclusive = true }
- }
+ LoginUiSideEffect.NavigateToOutOfTime -> {}
+ // TODO How to solve this?
+ // navigator.navigate(OutOfTimeDestination) {
+ // launchSingleTop = true
+ // popUpTo(NavGraphs.main) { inclusive = true }
+ // }
LoginUiSideEffect.NavigateToCreateAccountConfirmation ->
navigator.navigate(CreateAccountConfirmationDestination)
LoginUiSideEffect.GenericError ->
@@ -234,7 +225,7 @@ private fun LoginScreen(
topBarColor = MaterialTheme.colorScheme.primary,
iconTintColor = MaterialTheme.colorScheme.onPrimary,
onSettingsClicked = onSettingsClick,
- enabled = state.loginState is Idle,
+ enabled = state.loginState is LoginState.Idle,
onAccountClicked = null,
) {
val scrollState = rememberScrollState()
@@ -260,7 +251,10 @@ private fun LoginScreen(
onShowApiUnreachableDialog,
)
Spacer(modifier = Modifier.weight(BOTTOM_SPACER_WEIGHT))
- CreateAccountPanel(onCreateAccountClick, isEnabled = state.loginState is Idle)
+ CreateAccountPanel(
+ onCreateAccountClick,
+ isEnabled = state.loginState is LoginState.Idle,
+ )
}
}
}
@@ -346,19 +340,22 @@ private fun ColumnScope.LoginInput(
keyboardOptions =
KeyboardOptions(
imeAction = if (state.loginButtonEnabled) ImeAction.Done else ImeAction.None,
- keyboardType = KeyboardType.accountNumberKeyboardType(LocalContext.current),
+ keyboardType =
+ KeyboardType.Companion.accountNumberKeyboardType(LocalContext.current),
),
onValueChange = onAccountNumberChange,
singleLine = true,
maxLines = 1,
visualTransformation = accountNumberVisualTransformation(),
- enabled = state.loginState is Idle,
+ enabled = state.loginState is LoginState.Idle,
colors = mullvadWhiteTextFieldColors(),
textStyle = MaterialTheme.typography.bodyLarge.copy(textDirection = TextDirection.Ltr),
isError = state.loginState.isError(),
)
- AnimatedVisibility(visible = state.lastUsedAccount != null && state.loginState is Idle) {
+ AnimatedVisibility(
+ visible = state.lastUsedAccount != null && state.loginState is LoginState.Idle
+ ) {
val token = state.lastUsedAccount?.value.orEmpty()
val accountTransformation = remember { accountNumberVisualTransformation() }
val transformedText =
@@ -374,7 +371,7 @@ private fun ColumnScope.LoginInput(
onLoginClick(it.value)
}
},
- enabled = state.loginState is Idle,
+ enabled = state.loginState is LoginState.Idle,
onDeleteClick = onDeleteHistoryClick,
)
}
@@ -385,7 +382,7 @@ private fun ColumnScope.LoginInput(
private fun LoginIcon(loginState: LoginState, modifier: Modifier = Modifier) {
Box(contentAlignment = Alignment.Center, modifier = modifier) {
when (loginState) {
- is Idle ->
+ is LoginState.Idle ->
if (loginState.loginUiStateError != null) {
Image(
painter = painterResource(id = R.drawable.icon_fail),
@@ -394,8 +391,8 @@ private fun LoginIcon(loginState: LoginState, modifier: Modifier = Modifier) {
} else {
// If view is Idle, we display empty box to keep the same size as other states
}
- is Loading -> MullvadCircularProgressIndicatorLarge()
- Success ->
+ is LoginState.Loading -> MullvadCircularProgressIndicatorLarge()
+ LoginState.Success ->
Image(
painter = painterResource(id = R.drawable.icon_success),
contentDescription = stringResource(id = R.string.logged_in_title),
@@ -409,15 +406,15 @@ private fun LoginState.title(): String =
stringResource(
id =
when (this) {
- is Idle ->
+ is LoginState.Idle ->
when (this.loginUiStateError) {
is LoginUiStateError.LoginError -> R.string.login_fail_title
is LoginUiStateError.CreateAccountError ->
R.string.create_account_fail_title
null -> R.string.log_in
}
- is Loading -> R.string.logging_in_title
- Success -> R.string.logged_in_title
+ is LoginState.Loading -> R.string.logging_in_title
+ LoginState.Success -> R.string.logged_in_title
}
)
@@ -446,11 +443,11 @@ private fun LoginState.supportingText(
onShowApiUnreachableDialog: (LoginUiStateError) -> Unit
): AnnotatedString? =
when (this) {
- is Idle if
+ is LoginState.Idle if
loginUiStateError is LoginUiStateError.LoginError.ApiUnreachable ||
loginUiStateError is LoginUiStateError.CreateAccountError.ApiUnreachable
-> apiUnreachableText(loginUiStateError, onShowApiUnreachableDialog)
- is Idle -> {
+ is LoginState.Idle -> {
when (loginUiStateError) {
LoginUiStateError.LoginError.InvalidCredentials -> R.string.login_fail_description
is LoginUiStateError.LoginError.InvalidInput -> R.string.login_error_invalid_input
@@ -467,9 +464,9 @@ private fun LoginState.supportingText(
null -> null
}?.toAnnotatedString()
}
- is Loading.CreatingAccount -> R.string.creating_new_account.toAnnotatedString()
- is Loading.LoggingIn -> R.string.logging_in_description.toAnnotatedString()
- Success -> R.string.logged_in_description.toAnnotatedString()
+ is LoginState.Loading.CreatingAccount -> R.string.creating_new_account.toAnnotatedString()
+ is LoginState.Loading.LoggingIn -> R.string.logging_in_description.toAnnotatedString()
+ LoginState.Success -> R.string.logged_in_description.toAnnotatedString()
}
@Composable
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/LoginUiState.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/LoginUiState.kt
index 2dbde070f4..7bc16301a6 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/LoginUiState.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/LoginUiState.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.compose.state
+package net.mullvad.mullvadvpn.feature.login.impl
import net.mullvad.mullvadvpn.lib.model.AccountNumber
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/LoginUiStatePreviewParameterProvider.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/LoginUiStatePreviewParameterProvider.kt
index a0e8ebbee4..9936772c3e 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/LoginUiStatePreviewParameterProvider.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/LoginUiStatePreviewParameterProvider.kt
@@ -1,9 +1,6 @@
-package net.mullvad.mullvadvpn.compose.preview
+package net.mullvad.mullvadvpn.feature.login.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
-import net.mullvad.mullvadvpn.compose.state.LoginState
-import net.mullvad.mullvadvpn.compose.state.LoginUiState
-import net.mullvad.mullvadvpn.compose.state.LoginUiStateError
class LoginUiStatePreviewParameterProvider : PreviewParameterProvider<LoginUiState> {
override val values: Sequence<LoginUiState>
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/LoginViewModel.kt
index 7d8149c02e..6152058478 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/LoginViewModel.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.viewmodel
+package net.mullvad.mullvadvpn.feature.login.impl
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
@@ -20,12 +20,8 @@ import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
-import net.mullvad.mullvadvpn.compose.state.LoginState
-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.compose.state.LoginUiStateError
+import net.mullvad.mullvadvpn.feature.login.impl.LoginUiSideEffect.NavigateToWelcome
+import net.mullvad.mullvadvpn.feature.login.impl.LoginUiSideEffect.TooManyDevices
import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT
import net.mullvad.mullvadvpn.lib.common.util.delayAtLeast
import net.mullvad.mullvadvpn.lib.common.util.getOrDefault
@@ -33,13 +29,11 @@ import net.mullvad.mullvadvpn.lib.common.util.isBeforeNowInstant
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.model.CreateAccountError
import net.mullvad.mullvadvpn.lib.model.LoginAccountError
+import net.mullvad.mullvadvpn.lib.pushnotification.ScheduleNotificationAlarmUseCase
+import net.mullvad.mullvadvpn.lib.pushnotification.accountexpiry.AccountExpiryNotificationProvider
import net.mullvad.mullvadvpn.lib.repository.AccountRepository
import net.mullvad.mullvadvpn.lib.repository.NewDeviceRepository
import net.mullvad.mullvadvpn.lib.usecase.InternetAvailableUseCase
-import net.mullvad.mullvadvpn.service.notifications.accountexpiry.AccountExpiryNotificationProvider
-import net.mullvad.mullvadvpn.usecase.ScheduleNotificationAlarmUseCase
-import net.mullvad.mullvadvpn.viewmodel.LoginUiSideEffect.NavigateToWelcome
-import net.mullvad.mullvadvpn.viewmodel.LoginUiSideEffect.TooManyDevices
private const val MINIMUM_LOADING_SPINNER_TIME_MILLIS = 500L
@@ -116,7 +110,7 @@ class LoginViewModel(
}
private fun createAccount() {
- _loginState.value = Loading.CreatingAccount
+ _loginState.value = LoginState.Loading.CreatingAccount
viewModelScope.launch(dispatcher) {
accountRepository
.createAccount()
@@ -128,7 +122,7 @@ class LoginViewModel(
}
fun login(accountNumber: String) {
- _loginState.value = Loading.LoggingIn
+ _loginState.value = LoginState.Loading.LoggingIn
viewModelScope.launch(dispatcher) {
val uiState =
// Ensure we always take at least MINIMUM_LOADING_SPINNER_TIME_MILLIS to show the
@@ -140,7 +134,7 @@ class LoginViewModel(
{ it.toUiState() },
{
onSuccessfulLogin()
- Success
+ LoginState.Success
},
)
@@ -176,27 +170,28 @@ class LoginViewModel(
fun onAccountNumberChange(accountNumber: String) {
_loginInput.value = accountNumber.filter { it.isDigit() }
// If there is an error, clear it
- _loginState.update { if (it is Idle) Idle() else it }
+ _loginState.update { if (it is LoginState.Idle) LoginState.Idle() else it }
}
private suspend fun LoginAccountError.toUiState(): LoginState =
when (this) {
LoginAccountError.InvalidAccount ->
- Idle(LoginUiStateError.LoginError.InvalidCredentials)
+ LoginState.Idle(LoginUiStateError.LoginError.InvalidCredentials)
is LoginAccountError.MaxDevicesReached ->
- Idle().also { _uiSideEffect.send(TooManyDevices(accountNumber)) }
+ LoginState.Idle().also { _uiSideEffect.send(TooManyDevices(accountNumber)) }
is LoginAccountError.InvalidInput ->
- Idle(LoginUiStateError.LoginError.InvalidInput(accountNumber))
+ LoginState.Idle(LoginUiStateError.LoginError.InvalidInput(accountNumber))
LoginAccountError.Timeout,
LoginAccountError.ApiUnreachable ->
if (isInternetAvailable()) {
- Idle(LoginUiStateError.LoginError.ApiUnreachable)
+ LoginState.Idle(LoginUiStateError.LoginError.ApiUnreachable)
} else {
- Idle(LoginUiStateError.LoginError.NoInternetConnection)
+ LoginState.Idle(LoginUiStateError.LoginError.NoInternetConnection)
}
- LoginAccountError.TooManyAttempts -> Idle(LoginUiStateError.LoginError.TooManyAttempts)
+ LoginAccountError.TooManyAttempts ->
+ LoginState.Idle(LoginUiStateError.LoginError.TooManyAttempts)
is LoginAccountError.Unknown ->
- Idle(LoginUiStateError.LoginError.Unknown(this.toString())).also {
+ LoginState.Idle(LoginUiStateError.LoginError.Unknown(this.toString())).also {
Logger.w("Login failed with error: $this", error)
}
}
@@ -206,14 +201,14 @@ class LoginViewModel(
CreateAccountError.ApiUnreachable,
CreateAccountError.TimeOut ->
if (isInternetAvailable()) {
- Idle(LoginUiStateError.CreateAccountError.ApiUnreachable)
+ LoginState.Idle(LoginUiStateError.CreateAccountError.ApiUnreachable)
} else {
- Idle(LoginUiStateError.CreateAccountError.NoInternetConnection)
+ LoginState.Idle(LoginUiStateError.CreateAccountError.NoInternetConnection)
}
CreateAccountError.TooManyAttempts ->
- Idle(LoginUiStateError.CreateAccountError.TooManyAttempts)
+ LoginState.Idle(LoginUiStateError.CreateAccountError.TooManyAttempts)
is CreateAccountError.Unknown ->
- Idle(LoginUiStateError.CreateAccountError.Unknown).also {
+ LoginState.Idle(LoginUiStateError.CreateAccountError.Unknown).also {
Logger.w("Create account failed with error: $this", error)
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/info/ApiUnreachableInfoDialog.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/apiunreachable/ApiUnreachableInfoDialog.kt
index 6fd6f4b527..1bb5ed19b9 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/info/ApiUnreachableInfoDialog.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/apiunreachable/ApiUnreachableInfoDialog.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.compose.dialog.info
+package net.mullvad.mullvadvpn.feature.login.impl.apiunreachable
import android.content.ActivityNotFoundException
import android.os.Parcelable
@@ -17,23 +17,18 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.touchlab.kermit.Logger
import com.ramcosta.composedestinations.annotation.Destination
+import com.ramcosta.composedestinations.annotation.ExternalModuleGraph
import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.spec.DestinationStyle
import kotlinx.parcelize.Parcelize
-import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.common.compose.CollectSideEffectWithLifecycle
-import net.mullvad.mullvadvpn.compose.screen.MainGraph
-import net.mullvad.mullvadvpn.compose.state.ApiUnreachableUiState
-import net.mullvad.mullvadvpn.compose.util.EmailData
-import net.mullvad.mullvadvpn.compose.util.SendEmail
import net.mullvad.mullvadvpn.feature.problemreport.impl.provider.createShareLogFile
import net.mullvad.mullvadvpn.lib.ui.component.dialog.InfoDialog
import net.mullvad.mullvadvpn.lib.ui.component.textfield.ErrorSupportingText
import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryButton
+import net.mullvad.mullvadvpn.lib.ui.resource.R
import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme
import net.mullvad.mullvadvpn.lib.ui.theme.Dimens
-import net.mullvad.mullvadvpn.viewmodel.ApiUnreachableSideEffect
-import net.mullvad.mullvadvpn.viewmodel.ApiUnreachableViewModel
import org.koin.androidx.compose.koinViewModel
@Preview
@@ -69,7 +64,7 @@ sealed interface ApiUnreachableInfoDialogResult : Parcelable {
@Parcelize data object Error : ApiUnreachableInfoDialogResult
}
-@Destination<MainGraph>(
+@Destination<ExternalModuleGraph>(
style = DestinationStyle.Dialog::class,
navArgs = ApiUnreachableInfoDialogNavArgs::class,
)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ApiUnreachableUiState.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/apiunreachable/ApiUnreachableUiState.kt
index 2a4f407934..304552ee66 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ApiUnreachableUiState.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/apiunreachable/ApiUnreachableUiState.kt
@@ -1,6 +1,4 @@
-package net.mullvad.mullvadvpn.compose.state
-
-import net.mullvad.mullvadvpn.compose.dialog.info.LoginAction
+package net.mullvad.mullvadvpn.feature.login.impl.apiunreachable
data class ApiUnreachableUiState(
val showEnableAllAccessMethodsButton: Boolean,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiUnreachableViewModel.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/apiunreachable/ApiUnreachableViewModel.kt
index 6d5b1c2d85..a3dd4b1bf4 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiUnreachableViewModel.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/apiunreachable/ApiUnreachableViewModel.kt
@@ -1,9 +1,9 @@
-package net.mullvad.mullvadvpn.viewmodel
+package net.mullvad.mullvadvpn.feature.login.impl.apiunreachable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.ramcosta.composedestinations.generated.destinations.ApiUnreachableInfoDestination
+import com.ramcosta.composedestinations.generated.login.destinations.ApiUnreachableInfoDestination
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -14,8 +14,6 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
-import net.mullvad.mullvadvpn.compose.dialog.info.ApiUnreachableInfoDialogNavArgs
-import net.mullvad.mullvadvpn.compose.state.ApiUnreachableUiState
import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT
import net.mullvad.mullvadvpn.lib.repository.ApiAccessRepository
import net.mullvad.mullvadvpn.lib.ui.component.NEWLINE_STRING
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/SendEmail.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/apiunreachable/SendEmail.kt
index 5d02ad555f..b595c3234a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/SendEmail.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/apiunreachable/SendEmail.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.compose.util
+package net.mullvad.mullvadvpn.feature.login.impl.apiunreachable
import android.content.Context
import android.content.Intent
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DeviceListScreen.kt
index 6b5eb9005b..f8cd65a73b 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DeviceListScreen.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.compose.screen
+package net.mullvad.mullvadvpn.feature.login.impl.devicelist
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image
@@ -29,19 +29,17 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.compose.dropUnlessResumed
import com.ramcosta.composedestinations.annotation.Destination
-import com.ramcosta.composedestinations.generated.destinations.LoginDestination
-import com.ramcosta.composedestinations.generated.destinations.RemoveDeviceConfirmationDestination
+import com.ramcosta.composedestinations.annotation.ExternalModuleGraph
+import com.ramcosta.composedestinations.generated.login.destinations.LoginDestination
+import com.ramcosta.composedestinations.generated.login.destinations.RemoveDeviceConfirmationDestination
import com.ramcosta.composedestinations.generated.settings.destinations.SettingsDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.result.NavResult
import com.ramcosta.composedestinations.result.ResultRecipient
import kotlinx.coroutines.launch
-import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.common.compose.CollectSideEffectWithLifecycle
import net.mullvad.mullvadvpn.common.compose.dropUnlessResumed
import net.mullvad.mullvadvpn.common.compose.showSnackbarImmediately
-import net.mullvad.mullvadvpn.compose.preview.DeviceListUiStatePreviewParameterProvider
-import net.mullvad.mullvadvpn.compose.state.DeviceListUiState
import net.mullvad.mullvadvpn.core.animation.DefaultTransition
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.model.Device
@@ -53,11 +51,10 @@ import net.mullvad.mullvadvpn.lib.ui.component.positionForIndex
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryButton
import net.mullvad.mullvadvpn.lib.ui.designsystem.VariantButton
+import net.mullvad.mullvadvpn.lib.ui.resource.R
import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme
import net.mullvad.mullvadvpn.lib.ui.theme.Dimens
import net.mullvad.mullvadvpn.lib.ui.theme.color.selected
-import net.mullvad.mullvadvpn.viewmodel.DeviceListSideEffect
-import net.mullvad.mullvadvpn.viewmodel.DeviceListViewModel
import org.koin.androidx.compose.koinViewModel
@Composable
@@ -80,7 +77,10 @@ private fun PreviewDeviceListScreenContent(
data class DeviceListNavArgs(val accountNumber: AccountNumber)
-@Destination<MainGraph>(style = DefaultTransition::class, navArgs = DeviceListNavArgs::class)
+@Destination<ExternalModuleGraph>(
+ style = DefaultTransition::class,
+ navArgs = DeviceListNavArgs::class,
+)
@Composable
fun DeviceList(
navigator: DestinationsNavigator,
@@ -207,7 +207,7 @@ private fun ColumnScope.DeviceListError(tryAgain: () -> Unit) {
}
@Composable
-private fun ColumnScope.DeviceListContent(
+private fun DeviceListContent(
state: DeviceListUiState.Content,
navigateToRemoveDeviceConfirmationDialog: (Device) -> Unit,
) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeviceListUiState.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DeviceListUiState.kt
index adc5e3d07d..5da1312a44 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeviceListUiState.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DeviceListUiState.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.compose.state
+package net.mullvad.mullvadvpn.feature.login.impl.devicelist
import net.mullvad.mullvadvpn.lib.model.Device
import net.mullvad.mullvadvpn.lib.model.GetDeviceListError
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DeviceListUiStatePreviewParameterProvider.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DeviceListUiStatePreviewParameterProvider.kt
index 53102efb21..3d846bc7d0 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DeviceListUiStatePreviewParameterProvider.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DeviceListUiStatePreviewParameterProvider.kt
@@ -1,8 +1,7 @@
-package net.mullvad.mullvadvpn.compose.preview
+package net.mullvad.mullvadvpn.feature.login.impl.devicelist
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
-import net.mullvad.mullvadvpn.compose.preview.DevicePreviewData.generateDevices
-import net.mullvad.mullvadvpn.compose.state.DeviceListUiState
+import net.mullvad.mullvadvpn.feature.login.impl.devicelist.DevicePreviewData.generateDevices
import net.mullvad.mullvadvpn.lib.model.GetDeviceListError
class DeviceListUiStatePreviewParameterProvider : PreviewParameterProvider<DeviceListUiState> {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DeviceListViewModel.kt
index a1178cc2b8..341fe1c844 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DeviceListViewModel.kt
@@ -1,9 +1,9 @@
-package net.mullvad.mullvadvpn.viewmodel
+package net.mullvad.mullvadvpn.feature.login.impl.devicelist
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.ramcosta.composedestinations.generated.destinations.DeviceListDestination
+import com.ramcosta.composedestinations.generated.login.destinations.DeviceListDestination
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
@@ -18,8 +18,6 @@ import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
-import net.mullvad.mullvadvpn.compose.state.DeviceItemUiState
-import net.mullvad.mullvadvpn.compose.state.DeviceListUiState
import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.model.Device
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DevicePreviewData.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DevicePreviewData.kt
index 138706930f..edefed7687 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DevicePreviewData.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/DevicePreviewData.kt
@@ -1,8 +1,7 @@
-package net.mullvad.mullvadvpn.compose.preview
+package net.mullvad.mullvadvpn.feature.login.impl.devicelist
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
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RemoveDeviceConfirmationDialog.kt b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/RemoveDeviceConfirmationDialog.kt
index 0715eec43d..79aaab0c05 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RemoveDeviceConfirmationDialog.kt
+++ b/android/lib/feature/login/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/login/impl/devicelist/RemoveDeviceConfirmationDialog.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.compose.dialog
+package net.mullvad.mullvadvpn.feature.login.impl.devicelist
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -10,16 +10,16 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.core.text.HtmlCompat
import androidx.lifecycle.compose.dropUnlessResumed
import com.ramcosta.composedestinations.annotation.Destination
+import com.ramcosta.composedestinations.annotation.ExternalModuleGraph
import com.ramcosta.composedestinations.result.EmptyResultBackNavigator
import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.spec.DestinationStyle
-import net.mullvad.mullvadvpn.R
-import net.mullvad.mullvadvpn.compose.screen.MainGraph
import net.mullvad.mullvadvpn.feature.managedevices.impl.confirmation.ManageDeviceRemoveConfirmationPreviewParameterProvider
import net.mullvad.mullvadvpn.lib.model.Device
import net.mullvad.mullvadvpn.lib.model.DeviceId
import net.mullvad.mullvadvpn.lib.ui.component.dialog.NegativeConfirmationDialog
import net.mullvad.mullvadvpn.lib.ui.component.toAnnotatedString
+import net.mullvad.mullvadvpn.lib.ui.resource.R
import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme
@Preview
@@ -30,7 +30,7 @@ private fun PreviewRemoveDeviceConfirmationDialog(
AppTheme { RemoveDeviceConfirmation(EmptyResultBackNavigator(), device = device) }
}
-@Destination<MainGraph>(style = DestinationStyle.Dialog::class)
+@Destination<ExternalModuleGraph>(style = DestinationStyle.Dialog::class)
@Composable
fun RemoveDeviceConfirmation(navigator: ResultBackNavigator<DeviceId>, device: Device) {
val htmlFormattedString =
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModelTest.kt b/android/lib/feature/login/impl/src/test/java/net/mullvad/mullvadvpn/feature/login/impl/DeviceListViewModelTest.kt
index fc9e244faa..f6d5f2f192 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModelTest.kt
+++ b/android/lib/feature/login/impl/src/test/java/net/mullvad/mullvadvpn/feature/login/impl/DeviceListViewModelTest.kt
@@ -1,11 +1,11 @@
-package net.mullvad.mullvadvpn.viewmodel
+package net.mullvad.mullvadvpn.feature.login.impl
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import app.cash.turbine.test
import arrow.core.left
import arrow.core.right
-import com.ramcosta.composedestinations.generated.navargs.toSavedStateHandle
+import com.ramcosta.composedestinations.generated.login.navargs.toSavedStateHandle
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
@@ -18,8 +18,10 @@ import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
-import net.mullvad.mullvadvpn.compose.screen.DeviceListNavArgs
-import net.mullvad.mullvadvpn.compose.state.DeviceListUiState
+import net.mullvad.mullvadvpn.feature.login.impl.devicelist.DeviceListNavArgs
+import net.mullvad.mullvadvpn.feature.login.impl.devicelist.DeviceListSideEffect
+import net.mullvad.mullvadvpn.feature.login.impl.devicelist.DeviceListUiState
+import net.mullvad.mullvadvpn.feature.login.impl.devicelist.DeviceListViewModel
import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.model.DeleteDeviceError
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt b/android/lib/feature/login/impl/src/test/java/net/mullvad/mullvadvpn/feature/login/impl/LoginViewModelTest.kt
index 7a49b3289d..409ba1ea27 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt
+++ b/android/lib/feature/login/impl/src/test/java/net/mullvad/mullvadvpn/feature/login/impl/LoginViewModelTest.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.viewmodel
+package net.mullvad.mullvadvpn.feature.login.impl
import app.cash.turbine.ReceiveTurbine
import app.cash.turbine.test
@@ -17,21 +17,19 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
-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.compose.state.LoginUiStateError
-import net.mullvad.mullvadvpn.data.mock
+import net.mullvad.mullvadvpn.feature.login.impl.LoginState.Idle
+import net.mullvad.mullvadvpn.feature.login.impl.LoginState.Loading
+import net.mullvad.mullvadvpn.feature.login.impl.LoginState.Success
+import net.mullvad.mullvadvpn.feature.login.impl.data.mock
import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.model.AccountData
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.model.CreateAccountError
import net.mullvad.mullvadvpn.lib.model.LoginAccountError
+import net.mullvad.mullvadvpn.lib.pushnotification.ScheduleNotificationAlarmUseCase
+import net.mullvad.mullvadvpn.lib.pushnotification.accountexpiry.AccountExpiryNotificationProvider
import net.mullvad.mullvadvpn.lib.repository.AccountRepository
import net.mullvad.mullvadvpn.lib.usecase.InternetAvailableUseCase
-import net.mullvad.mullvadvpn.service.notifications.accountexpiry.AccountExpiryNotificationProvider
-import net.mullvad.mullvadvpn.usecase.ScheduleNotificationAlarmUseCase
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
diff --git a/android/lib/feature/login/impl/src/test/java/net/mullvad/mullvadvpn/feature/login/impl/data/AccountData.kt b/android/lib/feature/login/impl/src/test/java/net/mullvad/mullvadvpn/feature/login/impl/data/AccountData.kt
new file mode 100644
index 0000000000..d9e03e76ce
--- /dev/null
+++ b/android/lib/feature/login/impl/src/test/java/net/mullvad/mullvadvpn/feature/login/impl/data/AccountData.kt
@@ -0,0 +1,12 @@
+package net.mullvad.mullvadvpn.feature.login.impl.data
+
+import io.mockk.mockk
+import java.time.ZonedDateTime
+import net.mullvad.mullvadvpn.lib.model.AccountData
+
+fun AccountData.Companion.mock(expiry: ZonedDateTime): AccountData =
+ AccountData(
+ id = mockk(relaxed = true),
+ accountNumber = mockk(relaxed = true),
+ expiryDate = expiry,
+ )
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/transitions/LoginTransition.kt b/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/LoginTransition.kt
index 2c82d24d7c..b43594752a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/transitions/LoginTransition.kt
+++ b/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/LoginTransition.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.compose.transitions
+package net.mullvad.mullvadvpn.core.animation
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.EnterTransition
@@ -7,12 +7,7 @@ import androidx.compose.animation.core.spring
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.navigation.NavBackStackEntry
-import com.ramcosta.composedestinations.generated.destinations.ConnectDestination
-import com.ramcosta.composedestinations.generated.destinations.DeviceListDestination
-import com.ramcosta.composedestinations.generated.destinations.OutOfTimeDestination
-import com.ramcosta.composedestinations.generated.destinations.WelcomeDestination
import com.ramcosta.composedestinations.spec.DestinationStyle
-import com.ramcosta.composedestinations.utils.destination
object LoginTransition : DestinationStyle.Animated() {
override val enterTransition:
@@ -24,13 +19,15 @@ object LoginTransition : DestinationStyle.Animated() {
override val exitTransition:
AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition =
{
- when (this.targetState.destination()) {
- is OutOfTimeDestination,
- is WelcomeDestination,
- is ConnectDestination,
- is DeviceListDestination -> fadeOut(spring())
- else -> ExitTransition.None
- }
+ // TODO How to solve this?
+ fadeOut(spring())
+ // when (this.targetState.destination()) {
+ // is OutOfTimeDestination,
+ // is WelcomeDestination,
+ // is ConnectDestination,
+ // is DeviceListDestination -> fadeOut(spring())
+ // else -> ExitTransition.None
+ // }
}
override val popEnterTransition:
diff --git a/android/lib/push-notification/build.gradle.kts b/android/lib/push-notification/build.gradle.kts
new file mode 100644
index 0000000000..72f1751647
--- /dev/null
+++ b/android/lib/push-notification/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.mullvad.android.library)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.parcelize)
+}
+
+android { namespace = "net.mullvad.mullvadvpn.feature.pushnotifications" }
+
+dependencies {
+ implementation(projects.lib.common)
+ implementation(projects.lib.model)
+ implementation(projects.lib.repository)
+ implementation(projects.lib.ui.resource)
+
+ implementation(libs.androidx.ktx)
+ implementation(libs.androidx.lifecycle.service)
+ implementation(libs.androidx.work.runtime.ktx)
+ implementation(libs.arrow)
+ implementation(libs.kermit)
+ implementation(libs.koin.android)
+ implementation(libs.protobuf.kotlin.lite)
+}
diff --git a/android/lib/push-notification/src/main/AndroidManifest.xml b/android/lib/push-notification/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..8bdb7e14b3
--- /dev/null
+++ b/android/lib/push-notification/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+</manifest>
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannelFactory.kt b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/NotificationChannelFactory.kt
index 72539e8e94..c8ec728937 100644
--- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannelFactory.kt
+++ b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/NotificationChannelFactory.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.service.notifications
+package net.mullvad.mullvadvpn.lib.pushnotification
import android.app.NotificationManager
import android.content.res.Resources
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationManager.kt b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/NotificationManager.kt
index ed2d2f053a..6ca4f33007 100644
--- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationManager.kt
+++ b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/NotificationManager.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.service.notifications
+package net.mullvad.mullvadvpn.lib.pushnotification
import android.Manifest
import android.content.Context
@@ -13,15 +13,15 @@ import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.lib.model.Notification
import net.mullvad.mullvadvpn.lib.model.NotificationUpdate
-import net.mullvad.mullvadvpn.service.notifications.accountexpiry.toNotification
-import net.mullvad.mullvadvpn.service.notifications.tunnelstate.toNotification
+import net.mullvad.mullvadvpn.lib.pushnotification.accountexpiry.toNotification
+import net.mullvad.mullvadvpn.lib.pushnotification.tunnelstate.toNotification
@OptIn(FlowPreview::class)
class NotificationManager(
private val notificationManagerCompat: NotificationManagerCompat,
notificationProviders: List<NotificationProvider<Notification>>,
context: Context,
- val scope: CoroutineScope,
+ scope: CoroutineScope,
) {
init {
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationProvider.kt b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/NotificationProvider.kt
index ecdde13d7a..71d587e030 100644
--- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationProvider.kt
+++ b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/NotificationProvider.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.service.notifications
+package net.mullvad.mullvadvpn.lib.pushnotification
import kotlinx.coroutines.flow.Flow
import net.mullvad.mullvadvpn.lib.model.NotificationUpdate
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/ScheduleNotificationAlarmUseCase.kt b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/ScheduleNotificationAlarmUseCase.kt
index 1d178a15bf..5b40970a67 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/ScheduleNotificationAlarmUseCase.kt
+++ b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/ScheduleNotificationAlarmUseCase.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.usecase
+package net.mullvad.mullvadvpn.lib.pushnotification
import android.app.AlarmManager
import android.app.PendingIntent
@@ -8,8 +8,8 @@ import co.touchlab.kermit.Logger
import java.time.ZoneOffset
import java.time.ZonedDateTime
import net.mullvad.mullvadvpn.lib.common.util.accountExpiryNotificationTriggerAt
+import net.mullvad.mullvadvpn.lib.pushnotification.receiver.NotificationAlarmReceiver
import net.mullvad.mullvadvpn.lib.repository.UserPreferencesRepository
-import net.mullvad.mullvadvpn.receiver.NotificationAlarmReceiver
class ScheduleNotificationAlarmUseCase(
private val applicationContext: Context,
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ShouldBeOnForegroundProvider.kt b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/ShouldBeOnForegroundProvider.kt
index 1fdfb1ecb0..7caa49b3c8 100644
--- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ShouldBeOnForegroundProvider.kt
+++ b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/ShouldBeOnForegroundProvider.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.service.notifications
+package net.mullvad.mullvadvpn.lib.pushnotification
import kotlinx.coroutines.flow.Flow
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryAndroidNotification.kt b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/accountexpiry/AccountExpiryAndroidNotification.kt
index 97212797cd..c167a14870 100644
--- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryAndroidNotification.kt
+++ b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/accountexpiry/AccountExpiryAndroidNotification.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.service.notifications.accountexpiry
+package net.mullvad.mullvadvpn.lib.pushnotification.accountexpiry
import android.app.PendingIntent
import android.content.Context
@@ -9,7 +9,7 @@ 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.model.Notification
-import net.mullvad.mullvadvpn.service.R
+import net.mullvad.mullvadvpn.lib.ui.resource.R
internal fun Notification.AccountExpiry.toNotification(context: Context) =
NotificationCompat.Builder(context, channelId.value)
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryNotificationProvider.kt b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/accountexpiry/AccountExpiryNotificationProvider.kt
index 0be99896c9..2b72c0aac5 100644
--- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/accountexpiry/AccountExpiryNotificationProvider.kt
+++ b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/accountexpiry/AccountExpiryNotificationProvider.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.service.notifications.accountexpiry
+package net.mullvad.mullvadvpn.lib.pushnotification.accountexpiry
import co.touchlab.kermit.Logger
import java.time.Duration
@@ -9,7 +9,7 @@ import net.mullvad.mullvadvpn.lib.model.Notification
import net.mullvad.mullvadvpn.lib.model.NotificationChannelId
import net.mullvad.mullvadvpn.lib.model.NotificationId
import net.mullvad.mullvadvpn.lib.model.NotificationUpdate
-import net.mullvad.mullvadvpn.service.notifications.NotificationProvider
+import net.mullvad.mullvadvpn.lib.pushnotification.NotificationProvider
class AccountExpiryNotificationProvider(private val channelId: NotificationChannelId) :
NotificationProvider<Notification.AccountExpiry> {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/receiver/NotificationAlarmReceiver.kt b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/receiver/NotificationAlarmReceiver.kt
index 39f92f47f0..9692d555ed 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/receiver/NotificationAlarmReceiver.kt
+++ b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/receiver/NotificationAlarmReceiver.kt
@@ -1,15 +1,16 @@
-package net.mullvad.mullvadvpn.receiver
+package net.mullvad.mullvadvpn.lib.pushnotification.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
+import android.os.Build
import androidx.work.Constraints
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import co.touchlab.kermit.Logger
-import net.mullvad.mullvadvpn.worker.ExpiryNotificationWorker
+import net.mullvad.mullvadvpn.lib.pushnotification.worker.ExpiryNotificationWorker
import org.koin.core.component.KoinComponent
class NotificationAlarmReceiver : BroadcastReceiver(), KoinComponent {
@@ -17,7 +18,7 @@ class NotificationAlarmReceiver : BroadcastReceiver(), KoinComponent {
override fun onReceive(context: Context, intent: Intent?) {
// It is not possible to bind to a service from a notification alarm receiver so we will use
// a worker instead.
- Logger.d("Account expiry alarm triggered")
+ Logger.Companion.d("Account expiry alarm triggered")
val work =
OneTimeWorkRequestBuilder<ExpiryNotificationWorker>()
@@ -25,9 +26,7 @@ class NotificationAlarmReceiver : BroadcastReceiver(), KoinComponent {
// Setting expedited on android 12 or lower will cause the work manager to
// request a wake lock. We want to avoid using wakelocks so we disable expedited
// on android 12 and lower.
- if (
- android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU
- ) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
}
}
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationAction.kt b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/tunnelstate/TunnelStateNotificationAction.kt
index b799de0943..05eea1c666 100644
--- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationAction.kt
+++ b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/tunnelstate/TunnelStateNotificationAction.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.service.notifications.tunnelstate
+package net.mullvad.mullvadvpn.lib.pushnotification.tunnelstate
import android.app.PendingIntent
import android.content.Context
@@ -13,9 +13,9 @@ import net.mullvad.mullvadvpn.lib.model.Notification
import net.mullvad.mullvadvpn.lib.model.NotificationAction
import net.mullvad.mullvadvpn.lib.model.NotificationTunnelState
import net.mullvad.mullvadvpn.lib.model.PrepareError
-import net.mullvad.mullvadvpn.service.R
+import net.mullvad.mullvadvpn.lib.ui.resource.R
-internal fun Notification.Tunnel.toNotification(context: Context) =
+fun Notification.Tunnel.toNotification(context: Context) =
NotificationCompat.Builder(context, channelId.value)
.setContentIntent(contentIntent(context))
.setContentTitle(state.contentTitleResourceId(context))
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProvider.kt b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/tunnelstate/TunnelStateNotificationProvider.kt
index e3f985acd8..f75deb7258 100644
--- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProvider.kt
+++ b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/tunnelstate/TunnelStateNotificationProvider.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.service.notifications.tunnelstate
+package net.mullvad.mullvadvpn.lib.pushnotification.tunnelstate
import android.content.Context
import kotlinx.coroutines.CoroutineScope
@@ -18,10 +18,10 @@ import net.mullvad.mullvadvpn.lib.model.NotificationTunnelState
import net.mullvad.mullvadvpn.lib.model.NotificationUpdate
import net.mullvad.mullvadvpn.lib.model.PrepareError
import net.mullvad.mullvadvpn.lib.model.TunnelState
+import net.mullvad.mullvadvpn.lib.pushnotification.NotificationProvider
import net.mullvad.mullvadvpn.lib.repository.ConnectionProxy
import net.mullvad.mullvadvpn.lib.repository.DeviceRepository
import net.mullvad.mullvadvpn.lib.repository.UserPreferencesRepository
-import net.mullvad.mullvadvpn.service.notifications.NotificationProvider
class TunnelStateNotificationProvider(
context: Context,
@@ -31,7 +31,7 @@ class TunnelStateNotificationProvider(
channelId: NotificationChannelId,
scope: CoroutineScope,
) : NotificationProvider<Notification.Tunnel> {
- internal val notificationId = NotificationId(2)
+ val notificationId = NotificationId(2)
override val notifications: StateFlow<NotificationUpdate<Notification.Tunnel>> =
combine(
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/worker/ExpiryNotificationWorker.kt b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/worker/ExpiryNotificationWorker.kt
index 902ccbb50c..b4988933cb 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/worker/ExpiryNotificationWorker.kt
+++ b/android/lib/push-notification/src/main/kotlin/net/mullvad/mullvadvpn/lib/pushnotification/worker/ExpiryNotificationWorker.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.worker
+package net.mullvad.mullvadvpn.lib.pushnotification.worker
import android.app.Notification
import android.content.Context
@@ -16,13 +16,13 @@ import java.time.ZonedDateTime
import kotlin.getValue
import kotlinx.coroutines.withTimeoutOrNull
import net.mullvad.mullvadvpn.lib.common.constant.VPN_SERVICE_CLASS
+import net.mullvad.mullvadvpn.lib.common.serviceconnection.EmptyServiceConnection
import net.mullvad.mullvadvpn.lib.common.util.ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD
import net.mullvad.mullvadvpn.lib.model.NotificationChannel
+import net.mullvad.mullvadvpn.lib.pushnotification.ScheduleNotificationAlarmUseCase
+import net.mullvad.mullvadvpn.lib.pushnotification.accountexpiry.AccountExpiryNotificationProvider
import net.mullvad.mullvadvpn.lib.repository.AccountRepository
-import net.mullvad.mullvadvpn.service.R
-import net.mullvad.mullvadvpn.service.notifications.accountexpiry.AccountExpiryNotificationProvider
-import net.mullvad.mullvadvpn.ui.serviceconnection.EmptyServiceConnection
-import net.mullvad.mullvadvpn.usecase.ScheduleNotificationAlarmUseCase
+import net.mullvad.mullvadvpn.lib.ui.resource.R
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
diff --git a/android/service/build.gradle.kts b/android/service/build.gradle.kts
index a3928105bd..14f1b7ed28 100644
--- a/android/service/build.gradle.kts
+++ b/android/service/build.gradle.kts
@@ -80,6 +80,7 @@ dependencies {
implementation(projects.lib.grpc)
implementation(projects.lib.endpoint)
implementation(projects.lib.model)
+ implementation(projects.lib.pushNotification)
implementation(projects.lib.repository)
implementation(projects.lib.talpid)
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
index caaf7e127b..fd9af163f3 100644
--- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
+++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
@@ -19,12 +19,12 @@ import net.mullvad.mullvadvpn.lib.common.constant.KEY_DISCONNECT_ACTION
import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointFromIntentHolder
import net.mullvad.mullvadvpn.lib.grpc.ManagementService
import net.mullvad.mullvadvpn.lib.model.TunnelState
+import net.mullvad.mullvadvpn.lib.pushnotification.NotificationChannelFactory
+import net.mullvad.mullvadvpn.lib.pushnotification.NotificationManager
import net.mullvad.mullvadvpn.lib.repository.ConnectionProxy
import net.mullvad.mullvadvpn.service.di.vpnServiceModule
import net.mullvad.mullvadvpn.service.migration.MigrateSplitTunneling
import net.mullvad.mullvadvpn.service.notifications.ForegroundNotificationManager
-import net.mullvad.mullvadvpn.service.notifications.NotificationChannelFactory
-import net.mullvad.mullvadvpn.service.notifications.NotificationManager
import net.mullvad.mullvadvpn.service.util.extractAndOverwriteIfAssetMoreRecent
import net.mullvad.talpid.TalpidVpnService
import org.koin.android.ext.android.getKoin
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ForegroundNotificationManager.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ForegroundNotificationManager.kt
index cf324e6023..a63cb64f9a 100644
--- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ForegroundNotificationManager.kt
+++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ForegroundNotificationManager.kt
@@ -9,9 +9,9 @@ import net.mullvad.mullvadvpn.lib.model.Notification
import net.mullvad.mullvadvpn.lib.model.NotificationChannel
import net.mullvad.mullvadvpn.lib.model.NotificationTunnelState
import net.mullvad.mullvadvpn.lib.model.NotificationUpdate
+import net.mullvad.mullvadvpn.lib.pushnotification.tunnelstate.TunnelStateNotificationProvider
+import net.mullvad.mullvadvpn.lib.pushnotification.tunnelstate.toNotification
import net.mullvad.mullvadvpn.service.MullvadVpnService
-import net.mullvad.mullvadvpn.service.notifications.tunnelstate.TunnelStateNotificationProvider
-import net.mullvad.mullvadvpn.service.notifications.tunnelstate.toNotification
class ForegroundNotificationManager(
private val vpnService: MullvadVpnService,
diff --git a/android/service/src/test/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProviderTest.kt b/android/service/src/test/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProviderTest.kt
index 0595b3ac16..481c89956c 100644
--- a/android/service/src/test/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProviderTest.kt
+++ b/android/service/src/test/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProviderTest.kt
@@ -29,6 +29,7 @@ import net.mullvad.mullvadvpn.lib.model.NotificationUpdate
import net.mullvad.mullvadvpn.lib.model.PrepareError
import net.mullvad.mullvadvpn.lib.model.Prepared
import net.mullvad.mullvadvpn.lib.model.TunnelState
+import net.mullvad.mullvadvpn.lib.pushnotification.tunnelstate.TunnelStateNotificationProvider
import net.mullvad.mullvadvpn.lib.repository.ConnectionProxy
import net.mullvad.mullvadvpn.lib.repository.DeviceRepository
import net.mullvad.mullvadvpn.lib.repository.UserPreferencesRepository
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
index 7021eb4898..81742fef9a 100644
--- a/android/settings.gradle.kts
+++ b/android/settings.gradle.kts
@@ -48,6 +48,7 @@ include(
":lib:feature:customlist:impl",
":lib:feature:daita:impl",
":lib:feature:filter:impl",
+ ":lib:feature:login:impl",
":lib:feature:managedevices:impl",
":lib:feature:multihop:impl",
":lib:feature:notification:impl",
@@ -61,6 +62,7 @@ include(
":lib:model",
":lib:navigation",
":lib:payment",
+ ":lib:push-notification",
":lib:repository",
":lib:screen-test",
":lib:talpid",