diff --git a/app/build.gradle b/app/build.gradle index 73e367414e7..2c84b083f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -219,6 +219,14 @@ dependencies { implementation libs.androidx.glance.preview implementation libs.androidx.glance.appwidget.preview + // For saving/retrieving user credentials via Credential Manager + implementation libs.androidx.credentials + prodImplementation libs.androidx.credentials.play.services.auth + betaImplementation libs.androidx.credentials.play.services.auth + alphaImplementation libs.androidx.credentials.play.services.auth + devImplementation libs.androidx.credentials.play.services.auth + customImplementation libs.androidx.credentials.play.services.auth + // For language detection during editing prodImplementation libs.com.google.mlkit.language.id betaImplementation libs.com.google.mlkit.language.id diff --git a/app/src/main/java/org/wikipedia/login/LoginActivity.kt b/app/src/main/java/org/wikipedia/login/LoginActivity.kt index dfad6e85720..1365d261c81 100644 --- a/app/src/main/java/org/wikipedia/login/LoginActivity.kt +++ b/app/src/main/java/org/wikipedia/login/LoginActivity.kt @@ -13,6 +13,13 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.core.net.toUri import androidx.core.view.isVisible import androidx.core.widget.addTextChangedListener +import androidx.credentials.CredentialManager +import androidx.credentials.GetCredentialRequest +import androidx.credentials.GetPasswordOption +import androidx.credentials.PasswordCredential +import androidx.credentials.exceptions.GetCredentialCancellationException +import androidx.credentials.exceptions.GetCredentialException +import androidx.credentials.exceptions.NoCredentialException import androidx.lifecycle.lifecycleScope import com.google.android.material.textfield.TextInputLayout import kotlinx.coroutines.launch @@ -67,6 +74,7 @@ class LoginActivity : BaseActivity() { doLogin() } CreateAccountActivity.RESULT_ACCOUNT_NOT_CREATED -> finish() + CreateAccountActivity.RESULT_ACCOUNT_LOGIN -> requestSavedCredentials() } } @@ -135,6 +143,8 @@ class LoginActivity : BaseActivity() { if (savedInstanceState == null && !intent.hasExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE) && intent.getBooleanExtra(CREATE_ACCOUNT_FIRST, true)) { startCreateAccountActivity() + } else if (savedInstanceState == null) { + requestSavedCredentials() } setAllViewsClickListener() @@ -225,6 +235,28 @@ class LoginActivity : BaseActivity() { createAccountLauncher.launch(CreateAccountActivity.newIntent(this, loginSource)) } + private fun requestSavedCredentials() { + val credentialManager = CredentialManager.create(this) + val request = GetCredentialRequest(listOf(GetPasswordOption())) + lifecycleScope.launch { + try { + val result = credentialManager.getCredential(this@LoginActivity, request) + val credential = result.credential + if (credential is PasswordCredential) { + binding.loginUsernameText.editText?.setText(credential.id) + binding.loginPasswordInput.editText?.setText(credential.password) + doLogin() + } + } catch (e: GetCredentialCancellationException) { + L.d("Credential retrieval cancelled by user.") + } catch (e: NoCredentialException) { + L.d("No saved credentials found.") + } catch (e: GetCredentialException) { + L.e("Failed to retrieve saved credentials", e) + } + } + } + private fun onLoginSuccess() { val isReadingChallenge = loginSource == SOURCE_READING_CHALLENGE instrument?.submitInteraction(action = "success", actionContext = if (isReadingChallenge) mapOf("invoke_source" to loginSource) else null) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1336485d43a..a1b4de3d588 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -52,9 +52,12 @@ composeBom = "2026.05.00" composeActivity = "1.13.0" composeViewModel = "2.10.0" uiGraphics = "1.11.1" +credentialsVersion = "1.6.0" [libraries] +androidx-credentials = { module = "androidx.credentials:credentials", version.ref = "credentialsVersion" } +androidx-credentials-play-services-auth = { module = "androidx.credentials:credentials-play-services-auth", version.ref = "credentialsVersion" } androidx-espresso-intents = { module = "androidx.test.espresso:espresso-intents", version.ref = "espressoVersion" } androidx-junit = { module = "androidx.test.ext:junit", version.ref = "junitVersion" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }