Skip to content

Commit

Permalink
Merge remote-tracking branch 'maryam/wip_hackday_project'
Browse files Browse the repository at this point in the history
  • Loading branch information
Pururun committed Dec 7, 2023
2 parents 687ec0d + 12b27e5 commit aed7c8d
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ private fun LoginState.supportingText(): String? {
when (loginError) {
LoginError.InvalidCredentials -> R.string.login_fail_description
LoginError.UnableToCreateAccount -> R.string.failed_to_create_account
LoginError.NoInternetConnection -> R.string.no_internet_connection
is LoginError.Unknown -> R.string.error_occurred
null -> return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ sealed class LoginError {
data object InvalidCredentials : LoginError()

data class Unknown(val reason: String) : LoginError()

data object NoInternetConnection : LoginError()
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
import net.mullvad.mullvadvpn.ui.serviceconnection.SplitTunneling
import net.mullvad.mullvadvpn.usecase.AccountExpiryNotificationUseCase
import net.mullvad.mullvadvpn.usecase.ConnectivityUseCase
import net.mullvad.mullvadvpn.usecase.EmptyPaymentUseCase
import net.mullvad.mullvadvpn.usecase.NewDeviceNotificationUseCase
import net.mullvad.mullvadvpn.usecase.PaymentUseCase
Expand Down Expand Up @@ -100,6 +101,7 @@ val uiModule = module {
single { NewDeviceNotificationUseCase(get()) }
single { PortRangeUseCase(get()) }
single { RelayListUseCase(get(), get()) }
single { ConnectivityUseCase(get()) }

single { InAppNotificationController(get(), get(), get(), get(), MainScope()) }

Expand Down Expand Up @@ -130,7 +132,7 @@ val uiModule = module {
viewModel { ConnectViewModel(get(), get(), get(), get(), get(), get(), get()) }
viewModel { DeviceListViewModel(get(), get()) }
viewModel { DeviceRevokedViewModel(get(), get()) }
viewModel { LoginViewModel(get(), get(), get()) }
viewModel { LoginViewModel(get(), get(), get(), get()) }
viewModel { PrivacyDisclaimerViewModel(get()) }
viewModel { SelectLocationViewModel(get(), get(), get()) }
viewModel { SettingsViewModel(get(), get()) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package net.mullvad.mullvadvpn.usecase

import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities

class ConnectivityUseCase(val context: Context) {
fun isInternetAvailable(): Boolean {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

val network = connectivityManager.activeNetwork
val capabilities = connectivityManager.getNetworkCapabilities(network)

return capabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.compose.state.LoginError
import net.mullvad.mullvadvpn.compose.state.LoginState
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.constant.LOGIN_TIMEOUT_MILLIS
import net.mullvad.mullvadvpn.model.AccountCreationResult
import net.mullvad.mullvadvpn.model.AccountToken
import net.mullvad.mullvadvpn.model.LoginResult
import net.mullvad.mullvadvpn.repository.AccountRepository
import net.mullvad.mullvadvpn.repository.DeviceRepository
import net.mullvad.mullvadvpn.usecase.ConnectivityUseCase
import net.mullvad.mullvadvpn.usecase.NewDeviceNotificationUseCase
import net.mullvad.mullvadvpn.util.awaitWithTimeoutOrNull

Expand All @@ -42,6 +45,7 @@ class LoginViewModel(
private val accountRepository: AccountRepository,
private val deviceRepository: DeviceRepository,
private val newDeviceNotificationUseCase: NewDeviceNotificationUseCase,
private val connectivityUseCase: ConnectivityUseCase,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) : ViewModel() {
private val _loginState = MutableStateFlow(LoginUiState.INITIAL.loginState)
Expand Down Expand Up @@ -75,6 +79,10 @@ class LoginViewModel(
}

fun login(accountToken: String) {
if (!isInternetAvailable()) {
_loginState.value = Idle(LoginError.NoInternetConnection)
return
}
_loginState.value = Loading.LoggingIn
viewModelScope.launch(dispatcher) {
// Ensure we always take at least MINIMUM_LOADING_SPINNER_TIME_MILLIS to show the
Expand Down Expand Up @@ -135,4 +143,8 @@ class LoginViewModel(
Idle(LoginError.UnableToCreateAccount)
}
}

private fun isInternetAvailable(): Boolean {
return connectivityUseCase.isInternetAvailable()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import net.mullvad.mullvadvpn.compose.state.LoginError
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.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.model.AccountCreationResult
Expand All @@ -24,6 +26,7 @@ import net.mullvad.mullvadvpn.model.DeviceListEvent
import net.mullvad.mullvadvpn.model.LoginResult
import net.mullvad.mullvadvpn.repository.AccountRepository
import net.mullvad.mullvadvpn.repository.DeviceRepository
import net.mullvad.mullvadvpn.usecase.ConnectivityUseCase
import net.mullvad.mullvadvpn.usecase.NewDeviceNotificationUseCase
import org.junit.Assert.assertEquals
import org.junit.Before
Expand All @@ -33,6 +36,7 @@ import org.junit.Test
class LoginViewModelTest {
@get:Rule val testCoroutineRule = TestCoroutineRule()

@MockK private lateinit var connectivityUseCase: ConnectivityUseCase
@MockK private lateinit var mockedAccountRepository: AccountRepository
@MockK private lateinit var mockedDeviceRepository: DeviceRepository
@MockK private lateinit var mockedNewDeviceNotificationUseCase: NewDeviceNotificationUseCase
Expand All @@ -42,9 +46,10 @@ class LoginViewModelTest {

@Before
fun setup() {

Dispatchers.setMain(UnconfinedTestDispatcher())
MockKAnnotations.init(this, relaxUnitFun = true)

every { connectivityUseCase.isInternetAvailable() } returns true
every { mockedAccountRepository.accountHistory } returns accountHistoryTestEvents
every { mockedNewDeviceNotificationUseCase.newDeviceCreated() } returns Unit

Expand All @@ -53,10 +58,32 @@ class LoginViewModelTest {
mockedAccountRepository,
mockedDeviceRepository,
mockedNewDeviceNotificationUseCase,
connectivityUseCase,
UnconfinedTestDispatcher()
)
}

@Test
fun testIsInternetAvailableWithoutInternet() = runTest {
turbineScope {
// Arrange
every { connectivityUseCase.isInternetAvailable() } returns false
val uiStates = loginViewModel.uiState.testIn(backgroundScope)

// Act
loginViewModel.login("")

// Discard default item
uiStates.awaitItem()

// Assert
assertEquals(
Idle(loginError = LoginError.NoInternetConnection),
uiStates.awaitItem().loginState
)
}
}

@Test
fun testDefaultState() = runTest {
loginViewModel.uiState.test { assertEquals(LoginUiState.INITIAL, awaitItem()) }
Expand Down
1 change: 1 addition & 0 deletions android/lib/resource/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<string name="voucher_already_used">Voucher code has already been used.</string>
<string name="error_occurred">An error occurred.</string>
<string name="settings">Settings</string>
<string name="no_internet_connection">No internet connection</string>
<string name="settings_account">Account</string>
<string name="less_than_a_day_left">less than a day left</string>
<string name="less_than_a_minute_ago">less than a minute ago</string>
Expand Down
3 changes: 3 additions & 0 deletions gui/locales/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1765,6 +1765,9 @@ msgstr ""
msgid "Mullvad services unavailable"
msgstr ""

msgid "No internet connection"
msgstr ""

msgid "Preferences"
msgstr ""

Expand Down

0 comments on commit aed7c8d

Please sign in to comment.