summaryrefslogtreecommitdiffhomepage
path: root/android/app/src/main
diff options
context:
space:
mode:
authorAlbin <albin@mullvad.net>2024-08-23 10:27:49 +0200
committerAlbin <albin@mullvad.net>2024-08-23 10:27:49 +0200
commit97ee66d64d9e5ef9265eae3fe74464d1fc807dd4 (patch)
tree8506abc903055521c1110e0ec8cb428e1431c6ba /android/app/src/main
parent67486d316b94262cb2e478765f4234b5d12afcba (diff)
parent4c7a6fa7941519ea637345cf6521edd067a6aa3a (diff)
downloadmullvadvpn-97ee66d64d9e5ef9265eae3fe74464d1fc807dd4.tar.xz
mullvadvpn-97ee66d64d9e5ef9265eae3fe74464d1fc807dd4.zip
Merge branch 'ensure-all-potential-grpc-errors-are-wrapped-droid-1170'
Diffstat (limited to 'android/app/src/main')
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt24
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt13
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt11
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt10
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt18
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt14
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt7
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt6
9 files changed, 90 insertions, 17 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt
index 6969e56518..bd64cc3561 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt
@@ -57,6 +57,7 @@ import net.mullvad.mullvadvpn.compose.transitions.SlideInFromBottomTransition
import net.mullvad.mullvadvpn.compose.util.LaunchedEffectCollect
import net.mullvad.mullvadvpn.compose.util.SecureScreenWhileInView
import net.mullvad.mullvadvpn.compose.util.createCopyToClipboardHandle
+import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.payment.model.PaymentProduct
import net.mullvad.mullvadvpn.lib.payment.model.PaymentStatus
@@ -164,6 +165,7 @@ fun AccountScreen(
val snackbarHostState = remember { SnackbarHostState() }
val copyTextString = stringResource(id = R.string.copied_mullvad_account_number)
+ val errorString = stringResource(id = R.string.error_occurred)
val copyToClipboard = createCopyToClipboardHandle(snackbarHostState = snackbarHostState)
val openAccountPage = LocalUriHandler.current.createOpenAccountPageHook()
LaunchedEffectCollect(uiSideEffect) { sideEffect ->
@@ -173,6 +175,8 @@ fun AccountScreen(
openAccountPage(sideEffect.token)
is AccountViewModel.UiSideEffect.CopyAccountNumber ->
launch { copyToClipboard(sideEffect.accountNumber, copyTextString) }
+ AccountViewModel.UiSideEffect.GenericError ->
+ snackbarHostState.showSnackbarImmediately(message = errorString)
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt
index 8a31520aad..1758a31432 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt
@@ -24,6 +24,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
@@ -37,6 +38,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -73,6 +75,7 @@ import net.mullvad.mullvadvpn.compose.textfield.mullvadWhiteTextFieldColors
import net.mullvad.mullvadvpn.compose.transitions.LoginTransition
import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle
import net.mullvad.mullvadvpn.compose.util.accountNumberVisualTransformation
+import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.viewmodel.LoginUiSideEffect
@@ -126,6 +129,8 @@ fun Login(
}
}
+ val context = LocalContext.current
+ val snackbarHostState = remember { SnackbarHostState() }
CollectSideEffectWithLifecycle(vm.uiSideEffect) {
when (it) {
LoginUiSideEffect.NavigateToWelcome ->
@@ -147,21 +152,27 @@ fun Login(
launchSingleTop = true
popUpTo(NavGraphs.root) { inclusive = true }
}
+ LoginUiSideEffect.GenericError ->
+ snackbarHostState.showSnackbarImmediately(
+ message = context.getString(R.string.error_occurred),
+ )
}
}
LoginScreen(
- state,
- vm::login,
- vm::createAccount,
- vm::clearAccountHistory,
- vm::onAccountNumberChange,
- dropUnlessResumed { navigator.navigate(SettingsDestination) }
+ state = state,
+ snackbarHostState = snackbarHostState,
+ onLoginClick = vm::login,
+ onCreateAccountClick = vm::createAccount,
+ onDeleteHistoryClick = vm::clearAccountHistory,
+ onAccountNumberChange = vm::onAccountNumberChange,
+ onSettingsClick = dropUnlessResumed { navigator.navigate(SettingsDestination) }
)
}
@Composable
private fun LoginScreen(
state: LoginUiState,
+ snackbarHostState: SnackbarHostState = SnackbarHostState(),
onLoginClick: (String) -> Unit = {},
onCreateAccountClick: () -> Unit = {},
onDeleteHistoryClick: () -> Unit = {},
@@ -169,6 +180,7 @@ private fun LoginScreen(
onSettingsClick: () -> Unit = {},
) {
ScaffoldWithTopBar(
+ snackbarHostState = snackbarHostState,
topBarColor = MaterialTheme.colorScheme.primary,
iconTintColor = MaterialTheme.colorScheme.onPrimary,
onSettingsClicked = onSettingsClick,
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 eea7a20e47..7b5b4bff55 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
@@ -11,11 +11,14 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
@@ -49,6 +52,7 @@ import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState
import net.mullvad.mullvadvpn.compose.test.OUT_OF_TIME_SCREEN_TITLE_TEST_TAG
import net.mullvad.mullvadvpn.compose.transitions.HomeTransition
import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle
+import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately
import net.mullvad.mullvadvpn.lib.model.ErrorState
import net.mullvad.mullvadvpn.lib.model.ErrorStateCause
import net.mullvad.mullvadvpn.lib.model.TunnelState
@@ -135,6 +139,8 @@ fun OutOfTime(
}
}
+ val snackbarHostState = remember { SnackbarHostState() }
+ val context = LocalContext.current
val openAccountPage = LocalUriHandler.current.createOpenAccountPageHook()
CollectSideEffectWithLifecycle(vm.uiSideEffect, Lifecycle.State.RESUMED) { uiSideEffect ->
when (uiSideEffect) {
@@ -145,11 +151,16 @@ fun OutOfTime(
launchSingleTop = true
popUpTo(NavGraphs.root) { inclusive = true }
}
+ OutOfTimeViewModel.UiSideEffect.GenericError ->
+ snackbarHostState.showSnackbarImmediately(
+ message = context.getString(R.string.error_occurred)
+ )
}
}
OutOfTimeScreen(
state = state,
+ snackbarHostState = snackbarHostState,
onSitePaymentClick = vm::onSitePaymentClick,
onRedeemVoucherClick = dropUnlessResumed { navigator.navigate(RedeemVoucherDestination) },
onSettingsClick = dropUnlessResumed { navigator.navigate(SettingsDestination) },
@@ -165,6 +176,7 @@ fun OutOfTime(
@Composable
fun OutOfTimeScreen(
state: OutOfTimeUiState,
+ snackbarHostState: SnackbarHostState = SnackbarHostState(),
onDisconnectClick: () -> Unit = {},
onSitePaymentClick: () -> Unit = {},
onRedeemVoucherClick: () -> Unit = {},
@@ -176,6 +188,7 @@ fun OutOfTimeScreen(
val scrollState = rememberScrollState()
ScaffoldWithTopBarAndDeviceName(
+ snackbarHostState = snackbarHostState,
topBarColor =
if (state.tunnelState.isSecured()) {
MaterialTheme.colorScheme.tertiary
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 bb376c09f9..e53ec044cc 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
@@ -21,6 +21,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -57,6 +58,7 @@ import net.mullvad.mullvadvpn.compose.state.WelcomeUiState
import net.mullvad.mullvadvpn.compose.transitions.HomeTransition
import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle
import net.mullvad.mullvadvpn.compose.util.createCopyToClipboardHandle
+import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately
import net.mullvad.mullvadvpn.lib.common.util.groupWithSpaces
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.payment.model.PaymentProduct
@@ -131,6 +133,8 @@ fun Welcome(
}
}
+ val snackbarHostState = remember { SnackbarHostState() }
+ val context = LocalContext.current
val openAccountPage = LocalUriHandler.current.createOpenAccountPageHook()
CollectSideEffectWithLifecycle(sideEffect = vm.uiSideEffect, Lifecycle.State.RESUMED) {
uiSideEffect ->
@@ -141,11 +145,16 @@ fun Welcome(
launchSingleTop = true
popUpTo(NavGraphs.root) { inclusive = true }
}
+ WelcomeViewModel.UiSideEffect.GenericError ->
+ snackbarHostState.showSnackbarImmediately(
+ message = context.getString(R.string.error_occurred)
+ )
}
}
WelcomeScreen(
state = state,
+ snackbarHostState = snackbarHostState,
onSitePaymentClick = dropUnlessResumed { vm.onSitePaymentClick() },
onRedeemVoucherClick = dropUnlessResumed { navigator.navigate(RedeemVoucherDestination) },
onSettingsClick = dropUnlessResumed { navigator.navigate(SettingsDestination) },
@@ -163,6 +172,7 @@ fun Welcome(
@Composable
fun WelcomeScreen(
state: WelcomeUiState,
+ snackbarHostState: SnackbarHostState = SnackbarHostState(),
onSitePaymentClick: () -> Unit,
onRedeemVoucherClick: () -> Unit,
onSettingsClick: () -> Unit,
@@ -173,7 +183,6 @@ fun WelcomeScreen(
navigateToVerificationPendingDialog: () -> Unit
) {
val scrollState = rememberScrollState()
- val snackbarHostState = remember { SnackbarHostState() }
ScaffoldWithTopBar(
topBarColor = MaterialTheme.colorScheme.primary,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt
index b830d00c60..f8f66563e8 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt
@@ -75,8 +75,12 @@ class AccountViewModel(
fun onLogoutClick() {
viewModelScope.launch {
- accountRepository.logout()
- _uiSideEffect.send(UiSideEffect.NavigateToLogin)
+ accountRepository
+ .logout()
+ .fold(
+ { _uiSideEffect.send(UiSideEffect.GenericError) },
+ { _uiSideEffect.send(UiSideEffect.NavigateToLogin) }
+ )
}
}
@@ -127,6 +131,8 @@ class AccountViewModel(
UiSideEffect()
data class CopyAccountNumber(val accountNumber: String) : UiSideEffect()
+
+ data object GenericError : UiSideEffect()
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt
index d1f4b3713b..325ee42b43 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt
@@ -123,11 +123,19 @@ class ConnectViewModel(
}
fun onDisconnectClick() {
- viewModelScope.launch { connectionProxy.disconnect() }
+ viewModelScope.launch {
+ connectionProxy.disconnect().onLeft {
+ _uiSideEffect.send(UiSideEffect.ConnectError.Generic)
+ }
+ }
}
fun onReconnectClick() {
- viewModelScope.launch { connectionProxy.reconnect() }
+ viewModelScope.launch {
+ connectionProxy.reconnect().onLeft {
+ _uiSideEffect.send(UiSideEffect.ConnectError.Generic)
+ }
+ }
}
fun onConnectClick() {
@@ -156,7 +164,11 @@ class ConnectViewModel(
}
fun onCancelClick() {
- viewModelScope.launch { connectionProxy.disconnect() }
+ viewModelScope.launch {
+ connectionProxy.disconnect().onLeft {
+ _uiSideEffect.send(UiSideEffect.ConnectError.Generic)
+ }
+ }
}
fun onManageAccountClick() {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt
index 30bea42cfe..3c02c92917 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt
@@ -42,6 +42,8 @@ sealed interface LoginUiSideEffect {
data object NavigateToOutOfTime : LoginUiSideEffect
data class TooManyDevices(val accountNumber: AccountNumber) : LoginUiSideEffect
+
+ data object GenericError : LoginUiSideEffect
}
class LoginViewModel(
@@ -78,9 +80,15 @@ class LoginViewModel(
fun clearAccountHistory() =
viewModelScope.launch {
- accountRepository.clearAccountHistory()
- _mutableAccountHistory.update { null }
- _mutableAccountHistory.update { accountRepository.fetchAccountHistory() }
+ accountRepository
+ .clearAccountHistory()
+ .fold(
+ { _uiSideEffect.send(LoginUiSideEffect.GenericError) },
+ {
+ _mutableAccountHistory.update { null }
+ _mutableAccountHistory.update { accountRepository.fetchAccountHistory() }
+ }
+ )
}
fun createAccount() {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt
index b2738a56ae..dfa4e6e3cc 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt
@@ -22,6 +22,7 @@ import net.mullvad.mullvadvpn.usecase.OutOfTimeUseCase
import net.mullvad.mullvadvpn.usecase.PaymentUseCase
import net.mullvad.mullvadvpn.util.isSuccess
import net.mullvad.mullvadvpn.util.toPaymentState
+import net.mullvad.mullvadvpn.viewmodel.WelcomeViewModel.UiSideEffect
class OutOfTimeViewModel(
private val accountRepository: AccountRepository,
@@ -71,7 +72,9 @@ class OutOfTimeViewModel(
}
fun onDisconnectClick() {
- viewModelScope.launch { connectionProxy.disconnect() }
+ viewModelScope.launch {
+ connectionProxy.disconnect().onLeft { _uiSideEffect.send(UiSideEffect.GenericError) }
+ }
}
private fun verifyPurchases() {
@@ -118,5 +121,7 @@ class OutOfTimeViewModel(
data class OpenAccountView(val token: WebsiteAuthToken?) : UiSideEffect
data object OpenConnectScreen : UiSideEffect
+
+ data object GenericError : UiSideEffect
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt
index 525c6ca54e..e532045ae2 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt
@@ -78,7 +78,9 @@ class WelcomeViewModel(
}
fun onDisconnectClick() {
- viewModelScope.launch { connectionProxy.disconnect() }
+ viewModelScope.launch {
+ connectionProxy.disconnect().onLeft { _uiSideEffect.send(UiSideEffect.GenericError) }
+ }
}
private fun verifyPurchases() {
@@ -118,6 +120,8 @@ class WelcomeViewModel(
data class OpenAccountView(val token: WebsiteAuthToken?) : UiSideEffect
data object OpenConnectScreen : UiSideEffect
+
+ data object GenericError : UiSideEffect
}
companion object {