diff options
| author | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2026-02-13 14:31:01 +0100 |
|---|---|---|
| committer | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2026-02-13 14:31:01 +0100 |
| commit | 46bd6d2d116cf2b6279df738b646d0a11b029cb4 (patch) | |
| tree | c96e0f81347f6cbe6a314f11db09c3cf555fca2d | |
| parent | bd3fc2ac81101df3bd2f52e8a4a2d5400929b20e (diff) | |
| parent | c17fd90f2f52acb8724c7bc3ba73ca29e4bfae6a (diff) | |
| download | mullvadvpn-46bd6d2d116cf2b6279df738b646d0a11b029cb4.tar.xz mullvadvpn-46bd6d2d116cf2b6279df738b646d0a11b029cb4.zip | |
Merge branch 'refactor-connect-login-account'
153 files changed, 703 insertions, 811 deletions
diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 7f26d4d61c..75a18966f1 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -387,6 +387,7 @@ dependencies { implementation(project(":lib:common-compose")) implementation(projects.lib.grpc) implementation(projects.lib.endpoint) + implementation(projects.lib.feature.account.impl) implementation(projects.lib.feature.addtime.impl) implementation(projects.lib.feature.anticensorship.impl) implementation(projects.lib.feature.apiaccess.impl) @@ -396,6 +397,9 @@ dependencies { implementation(projects.lib.feature.customlist.impl) implementation(projects.lib.feature.daita.impl) implementation(projects.lib.feature.filter.impl) + implementation(projects.lib.feature.home.impl) + implementation(projects.lib.feature.location.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) @@ -407,6 +411,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/button/ExternalActionButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ExternalActionButton.kt deleted file mode 100644 index 7b7c092205..0000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ExternalActionButton.kt +++ /dev/null @@ -1,74 +0,0 @@ -package net.mullvad.mullvadvpn.compose.button - -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.rounded.OpenInNew -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import net.mullvad.mullvadvpn.lib.ui.designsystem.VariantButton -import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme - -@Preview -@Composable -private fun PreviewExternalButtonEnabled() { - AppTheme { ExternalButton(onClick = {}, text = "Button", isEnabled = true) } -} - -@Preview -@Composable -private fun PreviewExternalButtonDisabled() { - AppTheme { ExternalButton(onClick = {}, text = "Button", isEnabled = false) } -} - -@Preview -@Composable -private fun PreviewExternalButtonLongText() { - AppTheme { - ExternalButton( - onClick = {}, - text = "Button text is long and is trying to take up space that is large", - isEnabled = true, - ) - } -} - -@Preview -@Composable -private fun PreviewExternalButtonSpinner() { - AppTheme { - ExternalButton( - onClick = {}, - text = "Button text is long and is trying to take up space that is large", - isEnabled = true, - isLoading = true, - ) - } -} - -@Composable -fun ExternalButton( - onClick: () -> Unit, - text: String, - modifier: Modifier = Modifier, - isEnabled: Boolean = true, - isLoading: Boolean = false, -) { - VariantButton( - text = text, - onClick = onClick, - modifier = modifier, - isEnabled = isEnabled, - isLoading = isLoading, - icon = { - if (!isLoading) { - Icon( - imageVector = Icons.AutoMirrored.Rounded.OpenInNew, - tint = MaterialTheme.colorScheme.onTertiary, - contentDescription = null, - ) - } - }, - ) -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/MullvadSegmentedButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/MullvadSegmentedButton.kt deleted file mode 100644 index 9a854dc2da..0000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/MullvadSegmentedButton.kt +++ /dev/null @@ -1,121 +0,0 @@ -package net.mullvad.mullvadvpn.compose.button - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SegmentedButton -import androidx.compose.material3.SegmentedButtonDefaults -import androidx.compose.material3.SingleChoiceSegmentedButtonRow -import androidx.compose.material3.SingleChoiceSegmentedButtonRowScope -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.compositeOver -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme -import net.mullvad.mullvadvpn.lib.ui.theme.color.onSelected -import net.mullvad.mullvadvpn.lib.ui.theme.color.selected - -@Preview -@Composable -private fun PreviewMullvadSegmentedButton() { - AppTheme { - SingleChoiceSegmentedButtonRow { - MullvadSegmentedStartButton(selected = true, text = "Start", onClick = {}) - MullvadSegmentedMiddleButton(selected = false, text = "Middle", onClick = {}) - MullvadSegmentedEndButton(selected = false, text = "End", onClick = {}) - } - } -} - -@Composable -private fun SingleChoiceSegmentedButtonRowScope.MullvadSegmentedButton( - selected: Boolean, - text: String, - onClick: () -> Unit, - shape: Shape, - selectedProgress: Float = 1f, -) { - SegmentedButton( - onClick = onClick, - selected = selected, - colors = - SegmentedButtonDefaults.colors() - .copy( - activeContainerColor = - MaterialTheme.colorScheme.selected - .copy(alpha = selectedProgress) - .compositeOver(MaterialTheme.colorScheme.primary), - activeContentColor = - MaterialTheme.colorScheme.onSelected - .copy(alpha = selectedProgress) - .compositeOver(MaterialTheme.colorScheme.onPrimary), - inactiveContainerColor = MaterialTheme.colorScheme.primary, - inactiveContentColor = MaterialTheme.colorScheme.onPrimary, - ), - border = BorderStroke(0.dp, Color.Unspecified), - label = { - Text( - text = text, - textAlign = TextAlign.Center, - style = MaterialTheme.typography.titleMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - }, - icon = {}, - shape = shape, - ) -} - -@Composable -fun SingleChoiceSegmentedButtonRowScope.MullvadSegmentedStartButton( - selected: Boolean, - selectedProgress: Float = 1f, - text: String, - onClick: () -> Unit, -) { - MullvadSegmentedButton( - selected = selected, - selectedProgress = selectedProgress, - text = text, - onClick = onClick, - shape = RoundedCornerShape(topStart = 8.dp, bottomStart = 8.dp), - ) -} - -@Composable -fun SingleChoiceSegmentedButtonRowScope.MullvadSegmentedMiddleButton( - selected: Boolean, - selectedProgress: Float = 1f, - text: String, - onClick: () -> Unit, -) { - MullvadSegmentedButton( - selected = selected, - selectedProgress = selectedProgress, - text = text, - onClick = onClick, - shape = RoundedCornerShape(0.dp), // Square - ) -} - -@Composable -fun SingleChoiceSegmentedButtonRowScope.MullvadSegmentedEndButton( - selected: Boolean, - selectedProgress: Float = 1f, - text: String, - onClick: () -> Unit, -) { - MullvadSegmentedButton( - selected = selected, - selectedProgress = selectedProgress, - text = text, - onClick = onClick, - shape = RoundedCornerShape(topEnd = 8.dp, bottomEnd = 8.dp), - ) -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/SelectObfuscationCellPreviewParameterProvider.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/SelectObfuscationCellPreviewParameterProvider.kt deleted file mode 100644 index 646d4eb6e4..0000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/SelectObfuscationCellPreviewParameterProvider.kt +++ /dev/null @@ -1,23 +0,0 @@ -package net.mullvad.mullvadvpn.compose.preview - -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import net.mullvad.mullvadvpn.lib.model.Constraint -import net.mullvad.mullvadvpn.lib.model.ObfuscationMode -import net.mullvad.mullvadvpn.lib.model.Port - -class SelectObfuscationCellPreviewParameterProvider : - PreviewParameterProvider<Triple<ObfuscationMode, Constraint<Port>, Boolean>> { - override val values: Sequence<Triple<ObfuscationMode, Constraint<Port>, Boolean>> = - sequenceOf( - Triple(ObfuscationMode.Shadowsocks, Constraint.Any, false), - Triple(ObfuscationMode.Shadowsocks, Constraint.Any, true), - Triple(ObfuscationMode.Shadowsocks, Constraint.Only(Port(PORT)), false), - Triple(ObfuscationMode.Shadowsocks, Constraint.Only(Port(PORT)), true), - Triple(ObfuscationMode.Udp2Tcp, Constraint.Any, false), - Triple(ObfuscationMode.Udp2Tcp, Constraint.Any, true), - Triple(ObfuscationMode.Udp2Tcp, Constraint.Only(Port(PORT)), false), - Triple(ObfuscationMode.Udp2Tcp, Constraint.Only(Port(PORT)), true), - ) -} - -private const val PORT = 44 diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/ShadowsocksSettingsUiStatePreviewParameterProvider.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/ShadowsocksSettingsUiStatePreviewParameterProvider.kt deleted file mode 100644 index c01a12f0f6..0000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/ShadowsocksSettingsUiStatePreviewParameterProvider.kt +++ /dev/null @@ -1,18 +0,0 @@ -package net.mullvad.mullvadvpn.compose.preview - -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import net.mullvad.mullvadvpn.compose.state.ShadowsocksSettingsUiState -import net.mullvad.mullvadvpn.lib.common.Lc -import net.mullvad.mullvadvpn.lib.common.toLc -import net.mullvad.mullvadvpn.lib.model.Constraint -import net.mullvad.mullvadvpn.lib.model.Port - -class ShadowsocksSettingsUiStatePreviewParameterProvider : - PreviewParameterProvider<Lc<Unit, ShadowsocksSettingsUiState>> { - override val values: Sequence<Lc<Unit, ShadowsocksSettingsUiState>> = - sequenceOf( - Lc.Loading(Unit), - ShadowsocksSettingsUiState(port = Constraint.Any).toLc(), - ShadowsocksSettingsUiState(port = Constraint.Only(Port(1)), customPort = Port(1)).toLc(), - ) -} 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 16d8d2ef8c..8f8bdbfdc8 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 @@ -1,15 +1,14 @@ +@file:Suppress("MatchingDeclarationName") + package net.mullvad.mullvadvpn.compose.screen -import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.SharedTransitionLayout -import androidx.compose.animation.SharedTransitionScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.semantics.semantics @@ -20,6 +19,7 @@ import com.ramcosta.composedestinations.DestinationsNavHost import com.ramcosta.composedestinations.annotation.ExternalDestination import com.ramcosta.composedestinations.annotation.NavHostGraph import com.ramcosta.composedestinations.generated.NavGraphs +import com.ramcosta.composedestinations.generated.account.destinations.AccountDestination import com.ramcosta.composedestinations.generated.addtime.destinations.VerificationPendingDestination import com.ramcosta.composedestinations.generated.anticensorship.destinations.AntiCensorshipSettingsDestination import com.ramcosta.composedestinations.generated.anticensorship.destinations.CustomPortDestination @@ -47,6 +47,19 @@ 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.home.destinations.Android16UpgradeWarningInfoDestination +import com.ramcosta.composedestinations.generated.home.destinations.ConnectDestination +import com.ramcosta.composedestinations.generated.home.destinations.DeviceNameInfoDestination +import com.ramcosta.composedestinations.generated.home.destinations.DeviceRevokedDestination +import com.ramcosta.composedestinations.generated.home.destinations.OutOfTimeDestination +import com.ramcosta.composedestinations.generated.home.destinations.WelcomeDestination +import com.ramcosta.composedestinations.generated.location.destinations.SearchLocationDestination +import com.ramcosta.composedestinations.generated.location.destinations.SelectLocationDestination +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 @@ -76,28 +89,30 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.dependency import com.ramcosta.composedestinations.rememberNavHostEngine import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator +import net.mullvad.mullvadvpn.common.compose.LocalSharedTransitionScope import net.mullvad.mullvadvpn.common.compose.accessibilityDataSensitive import net.mullvad.mullvadvpn.util.BackstackObserver import net.mullvad.mullvadvpn.viewmodel.DaemonScreenEvent import net.mullvad.mullvadvpn.viewmodel.MullvadAppViewModel import org.koin.androidx.compose.koinViewModel -val LocalNavAnimatedVisibilityScope = compositionLocalOf<AnimatedVisibilityScope?> { null } -@OptIn(ExperimentalSharedTransitionApi::class) -val LocalSharedTransitionScope = compositionLocalOf<SharedTransitionScope?> { null } - @NavHostGraph annotation class MainGraph { + @ExternalDestination<AccountDestination> + @ExternalDestination<Android16UpgradeWarningInfoDestination> @ExternalDestination<AntiCensorshipSettingsDestination> @ExternalDestination<ApiAccessListDestination> @ExternalDestination<ApiAccessMethodDetailsDestination> @ExternalDestination<ApiAccessMethodInfoDestination> + @ExternalDestination<ApiUnreachableInfoDestination> @ExternalDestination<AppInfoDestination> @ExternalDestination<AppearanceDestination> @ExternalDestination<AutoConnectAndLockdownModeDestination> @ExternalDestination<ChangelogDestination> + @ExternalDestination<ConnectDestination> @ExternalDestination<ConnectOnStartupInfoDestination> @ExternalDestination<ContentBlockersInfoDestination> + @ExternalDestination<CreateAccountConfirmationDestination> @ExternalDestination<CreateCustomListDestination> @ExternalDestination<CustomDnsInfoDestination> @ExternalDestination<CustomListLocationsDestination> @@ -109,6 +124,9 @@ annotation class MainGraph { @ExternalDestination<DeleteApiAccessMethodConfirmationDestination> @ExternalDestination<DeleteCustomListDestination> @ExternalDestination<DeviceIpInfoDestination> + @ExternalDestination<DeviceListDestination> + @ExternalDestination<DeviceNameInfoDestination> + @ExternalDestination<DeviceRevokedDestination> @ExternalDestination<DiscardApiAccessChangesDestination> @ExternalDestination<DiscardChangesDestination> @ExternalDestination<DnsDestination> @@ -119,18 +137,23 @@ annotation class MainGraph { @ExternalDestination<ImportOverridesByTextDestination> @ExternalDestination<Ipv6InfoDestination> @ExternalDestination<LocalNetworkSharingInfoDestination> + @ExternalDestination<LoginDestination> @ExternalDestination<MalwareInfoDestination> @ExternalDestination<ManageDevicesDestination> @ExternalDestination<ManageDevicesRemoveConfirmationDestination> @ExternalDestination<MtuDestination> @ExternalDestination<MultihopDestination> @ExternalDestination<NotificationSettingsDestination> + @ExternalDestination<OutOfTimeDestination> @ExternalDestination<QuantumResistanceInfoDestination> @ExternalDestination<RedeemVoucherDestination> + @ExternalDestination<RemoveDeviceConfirmationDestination> @ExternalDestination<ReportProblemDestination> @ExternalDestination<ReportProblemNoEmailDestination> @ExternalDestination<ResetServerIpOverridesConfirmationDestination> @ExternalDestination<SaveApiAccessMethodDestination> + @ExternalDestination<SearchLocationDestination> + @ExternalDestination<SelectLocationDestination> @ExternalDestination<SelectPortDestination> @ExternalDestination<ServerIpOverridesDestination> @ExternalDestination<ServerIpOverridesInfoDestination> @@ -139,6 +162,7 @@ annotation class MainGraph { @ExternalDestination<VerificationPendingDestination> @ExternalDestination<ViewLogsDestination> @ExternalDestination<VpnSettingsDestination> + @ExternalDestination<WelcomeDestination> companion object Includes } 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..a57e59f5cb 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 @@ -19,11 +19,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview 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.home.destinations.ConnectDestination +import com.ramcosta.composedestinations.generated.home.destinations.DeviceRevokedDestination +import com.ramcosta.composedestinations.generated.home.destinations.OutOfTimeDestination +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/state/ConnectNotificationState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectNotificationState.kt deleted file mode 100644 index 8439680500..0000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectNotificationState.kt +++ /dev/null @@ -1 +0,0 @@ -package net.mullvad.mullvadvpn.compose.state diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ShadowsocksSettingsUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ShadowsocksSettingsUiState.kt deleted file mode 100644 index 351f7e1e02..0000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ShadowsocksSettingsUiState.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.mullvad.mullvadvpn.compose.state - -import net.mullvad.mullvadvpn.lib.model.Constraint -import net.mullvad.mullvadvpn.lib.model.Port - -data class ShadowsocksSettingsUiState( - val port: Constraint<Port> = Constraint.Any, - val customPort: Port? = null, -) { - val isCustom = port is Constraint.Only && port.value == customPort -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/Navigation.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/Navigation.kt deleted file mode 100644 index 0d08e331e9..0000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/Navigation.kt +++ /dev/null @@ -1,17 +0,0 @@ -package net.mullvad.mullvadvpn.compose.util - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisallowComposableCalls -import com.ramcosta.composedestinations.result.NavResult -import com.ramcosta.composedestinations.result.ResultRecipient -import com.ramcosta.composedestinations.spec.DestinationSpec - -@Composable -fun <D : DestinationSpec, V> ResultRecipient<D, V>.OnNavResultValue( - onValue: @DisallowComposableCalls (value: V) -> Unit -) = onNavResult { - when (it) { - NavResult.Canceled -> Unit - is NavResult.Value -> onValue(it.value) - } -} 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 ae91016f73..d1c7649436 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 @@ -16,17 +16,30 @@ import net.mullvad.mullvadvpn.apiaccess.impl.screen.save.SaveApiAccessMethodView import net.mullvad.mullvadvpn.appearance.impl.AppearanceViewModel import net.mullvad.mullvadvpn.appinfo.impl.AppInfoViewModel import net.mullvad.mullvadvpn.appinfo.impl.changelog.ChangelogViewModel -import net.mullvad.mullvadvpn.compose.screen.location.LocationBottomSheetState -import net.mullvad.mullvadvpn.compose.screen.location.RelayListScrollConnection import net.mullvad.mullvadvpn.customlist.impl.screen.create.CreateCustomListDialogViewModel import net.mullvad.mullvadvpn.customlist.impl.screen.delete.DeleteCustomListConfirmationViewModel import net.mullvad.mullvadvpn.customlist.impl.screen.editlist.EditCustomListViewModel import net.mullvad.mullvadvpn.customlist.impl.screen.editlocations.CustomListLocationsViewModel import net.mullvad.mullvadvpn.customlist.impl.screen.editname.EditCustomListNameDialogViewModel import net.mullvad.mullvadvpn.customlist.impl.screen.lists.CustomListsViewModel +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.home.impl.connect.ConnectViewModel +import net.mullvad.mullvadvpn.feature.home.impl.connect.notificationbanner.InAppNotificationController +import net.mullvad.mullvadvpn.feature.home.impl.devicerevoked.DeviceRevokedViewModel +import net.mullvad.mullvadvpn.feature.home.impl.outoftime.OutOfTimeViewModel +import net.mullvad.mullvadvpn.feature.home.impl.welcome.WelcomeViewModel +import net.mullvad.mullvadvpn.feature.location.impl.RelayListScrollConnection +import net.mullvad.mullvadvpn.feature.location.impl.SelectLocationViewModel +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.LocationBottomSheetState +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.LocationBottomSheetViewModel +import net.mullvad.mullvadvpn.feature.location.impl.list.SelectLocationListViewModel +import net.mullvad.mullvadvpn.feature.location.impl.search.SearchLocationViewModel +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 @@ -93,27 +106,14 @@ import net.mullvad.mullvadvpn.lib.usecase.inappnotification.TunnelStateNotificat import net.mullvad.mullvadvpn.lib.usecase.inappnotification.VersionNotificationUseCase import net.mullvad.mullvadvpn.multihop.impl.MultihopViewModel import net.mullvad.mullvadvpn.receiver.AutoStartVpnBootCompletedReceiver -import net.mullvad.mullvadvpn.repository.InAppNotificationController import net.mullvad.mullvadvpn.serveripoverride.impl.ServerIpOverridesViewModel import net.mullvad.mullvadvpn.serveripoverride.impl.reset.ResetServerIpOverridesConfirmationViewModel import net.mullvad.mullvadvpn.ui.MainActivity import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.util.BackstackObserver -import net.mullvad.mullvadvpn.viewmodel.AccountViewModel -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 import net.mullvad.mullvadvpn.viewmodel.SplashViewModel -import net.mullvad.mullvadvpn.viewmodel.WelcomeViewModel -import net.mullvad.mullvadvpn.viewmodel.location.LocationBottomSheetViewModel -import net.mullvad.mullvadvpn.viewmodel.location.SearchLocationViewModel -import net.mullvad.mullvadvpn.viewmodel.location.SelectLocationListViewModel -import net.mullvad.mullvadvpn.viewmodel.location.SelectLocationViewModel import org.apache.commons.validator.routines.InetAddressValidator import org.koin.android.ext.koin.androidContext import org.koin.core.module.dsl.viewModel 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/MainActivity.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt index b6af4b4fae..a3ff3255cc 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt @@ -22,10 +22,10 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.compose.screen.MullvadApp -import net.mullvad.mullvadvpn.compose.util.CreateVpnProfile import net.mullvad.mullvadvpn.di.paymentModule import net.mullvad.mullvadvpn.di.uiModule import net.mullvad.mullvadvpn.lib.common.constant.KEY_REQUEST_VPN_PROFILE +import net.mullvad.mullvadvpn.lib.common.util.CreateVpnProfile import net.mullvad.mullvadvpn.lib.common.util.SdkUtils.requestNotificationPermissionIfMissing import net.mullvad.mullvadvpn.lib.common.util.prepareVpnSafe import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointFromIntentHolder diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/StatusLevel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/StatusLevel.kt deleted file mode 100644 index acac4ae7f6..0000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/StatusLevel.kt +++ /dev/null @@ -1,7 +0,0 @@ -package net.mullvad.mullvadvpn.ui.notification - -enum class StatusLevel { - Error, - Warning, - Info, -} 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/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/lib/common-compose/src/main/kotlin/net/mullvad/mullvadvpn/common/compose/Shared.kt b/android/lib/common-compose/src/main/kotlin/net/mullvad/mullvadvpn/common/compose/Shared.kt new file mode 100644 index 0000000000..fe9f0a99de --- /dev/null +++ b/android/lib/common-compose/src/main/kotlin/net/mullvad/mullvadvpn/common/compose/Shared.kt @@ -0,0 +1,10 @@ +package net.mullvad.mullvadvpn.common.compose + +import androidx.compose.animation.AnimatedVisibilityScope +import androidx.compose.animation.ExperimentalSharedTransitionApi +import androidx.compose.animation.SharedTransitionScope +import androidx.compose.runtime.compositionLocalOf + +val LocalNavAnimatedVisibilityScope = compositionLocalOf<AnimatedVisibilityScope?> { null } +@OptIn(ExperimentalSharedTransitionApi::class) +val LocalSharedTransitionScope = compositionLocalOf<SharedTransitionScope?> { null } 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/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/CreateVpnProfile.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CreateVpnProfile.kt index 750ca6485c..2f1b6167d9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/CreateVpnProfile.kt +++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CreateVpnProfile.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.util +package net.mullvad.mullvadvpn.lib.common.util import android.app.Activity import android.content.Context diff --git a/android/lib/feature/account/impl/build.gradle.kts b/android/lib/feature/account/impl/build.gradle.kts new file mode 100644 index 0000000000..8c99f8daa0 --- /dev/null +++ b/android/lib/feature/account/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.account.impl" + ksp { arg("compose-destinations.moduleName", "account") } +} + +dependencies { + implementation(projects.lib.repository) + implementation(projects.lib.payment) + implementation(projects.lib.feature.addtime.impl) + implementation(projects.lib.feature.login.impl) + implementation(projects.lib.feature.managedevices.impl) + implementation(projects.lib.feature.redeemvoucher.impl) + + implementation(libs.koin.compose) + implementation(libs.arrow) + + // Destinations + implementation(libs.compose.destinations) + ksp(libs.compose.destinations.ksp) +} diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt b/android/lib/feature/account/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreenTest.kt index 6f9ec792d6..dccdd7269b 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt +++ b/android/lib/feature/account/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreenTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen +package net.mullvad.mullvadvpn.feature.account.impl import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.ui.test.ExperimentalTestApi @@ -16,7 +16,6 @@ import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.model.AccountNumber import net.mullvad.mullvadvpn.screen.test.createEdgeToEdgeComposeExtension import net.mullvad.mullvadvpn.screen.test.setContentWithTheme -import net.mullvad.mullvadvpn.viewmodel.AccountUiState import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension diff --git a/android/lib/feature/account/impl/src/main/AndroidManifest.xml b/android/lib/feature/account/impl/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..8bdb7e14b3 --- /dev/null +++ b/android/lib/feature/account/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/component/AccountNumberView.kt b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountNumberView.kt index bb261acb15..a2e4de851f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/AccountNumberView.kt +++ b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountNumberView.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.component +package net.mullvad.mullvadvpn.feature.account.impl import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreen.kt index 59a3391cb7..0c3924c895 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt +++ b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreen.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen +package net.mullvad.mullvadvpn.feature.account.impl import androidx.compose.animation.animateContentSize import androidx.compose.foundation.layout.Arrangement @@ -35,25 +35,21 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.LayoutDirection import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.dropUnlessResumed +import androidx.navigation.NavController import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.generated.NavGraphs +import com.ramcosta.composedestinations.annotation.ExternalModuleGraph import com.ramcosta.composedestinations.generated.addtime.destinations.VerificationPendingDestination -import com.ramcosta.composedestinations.generated.destinations.LoginDestination +import com.ramcosta.composedestinations.generated.login.destinations.LoginDestination import com.ramcosta.composedestinations.generated.managedevices.destinations.ManageDevicesDestination import com.ramcosta.composedestinations.generated.redeemvoucher.destinations.RedeemVoucherDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import java.time.ZonedDateTime import kotlinx.coroutines.launch -import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.common.compose.CollectSideEffectWithLifecycle import net.mullvad.mullvadvpn.common.compose.SecureScreenWhileInView import net.mullvad.mullvadvpn.common.compose.createCopyToClipboardHandle import net.mullvad.mullvadvpn.common.compose.createOpenAccountPageHook import net.mullvad.mullvadvpn.common.compose.showSnackbarImmediately -import net.mullvad.mullvadvpn.compose.component.CopyableObfuscationView -import net.mullvad.mullvadvpn.compose.component.InformationView -import net.mullvad.mullvadvpn.compose.component.MissingPolicy -import net.mullvad.mullvadvpn.compose.preview.AccountUiStatePreviewParameterProvider import net.mullvad.mullvadvpn.core.animation.AccountTransition import net.mullvad.mullvadvpn.feature.addtime.impl.AddTimeBottomSheet import net.mullvad.mullvadvpn.lib.common.Lc @@ -65,8 +61,6 @@ import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryTextButton import net.mullvad.mullvadvpn.lib.ui.tag.MANAGE_DEVICES_BUTTON_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme import net.mullvad.mullvadvpn.lib.ui.theme.Dimens -import net.mullvad.mullvadvpn.viewmodel.AccountUiState -import net.mullvad.mullvadvpn.viewmodel.AccountViewModel import org.koin.androidx.compose.koinViewModel @OptIn(ExperimentalMaterial3Api::class) @@ -90,9 +84,9 @@ private fun PreviewAccountScreen( } @OptIn(ExperimentalMaterial3Api::class) -@Destination<MainGraph>(style = AccountTransition::class) +@Destination<ExternalModuleGraph>(style = AccountTransition::class) @Composable -fun Account(navigator: DestinationsNavigator) { +fun Account(navController: NavController, navigator: DestinationsNavigator) { val vm = koinViewModel<AccountViewModel>() val state by vm.uiState.collectAsStateWithLifecycle() @@ -106,9 +100,9 @@ fun Account(navigator: DestinationsNavigator) { CollectSideEffectWithLifecycle(vm.uiSideEffect) { sideEffect -> when (sideEffect) { AccountViewModel.UiSideEffect.NavigateToLogin -> { - navigator.navigate(LoginDestination(null)) { + navController.navigate(LoginDestination.baseRoute) { launchSingleTop = true - popUpTo(NavGraphs.main) { inclusive = true } + popUpTo("main") { inclusive = true } } } is AccountViewModel.UiSideEffect.OpenAccountManagementPageInBrowser -> diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/AccountUiStatePreviewParameterProvider.kt b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountUiStatePreviewParameterProvider.kt index ebea5d2266..305f5b0898 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/AccountUiStatePreviewParameterProvider.kt +++ b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountUiStatePreviewParameterProvider.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.preview +package net.mullvad.mullvadvpn.feature.account.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import java.time.ZonedDateTime @@ -6,7 +6,6 @@ import java.time.format.DateTimeFormatter import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.common.toLc import net.mullvad.mullvadvpn.lib.model.AccountNumber -import net.mullvad.mullvadvpn.viewmodel.AccountUiState class AccountUiStatePreviewParameterProvider : PreviewParameterProvider<Lc<Unit, AccountUiState>> { override val values = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountViewModel.kt index 33f014a59f..ba7bb84c72 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt +++ b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountViewModel.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel +package net.mullvad.mullvadvpn.feature.account.impl import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/AnimatedIconButton.kt b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AnimatedIconButton.kt index dd91846c8c..e0eaaff03f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/AnimatedIconButton.kt +++ b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AnimatedIconButton.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.button +package net.mullvad.mullvadvpn.feature.account.impl import androidx.compose.animation.AnimatedContent import androidx.compose.material3.Icon diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CopyableObfuscationView.kt b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/CopyableObfuscationView.kt index 60f47ab133..41af898a2b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CopyableObfuscationView.kt +++ b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/CopyableObfuscationView.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.component +package net.mullvad.mullvadvpn.feature.account.impl import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -17,8 +17,6 @@ import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.button.AnimatedIconButton import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme @Preview diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/InformationView.kt b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/InformationView.kt index e46ca2cd7c..14cf201bb9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/InformationView.kt +++ b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/InformationView.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.component +package net.mullvad.mullvadvpn.feature.account.impl import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Text.kt b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/Text.kt index 17fdc39ae1..333bf95f94 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Text.kt +++ b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/Text.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.component +package net.mullvad.mullvadvpn.feature.account.impl import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModelTest.kt b/android/lib/feature/account/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountViewModelTest.kt index 8a44c03e36..c94d097e9d 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModelTest.kt +++ b/android/lib/feature/account/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountViewModelTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel +package net.mullvad.mullvadvpn.feature.account.impl import app.cash.turbine.test import arrow.core.right @@ -14,7 +14,6 @@ import kotlin.test.assertEquals import kotlin.test.assertIs import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest -import net.mullvad.mullvadvpn.data.UUID import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.lib.model.AccountNumber @@ -127,5 +126,6 @@ class AccountViewModelTest { companion object { private const val DUMMY_DEVICE_NAME = "fake_name" + private const val UUID = "12345678-1234-5678-1234-567812345678" } } diff --git a/android/lib/feature/home/impl/build.gradle.kts b/android/lib/feature/home/impl/build.gradle.kts new file mode 100644 index 0000000000..970ed2cdeb --- /dev/null +++ b/android/lib/feature/home/impl/build.gradle.kts @@ -0,0 +1,51 @@ +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.home.impl" + ksp { arg("compose-destinations.moduleName", "home") } +} + +dependencies { + implementation(projects.lib.map) + implementation(projects.lib.payment) + implementation(projects.lib.pushNotification) + implementation(projects.lib.repository) + implementation(projects.lib.tv) + implementation(projects.lib.usecase) + implementation(projects.lib.feature.account.impl) + implementation(projects.lib.feature.addtime.impl) + implementation(projects.lib.feature.anticensorship.impl) + implementation(projects.lib.feature.appinfo.impl) + implementation(projects.lib.feature.daita.impl) + implementation(projects.lib.feature.location.impl) + implementation(projects.lib.feature.login.impl) + implementation(projects.lib.feature.multihop.impl) + implementation(projects.lib.feature.redeemvoucher.impl) + implementation(projects.lib.feature.serveripoverride.impl) + implementation(projects.lib.feature.settings.impl) + implementation(projects.lib.feature.splittunneling.impl) + implementation(projects.lib.feature.vpnsettings.impl) + + implementation(libs.androidx.animation) + implementation(libs.koin.compose) + implementation(libs.arrow) + implementation(libs.compose.constrainlayout) + implementation(libs.androidx.credentials) { + // This dependency adds a lot of unused permissions to the app. + // It is not used so let's exclude it. + // Unfortunately, this is not possible to do using libs.version.toml + // https://github.com/gradle/gradle/issues/26367#issuecomment-2120830998 + exclude("androidx.biometric", "biometric") + } + + // Destinations + implementation(libs.compose.destinations) + ksp(libs.compose.destinations.ksp) +} diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt b/android/lib/feature/home/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectScreenTest.kt index 3a93a9361a..defb896fd8 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt +++ b/android/lib/feature/home/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectScreenTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen +package net.mullvad.mullvadvpn.feature.home.impl.connect import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag @@ -13,7 +13,6 @@ import io.mockk.verify import java.time.Duration import java.time.Instant import java.time.ZonedDateTime -import net.mullvad.mullvadvpn.compose.state.ConnectUiState import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect import net.mullvad.mullvadvpn.lib.model.ErrorState import net.mullvad.mullvadvpn.lib.model.ErrorStateCause diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt b/android/lib/feature/home/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedScreenTest.kt index 159c26e420..8b5c2af47e 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt +++ b/android/lib/feature/home/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedScreenTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen +package net.mullvad.mullvadvpn.feature.home.impl.devicerevoked import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText @@ -7,7 +7,6 @@ import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify -import net.mullvad.mullvadvpn.compose.state.DeviceRevokedUiState import net.mullvad.mullvadvpn.screen.test.createEdgeToEdgeComposeExtension import net.mullvad.mullvadvpn.screen.test.setContentWithTheme import org.junit.jupiter.api.BeforeEach diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt b/android/lib/feature/home/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/home/impl/outoftime/OutOfTimeScreenTest.kt index 6941b96c1f..19d0977cb9 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt +++ b/android/lib/feature/home/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/home/impl/outoftime/OutOfTimeScreenTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen +package net.mullvad.mullvadvpn.feature.home.impl.outoftime import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithContentDescription @@ -11,7 +11,6 @@ import io.mockk.every import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.flow.MutableStateFlow -import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState import net.mullvad.mullvadvpn.feature.addtime.impl.AddTimeUiState import net.mullvad.mullvadvpn.feature.addtime.impl.AddTimeViewModel import net.mullvad.mullvadvpn.lib.common.Lc diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt b/android/lib/feature/home/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/WelcomeScreenTest.kt index 11ffab3985..c4553d3193 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt +++ b/android/lib/feature/home/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/WelcomeScreenTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen +package net.mullvad.mullvadvpn.feature.home.impl.welcome import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag @@ -10,7 +10,6 @@ import io.mockk.every import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.flow.MutableStateFlow -import net.mullvad.mullvadvpn.compose.state.WelcomeUiState import net.mullvad.mullvadvpn.feature.addtime.impl.AddTimeUiState import net.mullvad.mullvadvpn.feature.addtime.impl.AddTimeViewModel import net.mullvad.mullvadvpn.lib.common.Lc diff --git a/android/lib/feature/home/impl/src/main/AndroidManifest.xml b/android/lib/feature/home/impl/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..8bdb7e14b3 --- /dev/null +++ b/android/lib/feature/home/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/transitions/HomeTransition.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/HomeTransition.kt index ab837f091d..9a6bfde4c5 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/transitions/HomeTransition.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/HomeTransition.kt @@ -1,13 +1,12 @@ -package net.mullvad.mullvadvpn.compose.transitions +package net.mullvad.mullvadvpn.feature.home.impl import androidx.compose.animation.AnimatedContentTransitionScope 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 // This is used for OutOfTime, Welcome, and Connect destinations. object HomeTransition : DestinationStyle.Animated() { @@ -15,7 +14,7 @@ object HomeTransition : DestinationStyle.Animated() { override val enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = { - when (initialState.destination()) { + when (initialState.destination) { LoginDestination -> fadeIn() else -> EnterTransition.None } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewData.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/TunnelStatePreviewData.kt index a35de85e4c..f3dd7f33b6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewData.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/TunnelStatePreviewData.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.preview +package net.mullvad.mullvadvpn.feature.home.impl import java.net.InetSocketAddress import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/info/Android16UpgradeWarningInfoDialog.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/Android16UpgradeWarningInfoDialog.kt index 7d3fe27ced..8f56fbae85 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/info/Android16UpgradeWarningInfoDialog.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/Android16UpgradeWarningInfoDialog.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.dialog.info +package net.mullvad.mullvadvpn.feature.home.impl.connect import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable @@ -7,13 +7,13 @@ import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.ExternalModuleGraph import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.spec.DestinationStyle -import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.common.compose.clickableAnnotatedString import net.mullvad.mullvadvpn.common.compose.createCopyToClipboardHandle -import net.mullvad.mullvadvpn.compose.screen.MainGraph import net.mullvad.mullvadvpn.lib.ui.component.dialog.InfoDialog +import net.mullvad.mullvadvpn.lib.ui.resource.R import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme @Preview @@ -22,7 +22,7 @@ private fun PreviewAndroid16UpgradeWarningInfoDialog() { AppTheme { Android16UpgradeWarningInfoDialog(onDismiss = {}, onClickEmail = {}) } } -@Destination<MainGraph>(style = DestinationStyle.Dialog::class) +@Destination<ExternalModuleGraph>(style = DestinationStyle.Dialog::class) @Composable fun Android16UpgradeWarningInfo(navigator: DestinationsNavigator) { val copyToClipboard = createCopyToClipboardHandle(isSensitive = false) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectScreen.kt index 73b229dad2..615f8c0639 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectScreen.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen +package net.mullvad.mullvadvpn.feature.home.impl.connect import android.content.res.Resources import androidx.activity.compose.rememberLauncherForActivityResult @@ -64,16 +64,17 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.dropUnlessResumed +import androidx.navigation.NavController import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.generated.NavGraphs +import com.ramcosta.composedestinations.annotation.ExternalModuleGraph +import com.ramcosta.composedestinations.generated.account.destinations.AccountDestination import com.ramcosta.composedestinations.generated.anticensorship.destinations.AntiCensorshipSettingsDestination import com.ramcosta.composedestinations.generated.appinfo.destinations.ChangelogDestination import com.ramcosta.composedestinations.generated.daita.destinations.DaitaDestination -import com.ramcosta.composedestinations.generated.destinations.AccountDestination -import com.ramcosta.composedestinations.generated.destinations.Android16UpgradeWarningInfoDestination -import com.ramcosta.composedestinations.generated.destinations.DeviceRevokedDestination -import com.ramcosta.composedestinations.generated.destinations.OutOfTimeDestination -import com.ramcosta.composedestinations.generated.destinations.SelectLocationDestination +import com.ramcosta.composedestinations.generated.home.destinations.Android16UpgradeWarningInfoDestination +import com.ramcosta.composedestinations.generated.home.destinations.DeviceRevokedDestination +import com.ramcosta.composedestinations.generated.home.destinations.OutOfTimeDestination +import com.ramcosta.composedestinations.generated.location.destinations.SelectLocationDestination import com.ramcosta.composedestinations.generated.multihop.destinations.MultihopDestination import com.ramcosta.composedestinations.generated.serveripoverride.destinations.ServerIpOverridesDestination import com.ramcosta.composedestinations.generated.settings.destinations.SettingsDestination @@ -82,9 +83,9 @@ import com.ramcosta.composedestinations.generated.vpnsettings.destinations.VpnSe 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.appinfo.impl.changelog.ChangelogNavArgs import net.mullvad.mullvadvpn.common.compose.CollectSideEffectWithLifecycle +import net.mullvad.mullvadvpn.common.compose.LocalNavAnimatedVisibilityScope import net.mullvad.mullvadvpn.common.compose.SECURE_ZOOM import net.mullvad.mullvadvpn.common.compose.SECURE_ZOOM_ANIMATION_MILLIS import net.mullvad.mullvadvpn.common.compose.UNSECURE_ZOOM @@ -94,18 +95,15 @@ import net.mullvad.mullvadvpn.common.compose.fallbackLatLong import net.mullvad.mullvadvpn.common.compose.isTv import net.mullvad.mullvadvpn.common.compose.safeOpenUri import net.mullvad.mullvadvpn.common.compose.showSnackbarImmediately -import net.mullvad.mullvadvpn.compose.button.ConnectionButton -import net.mullvad.mullvadvpn.compose.button.SwitchLocationButton -import net.mullvad.mullvadvpn.compose.component.ConnectionStatusText -import net.mullvad.mullvadvpn.compose.component.connectioninfo.ConnectionDetailPanel -import net.mullvad.mullvadvpn.compose.component.connectioninfo.FeatureIndicatorsPanel -import net.mullvad.mullvadvpn.compose.component.connectioninfo.toInAddress -import net.mullvad.mullvadvpn.compose.component.notificationbanner.NotificationBanner -import net.mullvad.mullvadvpn.compose.preview.ConnectUiStatePreviewParameterProvider -import net.mullvad.mullvadvpn.compose.state.ConnectUiState -import net.mullvad.mullvadvpn.compose.transitions.HomeTransition -import net.mullvad.mullvadvpn.compose.util.CreateVpnProfile import net.mullvad.mullvadvpn.core.OnNavResultValue +import net.mullvad.mullvadvpn.feature.home.impl.HomeTransition +import net.mullvad.mullvadvpn.feature.home.impl.connect.button.ConnectionButton +import net.mullvad.mullvadvpn.feature.home.impl.connect.button.SwitchLocationButton +import net.mullvad.mullvadvpn.feature.home.impl.connect.connectioninfo.ConnectionDetailPanel +import net.mullvad.mullvadvpn.feature.home.impl.connect.connectioninfo.FeatureIndicatorsPanel +import net.mullvad.mullvadvpn.feature.home.impl.connect.connectioninfo.toInAddress +import net.mullvad.mullvadvpn.feature.home.impl.connect.notificationbanner.NotificationBanner +import net.mullvad.mullvadvpn.lib.common.util.CreateVpnProfile import net.mullvad.mullvadvpn.lib.common.util.openVpnSettings import net.mullvad.mullvadvpn.lib.common.util.removeHtmlTags import net.mullvad.mullvadvpn.lib.map.AnimatedMap @@ -125,6 +123,7 @@ import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithTopBarAndDeviceName import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadSnackbar +import net.mullvad.mullvadvpn.lib.ui.resource.R import net.mullvad.mullvadvpn.lib.ui.tag.CONNECT_BUTTON_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.tag.CONNECT_CARD_HEADER_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.tag.RECONNECT_BUTTON_TEST_TAG @@ -137,7 +136,6 @@ import net.mullvad.mullvadvpn.lib.ui.theme.color.Alpha80 import net.mullvad.mullvadvpn.lib.ui.theme.color.AlphaInvisible import net.mullvad.mullvadvpn.lib.ui.theme.color.AlphaScrollbar import net.mullvad.mullvadvpn.lib.ui.theme.color.AlphaVisible -import net.mullvad.mullvadvpn.viewmodel.ConnectViewModel import org.koin.androidx.compose.koinViewModel private const val CONNECT_BUTTON_THROTTLE_MILLIS = 1000 @@ -175,9 +173,10 @@ private fun PreviewAccountScreen( } @Suppress("LongMethod") -@Destination<MainGraph>(style = HomeTransition::class) +@Destination<ExternalModuleGraph>(style = HomeTransition::class) @Composable fun Connect( + navController: NavController, navigator: DestinationsNavigator, animatedVisibilityScope: AnimatedVisibilityScope, selectLocationResultRecipient: ResultRecipient<SelectLocationDestination, Boolean>, @@ -207,15 +206,15 @@ fun Connect( openAccountPage(sideEffect.token) is ConnectViewModel.UiSideEffect.OutOfTime -> - navigator.navigate(OutOfTimeDestination) { + navController.navigate(OutOfTimeDestination.route) { launchSingleTop = true - popUpTo(NavGraphs.main) { inclusive = true } + popUpTo("main") { inclusive = true } } ConnectViewModel.UiSideEffect.RevokedDevice -> - navigator.navigate(DeviceRevokedDestination) { + navController.navigate(DeviceRevokedDestination.route) { launchSingleTop = true - popUpTo(NavGraphs.main) { inclusive = true } + popUpTo("main") { inclusive = true } } is ConnectViewModel.UiSideEffect.NotPrepared -> diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectUiState.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectUiState.kt index 956b1506a7..2c97b44f3f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectUiState.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectUiState.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.state +package net.mullvad.mullvadvpn.feature.home.impl.connect import net.mullvad.mullvadvpn.lib.model.GeoIpLocation import net.mullvad.mullvadvpn.lib.model.InAppNotification diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/ConnectUiStatePreviewParameterProvider.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectUiStatePreviewParameterProvider.kt index 0a332ba385..b362099faf 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/ConnectUiStatePreviewParameterProvider.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectUiStatePreviewParameterProvider.kt @@ -1,8 +1,8 @@ -package net.mullvad.mullvadvpn.compose.preview +package net.mullvad.mullvadvpn.feature.home.impl.connect import androidx.compose.ui.tooling.preview.PreviewParameterProvider import java.net.InetAddress -import net.mullvad.mullvadvpn.compose.state.ConnectUiState +import net.mullvad.mullvadvpn.feature.home.impl.TunnelStatePreviewData import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect import net.mullvad.mullvadvpn.lib.model.GeoIpLocation import net.mullvad.mullvadvpn.lib.model.InAppNotification diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModel.kt index 6e5234f09d..0e2c187ecf 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModel.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel +package net.mullvad.mullvadvpn.feature.home.impl.connect import android.content.res.Resources import android.net.Uri @@ -18,9 +18,8 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.state.ConnectUiState import net.mullvad.mullvadvpn.feature.addtime.impl.isSuccess +import net.mullvad.mullvadvpn.feature.home.impl.connect.notificationbanner.InAppNotificationController import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT import net.mullvad.mullvadvpn.lib.common.util.combine import net.mullvad.mullvadvpn.lib.common.util.daysFromNow @@ -38,11 +37,11 @@ import net.mullvad.mullvadvpn.lib.repository.DeviceRepository import net.mullvad.mullvadvpn.lib.repository.NewDeviceRepository import net.mullvad.mullvadvpn.lib.repository.PaymentLogic import net.mullvad.mullvadvpn.lib.repository.UserPreferencesRepository +import net.mullvad.mullvadvpn.lib.ui.resource.R import net.mullvad.mullvadvpn.lib.usecase.LastKnownLocationUseCase import net.mullvad.mullvadvpn.lib.usecase.OutOfTimeUseCase import net.mullvad.mullvadvpn.lib.usecase.SelectedLocationTitleUseCase import net.mullvad.mullvadvpn.lib.usecase.SystemVpnSettingsAvailableUseCase -import net.mullvad.mullvadvpn.repository.InAppNotificationController @Suppress("LongParameterList") class ConnectViewModel( @@ -89,6 +88,7 @@ class ConnectViewModel( when (tunnelState) { is TunnelState.Disconnected -> tunnelState.location ?: lastKnownDisconnectedLocation + is TunnelState.Connecting -> tunnelState.location is TunnelState.Connected -> tunnelState.location is TunnelState.Disconnecting -> @@ -100,6 +100,7 @@ class ConnectViewModel( // location ActionAfterDisconnect.Reconnect -> prevTunnelState?.location() } + is TunnelState.Error -> lastKnownDisconnectedLocation }, selectedRelayItemTitle = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectionStatusText.kt index 43610312c0..347202c39e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectionStatusText.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.component +package net.mullvad.mullvadvpn.feature.home.impl.connect import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column @@ -10,10 +10,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewParameterProvider import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect import net.mullvad.mullvadvpn.lib.model.TunnelState +import net.mullvad.mullvadvpn.lib.ui.resource.R import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme @Preview diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewParameterProvider.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/TunnelStatePreviewParameterProvider.kt index f9390e9986..e9bb06d47d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewParameterProvider.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/TunnelStatePreviewParameterProvider.kt @@ -1,11 +1,11 @@ -package net.mullvad.mullvadvpn.compose.preview +package net.mullvad.mullvadvpn.feature.home.impl.connect import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateConnectedState -import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateConnectingState -import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateDisconnectedState -import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateDisconnectingState -import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateErrorState +import net.mullvad.mullvadvpn.feature.home.impl.TunnelStatePreviewData.generateConnectedState +import net.mullvad.mullvadvpn.feature.home.impl.TunnelStatePreviewData.generateConnectingState +import net.mullvad.mullvadvpn.feature.home.impl.TunnelStatePreviewData.generateDisconnectedState +import net.mullvad.mullvadvpn.feature.home.impl.TunnelStatePreviewData.generateDisconnectingState +import net.mullvad.mullvadvpn.feature.home.impl.TunnelStatePreviewData.generateErrorState import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect import net.mullvad.mullvadvpn.lib.model.TunnelState diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/button/ConnectionButton.kt index cbad605ba5..5611a61e13 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/button/ConnectionButton.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.button +package net.mullvad.mullvadvpn.feature.home.impl.connect.button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.MaterialTheme @@ -7,10 +7,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewParameterProvider +import net.mullvad.mullvadvpn.feature.home.impl.connect.TunnelStatePreviewParameterProvider import net.mullvad.mullvadvpn.lib.model.TunnelState import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryButton +import net.mullvad.mullvadvpn.lib.ui.resource.R import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme @Composable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/SwitchLocationButton.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/button/SwitchLocationButton.kt index e26120a906..f30fd345f6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/SwitchLocationButton.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/button/SwitchLocationButton.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.button +package net.mullvad.mullvadvpn.feature.home.impl.connect.button import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -32,7 +32,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension -import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.lib.ui.resource.R import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme import net.mullvad.mullvadvpn.lib.ui.theme.Dimens diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/ConnectionDetailPanel.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/connectioninfo/ConnectionDetailPanel.kt index f593b3a632..4473ace88b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/ConnectionDetailPanel.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/connectioninfo/ConnectionDetailPanel.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.component.connectioninfo +package net.mullvad.mullvadvpn.feature.home.impl.connect.connectioninfo import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.layout.Box @@ -15,11 +15,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.ConnectionDetails +import net.mullvad.mullvadvpn.feature.home.impl.connect.ConnectionDetails import net.mullvad.mullvadvpn.lib.model.TransportProtocol import net.mullvad.mullvadvpn.lib.model.TunnelEndpoint import net.mullvad.mullvadvpn.lib.ui.component.SPACE_CHAR +import net.mullvad.mullvadvpn.lib.ui.resource.R import net.mullvad.mullvadvpn.lib.ui.tag.LOCATION_INFO_CONNECTION_IN_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.tag.LOCATION_INFO_CONNECTION_OUT_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.theme.Dimens diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/ConnectionInfoHeader.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/connectioninfo/ConnectionInfoHeader.kt index 49a42ebc9a..c68272a667 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/ConnectionInfoHeader.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/connectioninfo/ConnectionInfoHeader.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.component.connectioninfo +package net.mullvad.mullvadvpn.feature.home.impl.connect.connectioninfo import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/FeatureIndicatorsPanel.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/connectioninfo/FeatureIndicatorsPanel.kt index b15c8e5473..f41634048b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/FeatureIndicatorsPanel.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/connectioninfo/FeatureIndicatorsPanel.kt @@ -1,7 +1,7 @@ @file:OptIn(ExperimentalSharedTransitionApi::class) @file:Suppress("DEPRECATION") -package net.mullvad.mullvadvpn.compose.component.connectioninfo +package net.mullvad.mullvadvpn.feature.home.impl.connect.connectioninfo import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.core.EaseInQuart @@ -20,12 +20,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.LocalNavAnimatedVisibilityScope -import net.mullvad.mullvadvpn.compose.screen.LocalSharedTransitionScope +import net.mullvad.mullvadvpn.common.compose.LocalNavAnimatedVisibilityScope +import net.mullvad.mullvadvpn.common.compose.LocalSharedTransitionScope import net.mullvad.mullvadvpn.lib.model.FeatureIndicator import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadFeatureChip import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadMoreChip +import net.mullvad.mullvadvpn.lib.ui.resource.R import net.mullvad.mullvadvpn.lib.ui.theme.Dimens @Composable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/InAppNotificationController.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/notificationbanner/InAppNotificationController.kt index 3a74348e84..1633686c63 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/InAppNotificationController.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/notificationbanner/InAppNotificationController.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.repository +package net.mullvad.mullvadvpn.feature.home.impl.connect.notificationbanner import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationBanner.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/notificationbanner/NotificationBanner.kt index 67bcb1dfa5..cdc2819d7b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationBanner.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/notificationbanner/NotificationBanner.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.component.notificationbanner +package net.mullvad.mullvadvpn.feature.home.impl.connect.notificationbanner import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/DeviceRevokedLoginButton.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedLoginButton.kt index fbf360d3ba..ca86e3ae3e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/DeviceRevokedLoginButton.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedLoginButton.kt @@ -1,11 +1,10 @@ -package net.mullvad.mullvadvpn.compose.button +package net.mullvad.mullvadvpn.feature.home.impl.devicerevoked import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.state.DeviceRevokedUiState import net.mullvad.mullvadvpn.lib.ui.designsystem.NegativeButton import net.mullvad.mullvadvpn.lib.ui.designsystem.VariantButton +import net.mullvad.mullvadvpn.lib.ui.resource.R @Composable fun DeviceRevokedLoginButton(onClick: () -> Unit, state: DeviceRevokedUiState) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedScreen.kt index b1c5f491c5..5621faaa11 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedScreen.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen +package net.mullvad.mullvadvpn.feature.home.impl.devicerevoked import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -21,23 +21,19 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.dropUnlessResumed +import androidx.navigation.NavController import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.generated.NavGraphs -import com.ramcosta.composedestinations.generated.destinations.LoginDestination +import com.ramcosta.composedestinations.annotation.ExternalModuleGraph +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 import net.mullvad.mullvadvpn.common.compose.CollectSideEffectWithLifecycle -import net.mullvad.mullvadvpn.compose.button.DeviceRevokedLoginButton -import net.mullvad.mullvadvpn.compose.preview.DeviceRevokedUiStatePreviewParameterProvider -import net.mullvad.mullvadvpn.compose.state.DeviceRevokedUiState import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithTopBar import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar +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.AlphaScrollbar -import net.mullvad.mullvadvpn.viewmodel.DeviceRevokedSideEffect -import net.mullvad.mullvadvpn.viewmodel.DeviceRevokedViewModel import org.koin.androidx.compose.koinViewModel @Preview("Secured|Unsecured|Unknown") @@ -49,9 +45,9 @@ private fun PreviewDeviceRevokedScreen( AppTheme { DeviceRevokedScreen(state = state, onSettingsClicked = {}, onGoToLoginClicked = {}) } } -@Destination<MainGraph> +@Destination<ExternalModuleGraph> @Composable -fun DeviceRevoked(navigator: DestinationsNavigator) { +fun DeviceRevoked(navController: NavController, navigator: DestinationsNavigator) { val viewModel = koinViewModel<DeviceRevokedViewModel>() val state by viewModel.uiState.collectAsStateWithLifecycle() @@ -59,9 +55,9 @@ fun DeviceRevoked(navigator: DestinationsNavigator) { CollectSideEffectWithLifecycle(viewModel.uiSideEffect) { sideEffect -> when (sideEffect) { DeviceRevokedSideEffect.NavigateToLogin -> - navigator.navigate(LoginDestination()) { + navController.navigate(LoginDestination.baseRoute) { launchSingleTop = true - popUpTo(NavGraphs.main) { inclusive = true } + popUpTo("main") { inclusive = true } } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeviceRevokedUiState.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedUiState.kt index 4bc32ae757..9c42c854a6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeviceRevokedUiState.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedUiState.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.state +package net.mullvad.mullvadvpn.feature.home.impl.devicerevoked enum class DeviceRevokedUiState { SECURED, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DeviceRevokedUiStatePreviewParameterProvider.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedUiStatePreviewParameterProvider.kt index 018a160686..a5f8715f62 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/DeviceRevokedUiStatePreviewParameterProvider.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedUiStatePreviewParameterProvider.kt @@ -1,7 +1,6 @@ -package net.mullvad.mullvadvpn.compose.preview +package net.mullvad.mullvadvpn.feature.home.impl.devicerevoked import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import net.mullvad.mullvadvpn.compose.state.DeviceRevokedUiState class DeviceRevokedUiStatePreviewParameterProvider : PreviewParameterProvider<DeviceRevokedUiState> { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModel.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedViewModel.kt index 14a8de9f6b..32888ec86a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModel.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedViewModel.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel +package net.mullvad.mullvadvpn.feature.home.impl.devicerevoked import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -10,12 +10,11 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.receiveAsFlow 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/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/outoftime/OutOfTimeScreen.kt index 41cf615665..9993c06edf 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/outoftime/OutOfTimeScreen.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen +package net.mullvad.mullvadvpn.feature.home.impl.outoftime import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -37,32 +37,30 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.dropUnlessResumed +import androidx.navigation.NavController import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.generated.NavGraphs +import com.ramcosta.composedestinations.annotation.ExternalModuleGraph +import com.ramcosta.composedestinations.generated.account.destinations.AccountDestination import com.ramcosta.composedestinations.generated.addtime.destinations.VerificationPendingDestination -import com.ramcosta.composedestinations.generated.destinations.AccountDestination -import com.ramcosta.composedestinations.generated.destinations.ConnectDestination +import com.ramcosta.composedestinations.generated.home.destinations.ConnectDestination import com.ramcosta.composedestinations.generated.redeemvoucher.destinations.RedeemVoucherDestination import com.ramcosta.composedestinations.generated.settings.destinations.SettingsDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.common.compose.CollectSideEffectWithLifecycle import net.mullvad.mullvadvpn.common.compose.createOpenAccountPageHook import net.mullvad.mullvadvpn.common.compose.showSnackbarImmediately -import net.mullvad.mullvadvpn.compose.preview.OutOfTimeScreenPreviewParameterProvider -import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState -import net.mullvad.mullvadvpn.compose.transitions.HomeTransition import net.mullvad.mullvadvpn.feature.addtime.impl.AddTimeBottomSheet +import net.mullvad.mullvadvpn.feature.home.impl.HomeTransition import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithTopBarAndDeviceName import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar import net.mullvad.mullvadvpn.lib.ui.designsystem.NegativeButton import net.mullvad.mullvadvpn.lib.ui.designsystem.VariantButton +import net.mullvad.mullvadvpn.lib.ui.resource.R import net.mullvad.mullvadvpn.lib.ui.tag.OUT_OF_TIME_SCREEN_TITLE_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.tag.PLAY_PAYMENT_INFO_ICON_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme import net.mullvad.mullvadvpn.lib.ui.theme.Dimens import net.mullvad.mullvadvpn.lib.ui.theme.color.AlphaScrollbar -import net.mullvad.mullvadvpn.viewmodel.OutOfTimeViewModel import org.koin.androidx.compose.koinViewModel @Preview("Disconnected|Connecting|Error") @@ -83,9 +81,9 @@ private fun PreviewOutOfTimeScreen( } } -@Destination<MainGraph>(style = HomeTransition::class) +@Destination<ExternalModuleGraph>(style = HomeTransition::class) @Composable -fun OutOfTime(navigator: DestinationsNavigator) { +fun OutOfTime(navController: NavController, navigator: DestinationsNavigator) { val vm = koinViewModel<OutOfTimeViewModel>() val state by vm.uiState.collectAsStateWithLifecycle() @@ -97,9 +95,9 @@ fun OutOfTime(navigator: DestinationsNavigator) { is OutOfTimeViewModel.UiSideEffect.OpenAccountView -> openAccountPage(uiSideEffect.token) OutOfTimeViewModel.UiSideEffect.OpenConnectScreen -> - navigator.navigate(ConnectDestination) { + navController.navigate(ConnectDestination.route) { launchSingleTop = true - popUpTo(NavGraphs.main) { inclusive = true } + popUpTo("main") { inclusive = true } } OutOfTimeViewModel.UiSideEffect.GenericError -> snackbarHostState.showSnackbarImmediately( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/OutOfTimeScreenPreviewParameterProvider.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/outoftime/OutOfTimeScreenPreviewParameterProvider.kt index 91b05970a5..6927199ff2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/OutOfTimeScreenPreviewParameterProvider.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/outoftime/OutOfTimeScreenPreviewParameterProvider.kt @@ -1,10 +1,9 @@ -package net.mullvad.mullvadvpn.compose.preview +package net.mullvad.mullvadvpn.feature.home.impl.outoftime import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateConnectingState -import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateDisconnectedState -import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateErrorState -import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState +import net.mullvad.mullvadvpn.feature.home.impl.TunnelStatePreviewData.generateConnectingState +import net.mullvad.mullvadvpn.feature.home.impl.TunnelStatePreviewData.generateDisconnectedState +import net.mullvad.mullvadvpn.feature.home.impl.TunnelStatePreviewData.generateErrorState class OutOfTimeScreenPreviewParameterProvider : PreviewParameterProvider<OutOfTimeUiState> { override val values: Sequence<OutOfTimeUiState> = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/OutOfTimeUiState.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/outoftime/OutOfTimeUiState.kt index 89708d95eb..993bf60381 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/OutOfTimeUiState.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/outoftime/OutOfTimeUiState.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.state +package net.mullvad.mullvadvpn.feature.home.impl.outoftime import net.mullvad.mullvadvpn.lib.model.TunnelState diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/outoftime/OutOfTimeViewModel.kt index 79556f982b..454ca48625 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/outoftime/OutOfTimeViewModel.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel +package net.mullvad.mullvadvpn.feature.home.impl.outoftime import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -13,7 +13,6 @@ import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState import net.mullvad.mullvadvpn.feature.addtime.impl.hasPendingPayment import net.mullvad.mullvadvpn.feature.addtime.impl.isSuccess import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/info/DeviceNameInfoDialog.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/DeviceNameInfoDialog.kt index 9a8aad66d3..5ff3fd6dda 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/info/DeviceNameInfoDialog.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/DeviceNameInfoDialog.kt @@ -1,16 +1,16 @@ -package net.mullvad.mullvadvpn.compose.dialog.info +package net.mullvad.mullvadvpn.feature.home.impl.welcome import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.dropUnlessResumed import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.ExternalModuleGraph import com.ramcosta.composedestinations.navigation.DestinationsNavigator 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.InfoDialog +import net.mullvad.mullvadvpn.lib.ui.resource.R -@Destination<MainGraph>(style = DestinationStyle.Dialog::class) +@Destination<ExternalModuleGraph>(style = DestinationStyle.Dialog::class) @Composable fun DeviceNameInfo(navigator: DestinationsNavigator) { InfoDialog( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/WelcomeScreen.kt index 7a4348ee81..38ef6721ba 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/WelcomeScreen.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen +package net.mullvad.mullvadvpn.feature.home.impl.welcome import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -43,26 +43,24 @@ import androidx.credentials.exceptions.CreateCredentialException import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.dropUnlessResumed +import androidx.navigation.NavController import co.touchlab.kermit.Logger import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.generated.NavGraphs +import com.ramcosta.composedestinations.annotation.ExternalModuleGraph +import com.ramcosta.composedestinations.generated.account.destinations.AccountDestination import com.ramcosta.composedestinations.generated.addtime.destinations.VerificationPendingDestination -import com.ramcosta.composedestinations.generated.destinations.AccountDestination -import com.ramcosta.composedestinations.generated.destinations.ConnectDestination -import com.ramcosta.composedestinations.generated.destinations.DeviceNameInfoDestination +import com.ramcosta.composedestinations.generated.home.destinations.ConnectDestination +import com.ramcosta.composedestinations.generated.home.destinations.DeviceNameInfoDestination import com.ramcosta.composedestinations.generated.redeemvoucher.destinations.RedeemVoucherDestination import com.ramcosta.composedestinations.generated.settings.destinations.SettingsDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.common.compose.CollectSideEffectWithLifecycle import net.mullvad.mullvadvpn.common.compose.createCopyToClipboardHandle import net.mullvad.mullvadvpn.common.compose.createOpenAccountPageHook import net.mullvad.mullvadvpn.common.compose.showSnackbarImmediately -import net.mullvad.mullvadvpn.compose.component.CopyAnimatedIconButton -import net.mullvad.mullvadvpn.compose.preview.WelcomeScreenUiStatePreviewParameterProvider -import net.mullvad.mullvadvpn.compose.state.WelcomeUiState -import net.mullvad.mullvadvpn.compose.transitions.HomeTransition +import net.mullvad.mullvadvpn.feature.account.impl.CopyAnimatedIconButton import net.mullvad.mullvadvpn.feature.addtime.impl.AddTimeBottomSheet +import net.mullvad.mullvadvpn.feature.home.impl.HomeTransition import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.common.util.groupWithSpaces import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithTopBar @@ -70,11 +68,11 @@ import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorMedium import net.mullvad.mullvadvpn.lib.ui.designsystem.NegativeButton import net.mullvad.mullvadvpn.lib.ui.designsystem.VariantButton +import net.mullvad.mullvadvpn.lib.ui.resource.R import net.mullvad.mullvadvpn.lib.ui.tag.PLAY_PAYMENT_INFO_ICON_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme import net.mullvad.mullvadvpn.lib.ui.theme.Dimens import net.mullvad.mullvadvpn.lib.ui.theme.color.AlphaScrollbar -import net.mullvad.mullvadvpn.viewmodel.WelcomeViewModel import org.koin.androidx.compose.koinViewModel @Preview("Loading|Content|TunnelConnected") @@ -96,9 +94,9 @@ private fun PreviewWelcomeScreen( } } -@Destination<MainGraph>(style = HomeTransition::class) +@Destination<ExternalModuleGraph>(style = HomeTransition::class) @Composable -fun Welcome(navigator: DestinationsNavigator) { +fun Welcome(navController: NavController, navigator: DestinationsNavigator) { val vm = koinViewModel<WelcomeViewModel>() val state by vm.uiState.collectAsStateWithLifecycle() @@ -111,9 +109,9 @@ fun Welcome(navigator: DestinationsNavigator) { when (uiSideEffect) { is WelcomeViewModel.UiSideEffect.OpenAccountView -> openAccountPage(uiSideEffect.token) WelcomeViewModel.UiSideEffect.OpenConnectScreen -> - navigator.navigate(ConnectDestination) { + navController.navigate(ConnectDestination.route) { launchSingleTop = true - popUpTo(NavGraphs.main) { inclusive = true } + popUpTo("main") { inclusive = true } } WelcomeViewModel.UiSideEffect.GenericError -> snackbarHostState.showSnackbarImmediately( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/WelcomeScreenUiStatePreviewParameterProvider.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/WelcomeScreenUiStatePreviewParameterProvider.kt index 129d866daa..4f5ef3745c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/WelcomeScreenUiStatePreviewParameterProvider.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/WelcomeScreenUiStatePreviewParameterProvider.kt @@ -1,7 +1,7 @@ -package net.mullvad.mullvadvpn.compose.preview +package net.mullvad.mullvadvpn.feature.home.impl.welcome import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import net.mullvad.mullvadvpn.compose.state.WelcomeUiState +import net.mullvad.mullvadvpn.feature.home.impl.TunnelStatePreviewData import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.common.toLc import net.mullvad.mullvadvpn.lib.model.AccountNumber diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/WelcomeUiState.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/WelcomeUiState.kt index 880c2b9dcf..64801e5db8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/WelcomeUiState.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/WelcomeUiState.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.state +package net.mullvad.mullvadvpn.feature.home.impl.welcome import net.mullvad.mullvadvpn.lib.model.AccountNumber import net.mullvad.mullvadvpn.lib.model.TunnelState diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/WelcomeViewModel.kt index dd299ba6cc..9ec73dd6fe 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt +++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/WelcomeViewModel.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel +package net.mullvad.mullvadvpn.feature.home.impl.welcome import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -16,7 +16,6 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import net.mullvad.mullvadvpn.compose.state.WelcomeUiState import net.mullvad.mullvadvpn.feature.addtime.impl.hasPendingPayment import net.mullvad.mullvadvpn.feature.addtime.impl.isSuccess import net.mullvad.mullvadvpn.lib.common.Lc diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModelTest.kt index 3956056f48..c1147e9ae7 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt +++ b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModelTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel +package net.mullvad.mullvadvpn.feature.home.impl.connect import androidx.lifecycle.viewModelScope import app.cash.turbine.test @@ -18,7 +18,7 @@ import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest -import net.mullvad.mullvadvpn.compose.state.ConnectUiState +import net.mullvad.mullvadvpn.feature.home.impl.connect.notificationbanner.InAppNotificationController import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.lib.model.AccountData import net.mullvad.mullvadvpn.lib.model.DeviceState @@ -37,9 +37,6 @@ import net.mullvad.mullvadvpn.lib.usecase.LastKnownLocationUseCase import net.mullvad.mullvadvpn.lib.usecase.OutOfTimeUseCase import net.mullvad.mullvadvpn.lib.usecase.SelectedLocationTitleUseCase import net.mullvad.mullvadvpn.lib.usecase.SystemVpnSettingsAvailableUseCase -import net.mullvad.mullvadvpn.repository.InAppNotificationController -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -47,12 +44,8 @@ import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(TestCoroutineRule::class) class ConnectViewModelTest { - - private val mockServiceConnectionManager: ServiceConnectionManager = mockk() private lateinit var viewModel: ConnectViewModel - private val serviceConnectionState = - MutableStateFlow<ServiceConnectionState>(ServiceConnectionState.Unbound) private val accountExpiryState = MutableStateFlow<AccountData?>(null) private val device = MutableStateFlow<DeviceState?>(null) private val notifications = MutableStateFlow<List<InAppNotification>>(emptyList()) @@ -96,8 +89,6 @@ class ConnectViewModelTest { @BeforeEach fun setup() { - every { mockServiceConnectionManager.connectionState } returns serviceConnectionState - every { mockAccountRepository.accountData } returns accountExpiryState every { mockDeviceRepository.deviceState } returns device diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/notificationbanner/InAppNotificationControllerTest.kt index 3e4c0be96f..442a2fb661 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt +++ b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/notificationbanner/InAppNotificationControllerTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn +package net.mullvad.mullvadvpn.feature.home.impl.connect.notificationbanner import app.cash.turbine.test import io.mockk.MockKAnnotations @@ -21,7 +21,6 @@ import net.mullvad.mullvadvpn.lib.usecase.inappnotification.NewChangelogNotifica import net.mullvad.mullvadvpn.lib.usecase.inappnotification.NewDeviceNotificationUseCase import net.mullvad.mullvadvpn.lib.usecase.inappnotification.TunnelStateNotificationUseCase import net.mullvad.mullvadvpn.lib.usecase.inappnotification.VersionNotificationUseCase -import net.mullvad.mullvadvpn.repository.InAppNotificationController import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/data/AccountData.kt b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/data/AccountData.kt new file mode 100644 index 0000000000..121ff29ee4 --- /dev/null +++ b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/data/AccountData.kt @@ -0,0 +1,12 @@ +package net.mullvad.mullvadvpn.feature.home.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/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedViewModelTest.kt index c71a6efa4b..306cc13555 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt +++ b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/devicerevoked/DeviceRevokedViewModelTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel +package net.mullvad.mullvadvpn.feature.home.impl.devicerevoked import app.cash.turbine.test import arrow.core.right @@ -10,18 +10,17 @@ import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.mockk import io.mockk.unmockkAll +import kotlin.test.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow 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 import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/outoftime/OutOfTimeViewModelTest.kt index 1b46f83870..db018630bd 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt +++ b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/outoftime/OutOfTimeViewModelTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel +package net.mullvad.mullvadvpn.feature.home.impl.outoftime import androidx.lifecycle.viewModelScope import app.cash.turbine.test @@ -13,7 +13,6 @@ import kotlin.test.assertIs import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest -import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.lib.model.AccountData import net.mullvad.mullvadvpn.lib.model.DeviceState @@ -30,8 +29,6 @@ import net.mullvad.mullvadvpn.lib.repository.ConnectionProxy import net.mullvad.mullvadvpn.lib.repository.DeviceRepository import net.mullvad.mullvadvpn.lib.repository.PaymentLogic import net.mullvad.mullvadvpn.lib.usecase.OutOfTimeUseCase -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -40,8 +37,6 @@ import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(TestCoroutineRule::class) class OutOfTimeViewModelTest { - private val serviceConnectionStateFlow = - MutableStateFlow<ServiceConnectionState>(ServiceConnectionState.Unbound) private val accountExpiryStateFlow = MutableStateFlow<AccountData?>(null) private val accountStateFlow = MutableStateFlow<DeviceState?>(null) private val paymentAvailabilityFlow = MutableStateFlow<PaymentAvailability?>(null) @@ -56,7 +51,6 @@ class OutOfTimeViewModelTest { private val mockAccountRepository: AccountRepository = mockk(relaxed = true) private val mockDeviceRepository: DeviceRepository = mockk(relaxed = true) - private val mockServiceConnectionManager: ServiceConnectionManager = mockk() private val mockPaymentUseCase: PaymentLogic = mockk(relaxed = true) private val mockOutOfTimeUseCase: OutOfTimeUseCase = mockk(relaxed = true) @@ -64,8 +58,6 @@ class OutOfTimeViewModelTest { @BeforeEach fun setup() { - every { mockServiceConnectionManager.connectionState } returns serviceConnectionStateFlow - every { mockConnectionProxy.tunnelState } returns tunnelState every { mockAccountRepository.accountData } returns accountExpiryStateFlow diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/WelcomeViewModelTest.kt index 48361180bd..86aeac41ae 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt +++ b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/welcome/WelcomeViewModelTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel +package net.mullvad.mullvadvpn.feature.home.impl.welcome import androidx.lifecycle.viewModelScope import app.cash.turbine.test @@ -14,8 +14,7 @@ import kotlin.test.assertIs import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest -import net.mullvad.mullvadvpn.compose.state.WelcomeUiState -import net.mullvad.mullvadvpn.data.mock +import net.mullvad.mullvadvpn.feature.home.impl.data.mock import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.lib.model.AccountData @@ -34,8 +33,6 @@ import net.mullvad.mullvadvpn.lib.repository.AccountRepository import net.mullvad.mullvadvpn.lib.repository.ConnectionProxy import net.mullvad.mullvadvpn.lib.repository.DeviceRepository import net.mullvad.mullvadvpn.lib.repository.PaymentLogic -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -44,8 +41,6 @@ import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(TestCoroutineRule::class) class WelcomeViewModelTest { - private val serviceConnectionStateFlow = - MutableStateFlow<ServiceConnectionState>(ServiceConnectionState.Unbound) private val deviceStateFlow = MutableStateFlow<DeviceState?>(DeviceState.LoggedOut) private val accountExpiryStateFlow = MutableStateFlow<AccountData?>(null) private val purchaseResultFlow = MutableStateFlow<PurchaseResult?>(null) @@ -59,7 +54,6 @@ class WelcomeViewModelTest { private val mockAccountRepository: AccountRepository = mockk(relaxed = true) private val mockDeviceRepository: DeviceRepository = mockk(relaxed = true) - private val mockServiceConnectionManager: ServiceConnectionManager = mockk() private val mockPaymentUseCase: PaymentLogic = mockk(relaxed = true) private lateinit var viewModel: WelcomeViewModel @@ -68,8 +62,6 @@ class WelcomeViewModelTest { fun setup() { every { mockDeviceRepository.deviceState } returns deviceStateFlow - every { mockServiceConnectionManager.connectionState } returns serviceConnectionStateFlow - every { mockConnectionProxy.tunnelState } returns tunnelState every { mockAccountRepository.accountData } returns accountExpiryStateFlow diff --git a/android/lib/feature/location/impl/build.gradle.kts b/android/lib/feature/location/impl/build.gradle.kts new file mode 100644 index 0000000000..852dae4b98 --- /dev/null +++ b/android/lib/feature/location/impl/build.gradle.kts @@ -0,0 +1,30 @@ +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.location.impl" + ksp { arg("compose-destinations.moduleName", "location") } +} + +dependencies { + implementation(projects.lib.ui.icon) + implementation(projects.lib.repository) + implementation(projects.lib.usecase) + implementation(projects.lib.feature.customlist.impl) + implementation(projects.lib.feature.daita.impl) + implementation(projects.lib.feature.filter.impl) + + implementation(libs.compose.constrainlayout) + implementation(libs.koin.compose) + implementation(libs.arrow) + + // Destinations + implementation(libs.compose.destinations) + ksp(libs.compose.destinations.ksp) +} diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreenTest.kt b/android/lib/feature/location/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationScreenTest.kt index 5faaa3d3de..4ac721d03a 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreenTest.kt +++ b/android/lib/feature/location/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationScreenTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen.location +package net.mullvad.mullvadvpn.feature.location.impl import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.hasAnyAncestor @@ -17,10 +17,11 @@ import kotlinx.coroutines.flow.MutableStateFlow import net.mullvad.mullvadvpn.compose.data.DUMMY_RELAY_COUNTRIES import net.mullvad.mullvadvpn.compose.data.DUMMY_RELAY_ITEM_CUSTOM_LISTS import net.mullvad.mullvadvpn.compose.data.createSimpleRelayListItemList -import net.mullvad.mullvadvpn.compose.state.LocationBottomSheetUiState -import net.mullvad.mullvadvpn.compose.state.SelectLocationListUiState -import net.mullvad.mullvadvpn.compose.state.SelectLocationUiState -import net.mullvad.mullvadvpn.compose.state.SetAsState +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.LocationBottomSheetUiState +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.LocationBottomSheetViewModel +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.SetAsState +import net.mullvad.mullvadvpn.feature.location.impl.list.SelectLocationListUiState +import net.mullvad.mullvadvpn.feature.location.impl.list.SelectLocationListViewModel import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.common.Lce import net.mullvad.mullvadvpn.lib.model.CustomListId @@ -41,9 +42,6 @@ import net.mullvad.mullvadvpn.onNodeWithTagAndText import net.mullvad.mullvadvpn.performLongClick import net.mullvad.mullvadvpn.screen.test.createEdgeToEdgeComposeExtension import net.mullvad.mullvadvpn.screen.test.setContentWithTheme -import net.mullvad.mullvadvpn.viewmodel.location.LocationBottomSheetViewModel -import net.mullvad.mullvadvpn.viewmodel.location.SelectLocationListViewModel -import net.mullvad.mullvadvpn.viewmodel.location.UndoChangeMultihopAction import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreenTest.kt b/android/lib/feature/location/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationScreenTest.kt index 7775b7fcce..936eaa18cf 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreenTest.kt +++ b/android/lib/feature/location/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationScreenTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen.location +package net.mullvad.mullvadvpn.feature.location.impl.search import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag @@ -10,7 +10,7 @@ import io.mockk.mockk import io.mockk.unmockkAll import io.mockk.verify import net.mullvad.mullvadvpn.compose.data.DUMMY_RELAY_ITEM_CUSTOM_LISTS -import net.mullvad.mullvadvpn.compose.state.SearchLocationUiState +import net.mullvad.mullvadvpn.feature.location.impl.UndoChangeMultihopAction import net.mullvad.mullvadvpn.lib.common.Lce import net.mullvad.mullvadvpn.lib.model.CustomListId import net.mullvad.mullvadvpn.lib.model.RelayItem @@ -23,7 +23,6 @@ import net.mullvad.mullvadvpn.lib.usecase.MultihopChange import net.mullvad.mullvadvpn.lib.usecase.SelectRelayItemError import net.mullvad.mullvadvpn.screen.test.createEdgeToEdgeComposeExtension import net.mullvad.mullvadvpn.screen.test.setContentWithTheme -import net.mullvad.mullvadvpn.viewmodel.location.UndoChangeMultihopAction import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/android/lib/feature/location/impl/src/main/AndroidManifest.xml b/android/lib/feature/location/impl/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..8bdb7e14b3 --- /dev/null +++ b/android/lib/feature/location/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/constant/ContentType.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/ContentType.kt index 47b6ba7923..e520b095c2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/constant/ContentType.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/ContentType.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.constant +package net.mullvad.mullvadvpn.feature.location.impl // Content types, to improve the ability to reuse views object ContentType { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/CustomListEdit.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/CustomListEdit.kt index f723414ab4..6d7083c63f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/CustomListEdit.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/CustomListEdit.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel.location +package net.mullvad.mullvadvpn.feature.location.impl import arrow.core.Either import arrow.core.getOrElse diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/EmptyRelayListText.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/EmptyRelayListText.kt index e7aa8ba3d6..a81c150e8e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/EmptyRelayListText.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/EmptyRelayListText.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.component +package net.mullvad.mullvadvpn.feature.location.impl import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding @@ -8,7 +8,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.lib.ui.theme.Dimens @Composable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/Expand.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/Expand.kt index 726aa79674..c57ebc3a64 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/Expand.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/Expand.kt @@ -1,7 +1,8 @@ -package net.mullvad.mullvadvpn.viewmodel.location +package net.mullvad.mullvadvpn.feature.location.impl import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update +import net.mullvad.mullvadvpn.feature.location.impl.search.expandKey import net.mullvad.mullvadvpn.lib.model.CustomListId import net.mullvad.mullvadvpn.lib.model.RelayItemId diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/FilterRow.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/FilterRow.kt index 46285f3eca..6cca9012c7 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/FilterRow.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/FilterRow.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.cell +package net.mullvad.mullvadvpn.feature.location.impl import androidx.compose.foundation.background import androidx.compose.foundation.horizontalScroll @@ -15,7 +15,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.lib.model.Ownership import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadFilterChip import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/RelayListContent.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/RelayListContent.kt index e7f5465781..9c6a056d82 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/RelayListContent.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/RelayListContent.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen.location +package net.mullvad.mullvadvpn.feature.location.impl import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -20,11 +20,7 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.component.EmptyRelayListText -import net.mullvad.mullvadvpn.compose.screen.location.LocationBottomSheetState.ShowCustomListsEntryBottomSheet -import net.mullvad.mullvadvpn.compose.screen.location.LocationBottomSheetState.ShowEditCustomListBottomSheet -import net.mullvad.mullvadvpn.compose.screen.location.LocationBottomSheetState.ShowLocationBottomSheet +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.LocationBottomSheetState import net.mullvad.mullvadvpn.lib.model.CustomListId import net.mullvad.mullvadvpn.lib.model.RelayItem import net.mullvad.mullvadvpn.lib.model.RelayItemId @@ -138,7 +134,10 @@ private fun GeoLocationItem( onClick = { onSelect(listItem.item) }, onLongClick = { onUpdateBottomSheetState( - ShowLocationBottomSheet(item = listItem.item, relayListType = relayListType) + LocationBottomSheetState.ShowLocationBottomSheet( + item = listItem.item, + relayListType = relayListType, + ) ) }, onToggleExpand = { onToggleExpand(listItem.item.id, null, it) }, @@ -178,11 +177,17 @@ private fun RecentListItem( when (val entry = listItem.item) { is RelayItem.CustomList -> onUpdateBottomSheetState( - ShowEditCustomListBottomSheet(item = entry, relayListType = relayListType) + LocationBottomSheetState.ShowEditCustomListBottomSheet( + item = entry, + relayListType = relayListType, + ) ) is RelayItem.Location -> onUpdateBottomSheetState( - ShowLocationBottomSheet(item = entry, relayListType = relayListType) + LocationBottomSheetState.ShowLocationBottomSheet( + item = entry, + relayListType = relayListType, + ) ) } }, @@ -202,7 +207,10 @@ private fun CustomListItem( onClick = { onSelect(listItem.item) }, onLongClick = { onUpdateBottomSheetState( - ShowEditCustomListBottomSheet(item = listItem.item, relayListType = relayListType) + LocationBottomSheetState.ShowEditCustomListBottomSheet( + item = listItem.item, + relayListType = relayListType, + ) ) }, onToggleExpand = { onToggleExpand(listItem.item.id, null, it) }, @@ -226,7 +234,7 @@ private fun CustomListEntryItem( if (listItem.depth == 1) { { onUpdateBottomSheetState( - ShowCustomListsEntryBottomSheet( + LocationBottomSheetState.ShowCustomListsEntryBottomSheet( customListId = listItem.parentId, item = listItem.item, relayListType = relayListType, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/RelayListScrollConnection.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/RelayListScrollConnection.kt index 1dadd863e5..3811aeabbb 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/RelayListScrollConnection.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/RelayListScrollConnection.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen.location +package net.mullvad.mullvadvpn.feature.location.impl import kotlinx.coroutines.channels.Channel import net.mullvad.mullvadvpn.lib.model.RelayItem diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreen.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationScreen.kt index 7f5c1c447d..0c74ee46d5 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreen.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationScreen.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen.location +package net.mullvad.mullvadvpn.feature.location.impl import android.annotation.SuppressLint import androidx.compose.animation.AnimatedContent @@ -69,30 +69,31 @@ import androidx.constraintlayout.compose.layoutId import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.dropUnlessResumed import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.ExternalModuleGraph import com.ramcosta.composedestinations.generated.customlist.destinations.CreateCustomListDestination import com.ramcosta.composedestinations.generated.customlist.destinations.CustomListLocationsDestination import com.ramcosta.composedestinations.generated.customlist.destinations.CustomListsDestination import com.ramcosta.composedestinations.generated.customlist.destinations.DeleteCustomListDestination import com.ramcosta.composedestinations.generated.customlist.destinations.EditCustomListNameDestination import com.ramcosta.composedestinations.generated.daita.destinations.DaitaDestination -import com.ramcosta.composedestinations.generated.destinations.SearchLocationDestination import com.ramcosta.composedestinations.generated.filter.destinations.FilterDestination +import com.ramcosta.composedestinations.generated.location.destinations.SearchLocationDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.result.ResultBackNavigator import com.ramcosta.composedestinations.result.ResultRecipient import com.ramcosta.composedestinations.result.onResult import kotlin.math.roundToInt 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.isTv import net.mullvad.mullvadvpn.common.compose.showSnackbarImmediately -import net.mullvad.mullvadvpn.compose.cell.FilterRow -import net.mullvad.mullvadvpn.compose.preview.SelectLocationsUiStatePreviewParameterProvider -import net.mullvad.mullvadvpn.compose.screen.MainGraph -import net.mullvad.mullvadvpn.compose.state.SelectLocationUiState import net.mullvad.mullvadvpn.core.animation.TopLevelTransition +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.LocationBottomSheetState +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.LocationBottomSheets +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.OnCustomListNavResult +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.showResultSnackbar +import net.mullvad.mullvadvpn.feature.location.impl.list.SelectLocationList import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.model.Constraint import net.mullvad.mullvadvpn.lib.model.CustomListId @@ -118,9 +119,6 @@ import net.mullvad.mullvadvpn.lib.usecase.FilterChip import net.mullvad.mullvadvpn.lib.usecase.ModifyMultihopError import net.mullvad.mullvadvpn.lib.usecase.MultihopChange import net.mullvad.mullvadvpn.lib.usecase.SelectRelayItemError -import net.mullvad.mullvadvpn.viewmodel.location.SelectLocationSideEffect -import net.mullvad.mullvadvpn.viewmodel.location.SelectLocationViewModel -import net.mullvad.mullvadvpn.viewmodel.location.UndoChangeMultihopAction import org.koin.androidx.compose.koinViewModel val SCROLL_COLLAPSE_DISTANCE = 150.dp @@ -164,7 +162,7 @@ private fun PreviewSelectLocationScreen( } @SuppressLint("CheckResult") -@Destination<MainGraph>(style = TopLevelTransition::class) +@Destination<ExternalModuleGraph>(style = TopLevelTransition::class) @Suppress("LongMethod", "CyclomaticComplexMethod") @Composable fun SelectLocation( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationUiState.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationUiState.kt index 22c6effd30..6600618a10 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationUiState.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationUiState.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.state +package net.mullvad.mullvadvpn.feature.location.impl import net.mullvad.mullvadvpn.lib.model.ErrorStateCause import net.mullvad.mullvadvpn.lib.model.HopSelection diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModel.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationViewModel.kt index da613131af..41eb8c522e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModel.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationViewModel.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel.location +package net.mullvad.mullvadvpn.feature.location.impl import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -14,9 +14,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import net.mullvad.mullvadvpn.compose.screen.location.RelayListScrollConnection -import net.mullvad.mullvadvpn.compose.screen.location.ScrollEvent -import net.mullvad.mullvadvpn.compose.state.SelectLocationUiState import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT import net.mullvad.mullvadvpn.lib.common.util.combine diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/SelectLocationsUiStatePreviewParameterProvider.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationsUiStatePreviewParameterProvider.kt index 92f2ac162b..02ac2cecb2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/SelectLocationsUiStatePreviewParameterProvider.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationsUiStatePreviewParameterProvider.kt @@ -1,7 +1,6 @@ -package net.mullvad.mullvadvpn.compose.preview +package net.mullvad.mullvadvpn.feature.location.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import net.mullvad.mullvadvpn.compose.state.SelectLocationUiState import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.common.toLc import net.mullvad.mullvadvpn.lib.model.HopSelection diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/UndoChangeMultihopAction.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/UndoChangeMultihopAction.kt index f18d13046d..1365aeb453 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/UndoChangeMultihopAction.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/UndoChangeMultihopAction.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel.location +package net.mullvad.mullvadvpn.feature.location.impl import net.mullvad.mullvadvpn.lib.model.RelayItemId diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/LocationBottomSheet.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/bottomsheet/LocationBottomSheet.kt index 444f7b7321..43e78cd327 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/LocationBottomSheet.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/bottomsheet/LocationBottomSheet.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen.location +package net.mullvad.mullvadvpn.feature.location.impl.bottomsheet import android.content.res.Resources import androidx.compose.foundation.layout.Column @@ -42,10 +42,9 @@ import com.ramcosta.composedestinations.result.NavResult import com.ramcosta.composedestinations.result.ResultRecipient import com.ramcosta.composedestinations.spec.DestinationSpec import kotlinx.coroutines.launch -import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.common.compose.showSnackbarImmediately -import net.mullvad.mullvadvpn.compose.state.LocationBottomSheetUiState -import net.mullvad.mullvadvpn.compose.state.SetAsState +import net.mullvad.mullvadvpn.feature.location.impl.R +import net.mullvad.mullvadvpn.feature.location.impl.UndoChangeMultihopAction import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.common.util.relaylist.canAddLocation import net.mullvad.mullvadvpn.lib.model.CustomListId @@ -66,8 +65,6 @@ import net.mullvad.mullvadvpn.lib.ui.theme.Dimens import net.mullvad.mullvadvpn.lib.usecase.ModifyMultihopError import net.mullvad.mullvadvpn.lib.usecase.MultihopChange import net.mullvad.mullvadvpn.lib.usecase.SelectRelayItemError -import net.mullvad.mullvadvpn.viewmodel.location.LocationBottomSheetViewModel -import net.mullvad.mullvadvpn.viewmodel.location.UndoChangeMultihopAction import org.koin.androidx.compose.koinViewModel import org.koin.core.parameter.parametersOf diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/LocationBottomSheetUiState.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/bottomsheet/LocationBottomSheetUiState.kt index ec6e15ac30..26c0a015b7 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/LocationBottomSheetUiState.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/bottomsheet/LocationBottomSheetUiState.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.state +package net.mullvad.mullvadvpn.feature.location.impl.bottomsheet import net.mullvad.mullvadvpn.lib.model.CustomListId import net.mullvad.mullvadvpn.lib.model.CustomListName diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/LocationBottomSheetViewModel.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/bottomsheet/LocationBottomSheetViewModel.kt index b854a80a8c..81f211150c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/LocationBottomSheetViewModel.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/bottomsheet/LocationBottomSheetViewModel.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel.location +package net.mullvad.mullvadvpn.feature.location.impl.bottomsheet import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -12,9 +12,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.take import kotlinx.coroutines.launch -import net.mullvad.mullvadvpn.compose.screen.location.LocationBottomSheetState -import net.mullvad.mullvadvpn.compose.state.LocationBottomSheetUiState -import net.mullvad.mullvadvpn.compose.state.SetAsState +import net.mullvad.mullvadvpn.feature.location.impl.UndoChangeMultihopAction import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT import net.mullvad.mullvadvpn.lib.common.util.relaylist.withDescendants @@ -75,6 +73,7 @@ class LocationBottomSheetViewModel( ), ) ) + is LocationBottomSheetState.ShowEditCustomListBottomSheet -> Lc.Content( LocationBottomSheetUiState.CustomList( @@ -91,6 +90,7 @@ class LocationBottomSheetViewModel( locationBottomSheetState.item.id, ) ) + is LocationBottomSheetState.ShowLocationBottomSheet -> Lc.Content( LocationBottomSheetUiState.Location( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationList.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/list/SelectLocationList.kt index ac3ec1db9f..fb353910ee 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationList.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/list/SelectLocationList.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen.location +package net.mullvad.mullvadvpn.feature.location.impl.list import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues @@ -32,13 +32,13 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.flow.first -import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.common.compose.CollectSideEffectWithLifecycle import net.mullvad.mullvadvpn.common.compose.animateScrollAndCentralizeItem import net.mullvad.mullvadvpn.common.compose.animateScrollCentralizeItem -import net.mullvad.mullvadvpn.compose.constant.ContentType -import net.mullvad.mullvadvpn.compose.preview.SearchLocationsListUiStatePreviewParameterProvider -import net.mullvad.mullvadvpn.compose.state.SelectLocationListUiState +import net.mullvad.mullvadvpn.feature.location.impl.ContentType +import net.mullvad.mullvadvpn.feature.location.impl.CustomListHeader +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.LocationBottomSheetState +import net.mullvad.mullvadvpn.feature.location.impl.relayListContent import net.mullvad.mullvadvpn.lib.common.Lce import net.mullvad.mullvadvpn.lib.model.CustomListId import net.mullvad.mullvadvpn.lib.model.GeoLocationId @@ -49,18 +49,18 @@ import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar import net.mullvad.mullvadvpn.lib.ui.component.relaylist.RelayListItem import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryButton +import net.mullvad.mullvadvpn.lib.ui.resource.R import net.mullvad.mullvadvpn.lib.ui.tag.SELECT_LOCATION_LIST_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme import net.mullvad.mullvadvpn.lib.ui.theme.Dimens import net.mullvad.mullvadvpn.lib.ui.theme.color.AlphaScrollbar -import net.mullvad.mullvadvpn.viewmodel.location.SelectLocationListViewModel import org.koin.androidx.compose.koinViewModel import org.koin.core.parameter.parametersOf @Preview("Content|Loading|Error") @Composable private fun PreviewSelectLocationList( - @PreviewParameter(SearchLocationsListUiStatePreviewParameterProvider::class) + @PreviewParameter(SelectLocationsListUiStatePreviewParameterProvider::class) state: Lce<Unit, SelectLocationListUiState, Unit> ) { AppTheme { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationListUiState.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/list/SelectLocationListUiState.kt index 7f72218f7c..0f79b6d33b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationListUiState.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/list/SelectLocationListUiState.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.state +package net.mullvad.mullvadvpn.feature.location.impl.list import net.mullvad.mullvadvpn.lib.model.RelayItem import net.mullvad.mullvadvpn.lib.model.RelayListType diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModel.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/list/SelectLocationListViewModel.kt index 5a89db1e73..92c9e45049 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModel.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/list/SelectLocationListViewModel.kt @@ -1,7 +1,8 @@ -package net.mullvad.mullvadvpn.viewmodel.location +package net.mullvad.mullvadvpn.feature.location.impl.list import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -11,8 +12,12 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn -import net.mullvad.mullvadvpn.compose.screen.location.RelayListScrollConnection -import net.mullvad.mullvadvpn.compose.state.SelectLocationListUiState +import net.mullvad.mullvadvpn.feature.location.impl.RelayListScrollConnection +import net.mullvad.mullvadvpn.feature.location.impl.onToggleExpandSet +import net.mullvad.mullvadvpn.feature.location.impl.search.emptyLocationsRelayListItems +import net.mullvad.mullvadvpn.feature.location.impl.search.relayListItems +import net.mullvad.mullvadvpn.feature.location.impl.search.selectedByOtherEntryExitList +import net.mullvad.mullvadvpn.feature.location.impl.search.selectedByThisEntryExitList import net.mullvad.mullvadvpn.lib.common.Lce import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT import net.mullvad.mullvadvpn.lib.common.util.ignoreEntrySelection @@ -26,6 +31,7 @@ import net.mullvad.mullvadvpn.lib.model.RelayListType import net.mullvad.mullvadvpn.lib.repository.RelayListRepository import net.mullvad.mullvadvpn.lib.repository.SettingsRepository import net.mullvad.mullvadvpn.lib.repository.WireguardConstraintsRepository +import net.mullvad.mullvadvpn.lib.ui.component.relaylist.RelayListItem import net.mullvad.mullvadvpn.lib.usecase.FilteredRelayListUseCase import net.mullvad.mullvadvpn.lib.usecase.RecentsUseCase import net.mullvad.mullvadvpn.lib.usecase.SelectedLocationUseCase @@ -81,7 +87,7 @@ class SelectLocationListViewModel( _expandedItems.onToggleExpandSet(item, parent, expand) } - private fun relayListItems() = + private fun relayListItems(): Flow<List<RelayListItem>> = combine( filteredRelayListUseCase(relayListType = relayListType), filteredCustomListRelayItemsUseCase(relayListType = relayListType), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/SearchLocationsListUiStatePreviewParameterProvider.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/list/SelectLocationsListUiStatePreviewParameterProvider.kt index 78821247c0..a8d3160af2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/SearchLocationsListUiStatePreviewParameterProvider.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/list/SelectLocationsListUiStatePreviewParameterProvider.kt @@ -1,13 +1,12 @@ -package net.mullvad.mullvadvpn.compose.preview +package net.mullvad.mullvadvpn.feature.location.impl.list import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import net.mullvad.mullvadvpn.compose.state.SelectLocationListUiState import net.mullvad.mullvadvpn.lib.common.Lce import net.mullvad.mullvadvpn.lib.model.MultihopRelayListType import net.mullvad.mullvadvpn.lib.model.RelayListType import net.mullvad.mullvadvpn.lib.ui.component.relaylist.RelayListItemPreviewData -class SearchLocationsListUiStatePreviewParameterProvider : +class SelectLocationsListUiStatePreviewParameterProvider : PreviewParameterProvider<Lce<Unit, SelectLocationListUiState, Unit>> { override val values = sequenceOf( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/RelayItemListCreator.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/RelayItemListCreator.kt index 5eb1cafd3a..4ce6c6ff03 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/RelayItemListCreator.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/RelayItemListCreator.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel.location +package net.mullvad.mullvadvpn.feature.location.impl.search import net.mullvad.mullvadvpn.lib.common.util.relaylist.filterOnSearchTerm import net.mullvad.mullvadvpn.lib.model.CustomListId diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreen.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationScreen.kt index ef24d73bfb..40df302939 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreen.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationScreen.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.screen.location +package net.mullvad.mullvadvpn.feature.location.impl.search import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column @@ -44,6 +44,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.dropUnlessResumed import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.ExternalModuleGraph import com.ramcosta.composedestinations.generated.customlist.destinations.CreateCustomListDestination import com.ramcosta.composedestinations.generated.customlist.destinations.CustomListLocationsDestination import com.ramcosta.composedestinations.generated.customlist.destinations.DeleteCustomListDestination @@ -52,17 +53,19 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.result.ResultBackNavigator 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.cell.FilterRow -import net.mullvad.mullvadvpn.compose.component.EmptyRelayListText -import net.mullvad.mullvadvpn.compose.constant.ContentType -import net.mullvad.mullvadvpn.compose.preview.SearchLocationsUiStatePreviewParameterProvider -import net.mullvad.mullvadvpn.compose.screen.MainGraph -import net.mullvad.mullvadvpn.compose.state.SearchLocationUiState import net.mullvad.mullvadvpn.core.animation.TopLevelTransition +import net.mullvad.mullvadvpn.feature.location.impl.ContentType +import net.mullvad.mullvadvpn.feature.location.impl.EmptyRelayListText +import net.mullvad.mullvadvpn.feature.location.impl.FilterRow +import net.mullvad.mullvadvpn.feature.location.impl.UndoChangeMultihopAction +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.LocationBottomSheetState +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.LocationBottomSheets +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.OnCustomListNavResult +import net.mullvad.mullvadvpn.feature.location.impl.bottomsheet.showResultSnackbar +import net.mullvad.mullvadvpn.feature.location.impl.relayListContent import net.mullvad.mullvadvpn.lib.common.Lce import net.mullvad.mullvadvpn.lib.model.CustomListId import net.mullvad.mullvadvpn.lib.model.RelayItem @@ -73,6 +76,7 @@ import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar import net.mullvad.mullvadvpn.lib.ui.designsystem.ListHeader import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadSnackbar +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.AlphaScrollbar @@ -80,9 +84,6 @@ import net.mullvad.mullvadvpn.lib.usecase.FilterChip import net.mullvad.mullvadvpn.lib.usecase.ModifyMultihopError import net.mullvad.mullvadvpn.lib.usecase.MultihopChange import net.mullvad.mullvadvpn.lib.usecase.SelectRelayItemError -import net.mullvad.mullvadvpn.viewmodel.location.SearchLocationSideEffect -import net.mullvad.mullvadvpn.viewmodel.location.SearchLocationViewModel -import net.mullvad.mullvadvpn.viewmodel.location.UndoChangeMultihopAction import org.koin.androidx.compose.koinViewModel @Preview("Loading|Default|No Locations|Not found|Results") @@ -118,7 +119,10 @@ data class SearchLocationNavArgs(val relayListType: RelayListType) @Suppress("LongMethod") @Composable -@Destination<MainGraph>(style = TopLevelTransition::class, navArgs = SearchLocationNavArgs::class) +@Destination<ExternalModuleGraph>( + style = TopLevelTransition::class, + navArgs = SearchLocationNavArgs::class, +) fun SearchLocation( navigator: DestinationsNavigator, backNavigator: ResultBackNavigator<RelayListType>, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SearchLocationUiState.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationUiState.kt index fe7d0d9cca..2a8c40ee9b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SearchLocationUiState.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationUiState.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.compose.state +package net.mullvad.mullvadvpn.feature.location.impl.search import net.mullvad.mullvadvpn.lib.model.RelayItem import net.mullvad.mullvadvpn.lib.model.RelayListType diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SearchLocationViewModel.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationViewModel.kt index e0e37fa96a..1d8dce7e71 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SearchLocationViewModel.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationViewModel.kt @@ -1,9 +1,9 @@ -package net.mullvad.mullvadvpn.viewmodel.location +package net.mullvad.mullvadvpn.feature.location.impl.search import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.ramcosta.composedestinations.generated.destinations.SearchLocationDestination +import com.ramcosta.composedestinations.generated.location.destinations.SearchLocationDestination import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -13,7 +13,10 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import net.mullvad.mullvadvpn.compose.state.SearchLocationUiState +import net.mullvad.mullvadvpn.feature.location.impl.UndoChangeMultihopAction +import net.mullvad.mullvadvpn.feature.location.impl.addLocationToCustomList +import net.mullvad.mullvadvpn.feature.location.impl.onToggleExpandMap +import net.mullvad.mullvadvpn.feature.location.impl.removeLocationFromCustomList import net.mullvad.mullvadvpn.lib.common.Lce import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT import net.mullvad.mullvadvpn.lib.common.util.combine diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/SearchLocationsUiStatePreviewParameterProvider.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationsUiStatePreviewParameterProvider.kt index c7c27a24d2..a283fb29d0 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/SearchLocationsUiStatePreviewParameterProvider.kt +++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationsUiStatePreviewParameterProvider.kt @@ -1,7 +1,6 @@ -package net.mullvad.mullvadvpn.compose.preview +package net.mullvad.mullvadvpn.feature.location.impl.search import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import net.mullvad.mullvadvpn.compose.state.SearchLocationUiState import net.mullvad.mullvadvpn.lib.common.Lce import net.mullvad.mullvadvpn.lib.model.MultihopRelayListType import net.mullvad.mullvadvpn.lib.model.RelayListType diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModelTest.kt b/android/lib/feature/location/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationViewModelTest.kt index d1c30a397a..42a6709ffd 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModelTest.kt +++ b/android/lib/feature/location/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationViewModelTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel.location +package net.mullvad.mullvadvpn.feature.location.impl import androidx.lifecycle.viewModelScope import app.cash.turbine.test @@ -15,8 +15,6 @@ import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest -import net.mullvad.mullvadvpn.compose.screen.location.RelayListScrollConnection -import net.mullvad.mullvadvpn.compose.state.SelectLocationUiState import net.mullvad.mullvadvpn.lib.common.Lc import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.lib.common.test.assertLists diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModelTest.kt b/android/lib/feature/location/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/location/impl/list/SelectLocationListViewModelTest.kt index 3db935322a..64984834d5 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModelTest.kt +++ b/android/lib/feature/location/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/location/impl/list/SelectLocationListViewModelTest.kt @@ -1,4 +1,4 @@ -package net.mullvad.mullvadvpn.viewmodel.location +package net.mullvad.mullvadvpn.feature.location.impl.list import app.cash.turbine.test import io.mockk.every @@ -6,11 +6,13 @@ import io.mockk.mockk import io.mockk.mockkStatic import io.mockk.unmockkAll import io.mockk.verify +import kotlin.test.assertEquals import kotlin.test.assertIs +import kotlin.test.assertTrue import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest -import net.mullvad.mullvadvpn.compose.screen.location.RelayListScrollConnection -import net.mullvad.mullvadvpn.compose.state.SelectLocationListUiState +import net.mullvad.mullvadvpn.feature.location.impl.RelayListScrollConnection +import net.mullvad.mullvadvpn.feature.location.impl.search.relayListItems import net.mullvad.mullvadvpn.lib.common.Lce import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.lib.common.test.assertLists @@ -32,8 +34,6 @@ import net.mullvad.mullvadvpn.lib.usecase.SelectedLocationUseCase import net.mullvad.mullvadvpn.lib.usecase.customlists.CustomListsRelayItemUseCase import net.mullvad.mullvadvpn.lib.usecase.customlists.FilterCustomListsRelayItemUseCase import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -272,7 +272,7 @@ class SelectLocationListViewModelTest { companion object { private const val RELAY_ITEM_LIST_CREATOR_CLASS = - "net.mullvad.mullvadvpn.viewmodel.location.RelayItemListCreatorKt" + "net.mullvad.mullvadvpn.feature.location.impl.search.RelayItemListCreatorKt" private const val LOCATION_UTIL_CLASS = "net.mullvad.mullvadvpn.lib.common.util.LocationUtilKt" diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SearchLocationViewModelTest.kt b/android/lib/feature/location/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationViewModelTest.kt index efe3fb3d62..a872495fd9 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SearchLocationViewModelTest.kt +++ b/android/lib/feature/location/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationViewModelTest.kt @@ -1,14 +1,13 @@ -package net.mullvad.mullvadvpn.viewmodel.location +package net.mullvad.mullvadvpn.feature.location.impl.search import app.cash.turbine.test -import com.ramcosta.composedestinations.generated.navargs.toSavedStateHandle +import com.ramcosta.composedestinations.generated.location.navargs.toSavedStateHandle import io.mockk.every import io.mockk.mockk import kotlin.test.assertIs +import kotlin.test.assertTrue import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest -import net.mullvad.mullvadvpn.compose.screen.location.SearchLocationNavArgs -import net.mullvad.mullvadvpn.compose.state.SearchLocationUiState import net.mullvad.mullvadvpn.lib.common.Lce import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule import net.mullvad.mullvadvpn.lib.common.test.assertLists @@ -34,7 +33,6 @@ import net.mullvad.mullvadvpn.lib.usecase.SelectedLocationUseCase import net.mullvad.mullvadvpn.lib.usecase.customlists.CustomListActionUseCase import net.mullvad.mullvadvpn.lib.usecase.customlists.CustomListsRelayItemUseCase import net.mullvad.mullvadvpn.lib.usecase.customlists.FilterCustomListsRelayItemUseCase -import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith 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..7425a54c56 --- /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"> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> +</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..2d45db3745 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 @@ -62,50 +62,39 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.LayoutDirection import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.dropUnlessResumed +import androidx.navigation.NavController 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,9 +119,10 @@ 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( + navController: NavController, navigator: DestinationsNavigator, accountNumber: String? = null, vm: LoginViewModel = koinViewModel(), @@ -177,23 +167,23 @@ fun Login( CollectSideEffectWithLifecycle(vm.uiSideEffect) { when (it) { LoginUiSideEffect.NavigateToWelcome -> - navigator.navigate(WelcomeDestination) { + navController.navigate("home/welcome") { launchSingleTop = true - popUpTo(NavGraphs.main) { inclusive = true } + popUpTo("main") { inclusive = true } } is LoginUiSideEffect.NavigateToConnect -> - navigator.navigate(ConnectDestination) { + navController.navigate("home/connect") { launchSingleTop = true - popUpTo(NavGraphs.main) { inclusive = true } + popUpTo("main") { inclusive = true } } is LoginUiSideEffect.TooManyDevices -> navigator.navigate(DeviceListDestination(it.accountNumber)) { launchSingleTop = true } LoginUiSideEffect.NavigateToOutOfTime -> - navigator.navigate(OutOfTimeDestination) { + navController.navigate("home/out_of_time") { launchSingleTop = true - popUpTo(NavGraphs.main) { inclusive = true } + popUpTo("main") { inclusive = true } } LoginUiSideEffect.NavigateToCreateAccountConfirmation -> navigator.navigate(CreateAccountConfirmationDestination) @@ -234,7 +224,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 +250,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, + ) } } } @@ -352,13 +345,15 @@ private fun ColumnScope.LoginInput( 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 +369,7 @@ private fun ColumnScope.LoginInput( onLoginClick(it.value) } }, - enabled = state.loginState is Idle, + enabled = state.loginState is LoginState.Idle, onDeleteClick = onDeleteHistoryClick, ) } @@ -385,7 +380,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 +389,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 +404,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 +441,11 @@ private fun LoginState.supportingText( onShowApiUnreachableDialog: (LoginUiStateError) -> Unit ): AnnotatedString? = when (this) { - is Idle if - loginUiStateError is LoginUiStateError.LoginError.ApiUnreachable || - loginUiStateError is LoginUiStateError.CreateAccountError.ApiUnreachable + 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 +462,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/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/Navigation.kt b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/Navigation.kt index ab94081b7f..ab94081b7f 100644 --- a/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/Navigation.kt +++ b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/Navigation.kt diff --git a/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/AccountTransition.kt b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/animation/AccountTransition.kt index e4fa8e72f8..e4fa8e72f8 100644 --- a/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/AccountTransition.kt +++ b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/animation/AccountTransition.kt diff --git a/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/AnimationConstant.kt b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/animation/AnimationConstant.kt index 12eb6e2215..12eb6e2215 100644 --- a/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/AnimationConstant.kt +++ b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/animation/AnimationConstant.kt diff --git a/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/DefaultTransition.kt b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/animation/DefaultTransition.kt index cc45ab55fe..cc45ab55fe 100644 --- a/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/DefaultTransition.kt +++ b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/animation/DefaultTransition.kt diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/transitions/LoginTransition.kt b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/animation/LoginTransition.kt index 2c82d24d7c..863ee93916 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/transitions/LoginTransition.kt +++ b/android/lib/navigation/src/main/kotlin/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,11 +19,11 @@ object LoginTransition : DestinationStyle.Animated() { override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = { - when (this.targetState.destination()) { - is OutOfTimeDestination, - is WelcomeDestination, - is ConnectDestination, - is DeviceListDestination -> fadeOut(spring()) + when (this.targetState.destination.route) { + "home/out_of_time", + "home/welcome", + "home/connect", + "login/device_list" -> fadeOut(spring()) else -> ExitTransition.None } } diff --git a/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/SlideInFromRightTransition.kt b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/animation/SlideInFromRightTransition.kt index 79587f47c2..79587f47c2 100644 --- a/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/SlideInFromRightTransition.kt +++ b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/animation/SlideInFromRightTransition.kt diff --git a/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/TopLevelTransition.kt b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/animation/TopLevelTransition.kt index 3297be9627..3297be9627 100644 --- a/android/lib/navigation/src/main/java/net/mullvad/mullvadvpn/core/animation/TopLevelTransition.kt +++ b/android/lib/navigation/src/main/kotlin/net/mullvad/mullvadvpn/core/animation/TopLevelTransition.kt 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..fdd2360d8e --- /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"> + <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> +</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..1f5df775b8 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 { @@ -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 51778bd6fa..dc349597f4 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -38,6 +38,7 @@ include( ":lib:common-test", ":lib:grpc", ":lib:endpoint", + ":lib:feature:account:impl", ":lib:feature:addtime:impl", ":lib:feature:anticensorship:impl", ":lib:feature:apiaccess:impl", @@ -47,6 +48,9 @@ include( ":lib:feature:customlist:impl", ":lib:feature:daita:impl", ":lib:feature:filter:impl", + ":lib:feature:home:impl", + ":lib:feature:location:impl", + ":lib:feature:login:impl", ":lib:feature:managedevices:impl", ":lib:feature:multihop:impl", ":lib:feature:notification:impl", @@ -60,6 +64,7 @@ include( ":lib:model", ":lib:navigation", ":lib:payment", + ":lib:push-notification", ":lib:repository", ":lib:screen-test", ":lib:talpid", |
