diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt index ec5fa08299b6..d43a0931a1d6 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt @@ -34,7 +34,6 @@ class OutOfTimeScreenTest { // Arrange composeTestRule.setContentWithTheme { OutOfTimeScreen( - showSitePayment = false, uiState = OutOfTimeUiState(deviceName = ""), onSitePaymentClick = {}, onRedeemVoucherClick = {}, @@ -60,8 +59,7 @@ class OutOfTimeScreenTest { // Arrange composeTestRule.setContentWithTheme { OutOfTimeScreen( - showSitePayment = true, - uiState = OutOfTimeUiState(deviceName = ""), + uiState = OutOfTimeUiState(deviceName = "", showSitePayment = true), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -80,8 +78,7 @@ class OutOfTimeScreenTest { val mockClickListener: () -> Unit = mockk(relaxed = true) composeTestRule.setContentWithTheme { OutOfTimeScreen( - showSitePayment = true, - uiState = OutOfTimeUiState(deviceName = ""), + uiState = OutOfTimeUiState(deviceName = "", showSitePayment = true), onSitePaymentClick = mockClickListener, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -103,8 +100,7 @@ class OutOfTimeScreenTest { val mockClickListener: () -> Unit = mockk(relaxed = true) composeTestRule.setContentWithTheme { OutOfTimeScreen( - showSitePayment = true, - uiState = OutOfTimeUiState(deviceName = ""), + uiState = OutOfTimeUiState(deviceName = "", showSitePayment = true), onSitePaymentClick = {}, onRedeemVoucherClick = mockClickListener, onSettingsClick = {}, @@ -126,11 +122,11 @@ class OutOfTimeScreenTest { val mockClickListener: () -> Unit = mockk(relaxed = true) composeTestRule.setContentWithTheme { OutOfTimeScreen( - showSitePayment = true, uiState = OutOfTimeUiState( tunnelState = TunnelState.Connecting(null, null), - deviceName = "" + deviceName = "", + showSitePayment = true ), onSitePaymentClick = {}, onRedeemVoucherClick = {}, @@ -152,8 +148,11 @@ class OutOfTimeScreenTest { // Arrange composeTestRule.setContentWithTheme { OutOfTimeScreen( - showSitePayment = true, - uiState = OutOfTimeUiState().copy(billingPaymentState = PaymentState.Error.Billing), + uiState = + OutOfTimeUiState( + showSitePayment = true, + billingPaymentState = PaymentState.Error.Billing + ), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -174,9 +173,9 @@ class OutOfTimeScreenTest { every { mockPaymentProduct.status } returns null composeTestRule.setContentWithTheme { OutOfTimeScreen( - showSitePayment = true, uiState = OutOfTimeUiState( + showSitePayment = true, billingPaymentState = PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) ), @@ -200,13 +199,12 @@ class OutOfTimeScreenTest { every { mockPaymentProduct.status } returns PaymentStatus.PENDING composeTestRule.setContentWithTheme { OutOfTimeScreen( - showSitePayment = true, uiState = - OutOfTimeUiState() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), + OutOfTimeUiState( + showSitePayment = true, + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), ) } @@ -223,13 +221,12 @@ class OutOfTimeScreenTest { val mockNavigateToVerificationPending: () -> Unit = mockk(relaxed = true) composeTestRule.setContentWithTheme { OutOfTimeScreen( - showSitePayment = true, uiState = - OutOfTimeUiState() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), + OutOfTimeUiState( + showSitePayment = true, + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), navigateToVerificationPendingDialog = mockNavigateToVerificationPending ) } @@ -249,13 +246,12 @@ class OutOfTimeScreenTest { every { mockPaymentProduct.status } returns PaymentStatus.VERIFICATION_IN_PROGRESS composeTestRule.setContentWithTheme { OutOfTimeScreen( - showSitePayment = true, uiState = - OutOfTimeUiState() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), + OutOfTimeUiState( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), + showSitePayment = true, + ) ) } @@ -273,11 +269,11 @@ class OutOfTimeScreenTest { every { mockPaymentProduct.status } returns null composeTestRule.setContentWithTheme { OutOfTimeScreen( - showSitePayment = true, uiState = OutOfTimeUiState( billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), + showSitePayment = true, ), onSitePaymentClick = {}, onRedeemVoucherClick = {}, diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt index 803a2a0b1436..e62b1a399b15 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt @@ -8,9 +8,6 @@ import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.mockk import io.mockk.verify -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.compose.state.PaymentState import net.mullvad.mullvadvpn.compose.state.WelcomeUiState @@ -19,7 +16,6 @@ import net.mullvad.mullvadvpn.lib.payment.model.PaymentProduct import net.mullvad.mullvadvpn.lib.payment.model.PaymentStatus import net.mullvad.mullvadvpn.lib.payment.model.ProductId import net.mullvad.mullvadvpn.lib.payment.model.ProductPrice -import net.mullvad.mullvadvpn.viewmodel.WelcomeViewModel import org.junit.Before import org.junit.Rule import org.junit.Test @@ -38,12 +34,10 @@ class WelcomeScreenTest { composeTestRule.setContentWithTheme { WelcomeScreen( uiState = WelcomeUiState(), - uiSideEffect = MutableSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, onAccountClick = {}, - openConnectScreen = {}, navigateToDeviceInfoDialog = {}, onPurchaseBillingProductClick = { _ -> }, navigateToVerificationPendingDialog = {} @@ -63,12 +57,10 @@ class WelcomeScreenTest { composeTestRule.setContentWithTheme { WelcomeScreen( uiState = WelcomeUiState(), - uiSideEffect = MutableSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, onAccountClick = {}, - openConnectScreen = {}, navigateToDeviceInfoDialog = {}, onPurchaseBillingProductClick = { _ -> }, navigateToVerificationPendingDialog = {} @@ -94,12 +86,10 @@ class WelcomeScreenTest { composeTestRule.setContentWithTheme { WelcomeScreen( uiState = WelcomeUiState(accountNumber = rawAccountNumber), - uiSideEffect = MutableSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, onAccountClick = {}, - openConnectScreen = {}, onPurchaseBillingProductClick = { _ -> }, navigateToDeviceInfoDialog = {}, navigateToVerificationPendingDialog = {} @@ -110,52 +100,6 @@ class WelcomeScreenTest { composeTestRule.apply { onNodeWithText(expectedAccountNumber).assertExists() } } - @Test - fun testOpenAccountView() { - // Arrange - composeTestRule.setContentWithTheme { - WelcomeScreen( - uiState = WelcomeUiState(), - uiSideEffect = - MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenAccountView("222")), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - openConnectScreen = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {} - ) - } - - // Assert - composeTestRule.apply { onNodeWithText("Congrats!").assertDoesNotExist() } - } - - @Test - fun testOpenConnectScreen() { - // Arrange - val mockClickListener: () -> Unit = mockk(relaxed = true) - composeTestRule.setContentWithTheme { - WelcomeScreen( - uiState = WelcomeUiState(), - uiSideEffect = MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenConnectScreen), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - openConnectScreen = mockClickListener, - onPurchaseBillingProductClick = { _ -> }, - navigateToVerificationPendingDialog = {}, - navigateToDeviceInfoDialog = {} - ) - } - - // Assert - verify(exactly = 1) { mockClickListener.invoke() } - } - @Test fun testClickSitePaymentButton() { // Arrange @@ -163,12 +107,10 @@ class WelcomeScreenTest { composeTestRule.setContentWithTheme { WelcomeScreen( uiState = WelcomeUiState(showSitePayment = true), - uiSideEffect = MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenConnectScreen), onSitePaymentClick = mockClickListener, onRedeemVoucherClick = {}, onSettingsClick = {}, onAccountClick = {}, - openConnectScreen = {}, onPurchaseBillingProductClick = { _ -> }, navigateToVerificationPendingDialog = {}, navigateToDeviceInfoDialog = {} @@ -189,12 +131,10 @@ class WelcomeScreenTest { composeTestRule.setContentWithTheme { WelcomeScreen( uiState = WelcomeUiState(), - uiSideEffect = MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenConnectScreen), onSitePaymentClick = {}, onRedeemVoucherClick = mockClickListener, onSettingsClick = {}, onAccountClick = {}, - openConnectScreen = {}, onPurchaseBillingProductClick = { _ -> }, navigateToVerificationPendingDialog = {}, navigateToDeviceInfoDialog = {} @@ -214,12 +154,10 @@ class WelcomeScreenTest { composeTestRule.setContentWithTheme { WelcomeScreen( uiState = WelcomeUiState().copy(billingPaymentState = PaymentState.Error.Billing), - uiSideEffect = MutableSharedFlow().asSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, onAccountClick = {}, - openConnectScreen = {}, onPurchaseBillingProductClick = { _ -> }, navigateToVerificationPendingDialog = {}, navigateToDeviceInfoDialog = {} @@ -243,12 +181,10 @@ class WelcomeScreenTest { billingPaymentState = PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) ), - uiSideEffect = MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenConnectScreen), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, onAccountClick = {}, - openConnectScreen = {}, onPurchaseBillingProductClick = { _ -> }, navigateToVerificationPendingDialog = {}, navigateToDeviceInfoDialog = {} @@ -273,12 +209,10 @@ class WelcomeScreenTest { billingPaymentState = PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) ), - uiSideEffect = MutableSharedFlow().asSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, onAccountClick = {}, - openConnectScreen = {}, onPurchaseBillingProductClick = { _ -> }, navigateToVerificationPendingDialog = {}, navigateToDeviceInfoDialog = {} @@ -304,12 +238,10 @@ class WelcomeScreenTest { billingPaymentState = PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) ), - uiSideEffect = MutableSharedFlow().asSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, onAccountClick = {}, - openConnectScreen = {}, onPurchaseBillingProductClick = { _ -> }, navigateToVerificationPendingDialog = mockShowPendingInfo, navigateToDeviceInfoDialog = {} @@ -337,12 +269,10 @@ class WelcomeScreenTest { billingPaymentState = PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) ), - uiSideEffect = MutableSharedFlow().asSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, onAccountClick = {}, - openConnectScreen = {}, onPurchaseBillingProductClick = { _ -> }, navigateToVerificationPendingDialog = {}, navigateToDeviceInfoDialog = {} @@ -368,12 +298,10 @@ class WelcomeScreenTest { billingPaymentState = PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) ), - uiSideEffect = MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenConnectScreen), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, onAccountClick = {}, - openConnectScreen = {}, onPurchaseBillingProductClick = clickHandler, navigateToVerificationPendingDialog = {}, navigateToDeviceInfoDialog = {} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt index f90724d0cea7..18925d0524dd 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt @@ -43,7 +43,6 @@ import net.mullvad.mullvadvpn.compose.destinations.VerificationPendingDialogDest import net.mullvad.mullvadvpn.compose.extensions.createOpenAccountPageHook import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState import net.mullvad.mullvadvpn.compose.transitions.NoTransition -import net.mullvad.mullvadvpn.constant.IS_PLAY_BUILD import net.mullvad.mullvadvpn.lib.payment.model.ProductId import net.mullvad.mullvadvpn.lib.theme.AppTheme import net.mullvad.mullvadvpn.lib.theme.Dimens diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt index 0513d7ba210c..b5bacde95aef 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt @@ -34,9 +34,6 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.popUpTo import com.ramcosta.composedestinations.result.NavResult import com.ramcosta.composedestinations.result.ResultRecipient -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.compose.NavGraphs import net.mullvad.mullvadvpn.compose.button.RedeemVoucherButton @@ -86,12 +83,10 @@ private fun PreviewWelcomeScreen() { ) ) ), - uiSideEffect = MutableSharedFlow().asSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, onAccountClick = {}, - openConnectScreen = {}, onPurchaseBillingProductClick = { _ -> }, navigateToDeviceInfoDialog = {}, navigateToVerificationPendingDialog = {} @@ -133,21 +128,30 @@ fun Welcome( } } + val context = LocalContext.current + LaunchedEffect(Unit) { + vm.uiSideEffect.collect { uiSideEffect -> + when (uiSideEffect) { + is WelcomeViewModel.UiSideEffect.OpenAccountView -> + context.openAccountPageInBrowser(uiSideEffect.token) + WelcomeViewModel.UiSideEffect.OpenConnectScreen -> { + navigator.navigate(ConnectDestination) { + launchSingleTop = true + popUpTo(NavGraphs.root) { inclusive = true } + } + } + } + } + } + WelcomeScreen( uiState = state, - uiSideEffect = vm.uiSideEffect, onSitePaymentClick = vm::onSitePaymentClick, onRedeemVoucherClick = { navigator.navigate(RedeemVoucherDestination) { launchSingleTop = true } }, onSettingsClick = { navigator.navigate(SettingsDestination) { launchSingleTop = true } }, onAccountClick = { navigator.navigate(AccountDestination) { launchSingleTop = true } }, - openConnectScreen = { - navigator.navigate(ConnectDestination) { - launchSingleTop = true - popUpTo(NavGraphs.root) { inclusive = true } - } - }, navigateToDeviceInfoDialog = { navigator.navigate(DeviceNameInfoDialogDestination) { launchSingleTop = true } }, @@ -163,27 +167,14 @@ fun Welcome( @Composable fun WelcomeScreen( uiState: WelcomeUiState, - uiSideEffect: SharedFlow, onSitePaymentClick: () -> Unit, onRedeemVoucherClick: () -> Unit, onSettingsClick: () -> Unit, onAccountClick: () -> Unit, - openConnectScreen: () -> Unit, onPurchaseBillingProductClick: (productId: ProductId) -> Unit, navigateToDeviceInfoDialog: () -> Unit, navigateToVerificationPendingDialog: () -> Unit ) { - val context = LocalContext.current - LaunchedEffect(Unit) { - uiSideEffect.collect { uiSideEffect -> - when (uiSideEffect) { - is WelcomeViewModel.UiSideEffect.OpenAccountView -> - context.openAccountPageInBrowser(uiSideEffect.token) - WelcomeViewModel.UiSideEffect.OpenConnectScreen -> openConnectScreen() - } - } - } - val scrollState = rememberScrollState() val snackbarHostState = remember { SnackbarHostState() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/OutOfTimeUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/OutOfTimeUiState.kt index 60a9e4589bfe..54fd414f866a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/OutOfTimeUiState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/OutOfTimeUiState.kt @@ -5,6 +5,6 @@ import net.mullvad.mullvadvpn.model.TunnelState data class OutOfTimeUiState( val tunnelState: TunnelState = TunnelState.Disconnected, val deviceName: String = "", - val showSitePayment: Boolean = true, + val showSitePayment: Boolean = false, val billingPaymentState: PaymentState? = null, ) diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/SdkUtils.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/SdkUtils.kt index 951bb1631ec6..11394ca8ddbe 100644 --- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/SdkUtils.kt +++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/SdkUtils.kt @@ -2,7 +2,6 @@ package net.mullvad.mullvadvpn.lib.common.util import android.Manifest import android.app.PendingIntent -import android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT import android.content.Context import android.content.pm.PackageInfo import android.content.pm.PackageManager diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountHistory.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountHistory.kt index ce1b71123237..f003ee316ba9 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountHistory.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountHistory.kt @@ -4,8 +4,7 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize sealed class AccountHistory : Parcelable { - @Parcelize - data class Available(val accountToken: String) : AccountHistory() + @Parcelize data class Available(val accountToken: String) : AccountHistory() @Parcelize object Missing : AccountHistory()