diff options
Diffstat (limited to 'android')
10 files changed, 64 insertions, 6 deletions
diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index ef57ebdef7..76be993cd6 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -22,8 +22,12 @@ Line wrap the file at 100 chars. Th * **Security**: in case of vulnerabilities. ## [Unreleased] + +### Added +- Prompt password manager to store new account number on account creation. + ### Changed -- Disable Wireguard port setting when a obfuscation is selected since it is not used when an +- Disable Wireguard port setting when a obfuscation is selected since it is not used when an obfuscation is applied. ### Removed diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index d73f673626..51d91ad6b9 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -378,8 +378,9 @@ dependencies { implementation(libs.commons.validator) implementation(libs.androidx.activity.compose) implementation(libs.androidx.datastore) - implementation(libs.androidx.ktx) implementation(libs.androidx.coresplashscreen) + implementation(libs.androidx.credentials) + implementation(libs.androidx.ktx) implementation(libs.androidx.lifecycle.runtime) implementation(libs.androidx.lifecycle.viewmodel) implementation(libs.androidx.lifecycle.runtime.compose) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index dd660cda4b..7066e06bc9 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -33,7 +33,7 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher" android:theme="@style/Theme.App.Starting" - tools:ignore="GoogleAppIndexingWarning"> + tools:ignore="CredManMissingDal,CredentialDependency,GoogleAppIndexingWarning"> <!-- MainActivity Must be exported in order to be launchable. 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 5146d65bb0..20396c4849 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 @@ -29,9 +29,13 @@ 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 androidx.credentials.CreatePasswordRequest +import androidx.credentials.CredentialManager +import androidx.credentials.exceptions.CreateCredentialException import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.dropUnlessResumed +import co.touchlab.kermit.Logger import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.generated.NavGraphs @@ -140,6 +144,17 @@ fun Welcome( snackbarHostState.showSnackbarImmediately( message = context.getString(R.string.error_occurred) ) + is WelcomeViewModel.UiSideEffect.StoreCredentialsRequest -> { + // UserId is not allowed to be empty + val createPasswordRequest = + CreatePasswordRequest(id = "-", password = uiSideEffect.accountNumber.value) + val credentialsManager = CredentialManager.create(context) + try { + credentialsManager.createCredential(context, createPasswordRequest) + } catch (e: CreateCredentialException) { + Logger.w("Unable to create Credentials") + } + } } } 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 0e91390262..e22055cba7 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 @@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.compose.state.WelcomeUiState import net.mullvad.mullvadvpn.lib.common.util.isAfterNowInstant +import net.mullvad.mullvadvpn.lib.model.AccountNumber import net.mullvad.mullvadvpn.lib.model.WebsiteAuthToken import net.mullvad.mullvadvpn.lib.shared.AccountRepository import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy @@ -39,12 +40,18 @@ class WelcomeViewModel( val uiState = combine( connectionProxy.tunnelState, - deviceRepository.deviceState.filterNotNull(), + deviceRepository.deviceState.filterNotNull().onEach { + viewModelScope.launch { + it.accountNumber()?.let { accountNumber -> + _uiSideEffect.send(UiSideEffect.StoreCredentialsRequest(accountNumber)) + } + } + }, paymentUseCase.paymentAvailability, ) { tunnelState, accountState, paymentAvailability -> WelcomeUiState( tunnelState = tunnelState, - accountNumber = accountState.token(), + accountNumber = accountState.accountNumber(), deviceName = accountState.displayName(), showSitePayment = !isPlayBuild, billingPaymentState = paymentAvailability?.toPaymentState(), @@ -122,6 +129,8 @@ class WelcomeViewModel( data object OpenConnectScreen : UiSideEffect + data class StoreCredentialsRequest(val accountNumber: AccountNumber) : UiSideEffect + data object GenericError : UiSideEffect } diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 69abe85c2e..29a3c3e105 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -12,6 +12,7 @@ android-volley = "1.2.1" androidx-activitycompose = "1.10.1" androidx-appcompat = "1.7.0" androidx-ktx = "1.15.0" +androidx-credentials = "1.3.0" androidx-coresplashscreen = "1.1.0-rc01" androidx-datastore = "1.1.3" androidx-espresso = "3.6.1" @@ -85,6 +86,7 @@ android-volley = { module = "com.android.volley:volley", version.ref = "android- androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activitycompose" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } androidx-coresplashscreen = { module = "androidx.core:core-splashscreen", version.ref = "androidx-coresplashscreen" } +androidx-credentials = { module = "androidx.credentials:credentials", version.ref = "androidx-credentials" } androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "androidx-datastore" } androidx-espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" } androidx-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-ktx" } diff --git a/android/gradle/verification-metadata.xml b/android/gradle/verification-metadata.xml index e08cd1d92b..29c3c60ff7 100644 --- a/android/gradle/verification-metadata.xml +++ b/android/gradle/verification-metadata.xml @@ -187,6 +187,7 @@ <trusting group="androidx.collection"/> <trusting group="androidx.constraintlayout"/> <trusting group="androidx.core"/> + <trusting group="androidx.credentials" name="credentials"/> <trusting group="androidx.databinding"/> <trusting group="androidx.datastore"/> <trusting group="androidx.fragment"/> @@ -413,6 +414,11 @@ <sha256 value="9516c2ae44284ea0bd3d0eade0ee638879b708cbe31e3af92ba96c300604ebc3" origin="Generated by Gradle"/> </artifact> </component> + <component group="androidx.annotation" name="annotation" version="1.5.0"> + <artifact name="annotation-1.5.0.module"> + <sha256 value="4c84feee2db891ff6b97d613a0d40ab96ce297b034a6927ca8479f09e82d7c2e" origin="Generated by Gradle"/> + </artifact> + </component> <component group="androidx.annotation" name="annotation" version="1.6.0"> <artifact name="annotation-1.6.0.module"> <sha256 value="6146b6138643b2ac0590df509dd51abaea769c79fd7602eb217168fe5af78cd2" origin="Generated by Gradle"/> @@ -1477,6 +1483,14 @@ <sha256 value="11386cfa46cbbfddb6a4059f14354c00691cf65d3d63c3618818a83326ef3c7f" origin="Generated by Gradle"/> </artifact> </component> + <component group="androidx.credentials" name="credentials" version="1.3.0"> + <artifact name="credentials-1.3.0.aar"> + <sha256 value="b33c1a3e2d41fc3a163dd161d3334d2510d9b2086ed923c60a6f79ee22b78984" origin="Generated by Gradle"/> + </artifact> + <artifact name="credentials-1.3.0.module"> + <sha256 value="bd5d6f9628aa958f03823e7a913a83cf11a403e5df2150491f4ce7e1684a708a" origin="Generated by Gradle"/> + </artifact> + </component> <component group="androidx.cursoradapter" name="cursoradapter" version="1.0.0"> <artifact name="cursoradapter-1.0.0.aar"> <sha256 value="a81c8fe78815fa47df5b749deb52727ad11f9397da58b16017f4eb2c11e28564" origin="Generated by Gradle"/> diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/DeviceState.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/DeviceState.kt index ccec166a47..12131832ed 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/DeviceState.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/DeviceState.kt @@ -15,7 +15,7 @@ sealed class DeviceState : Parcelable { return (this as? LoggedIn)?.device?.displayName() } - fun token(): AccountNumber? { + fun accountNumber(): AccountNumber? { return (this as? LoggedIn)?.accountNumber } } diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt index 2b9b008ad0..c940f9bfba 100644 --- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt @@ -152,4 +152,15 @@ class AppInteractor( device.findObjectWithTimeout(By.desc("Remove")).click() clickActionButtonByText("Yes, log out device") } + + fun dismissStorePasswordPromptIfShown() { + try { + device.waitForIdle() + val selector = By.textContains("password") + device.wait(Until.hasObject(selector), DEFAULT_TIMEOUT) + device.pressBack() + } catch (e: IllegalArgumentException) { + // This is OK since it means the password prompt wasn't shown. + } + } } diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/CreateAccountMockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/CreateAccountMockApiTest.kt index 05418cb34b..9b5cf85b95 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/CreateAccountMockApiTest.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/CreateAccountMockApiTest.kt @@ -24,6 +24,8 @@ class CreateAccountMockApiTest : MockApiTest() { app.waitForLoginPrompt() app.attemptCreateAccount() + app.dismissStorePasswordPromptIfShown() + // Assert val expectedResult = "1234 1234 1234 1234" app.ensureAccountCreated(expectedResult) |
