Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 32 additions & 0 deletions app/src/main/java/org/wikipedia/login/LoginActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -67,6 +74,7 @@ class LoginActivity : BaseActivity() {
doLogin()
}
CreateAccountActivity.RESULT_ACCOUNT_NOT_CREATED -> finish()
CreateAccountActivity.RESULT_ACCOUNT_LOGIN -> requestSavedCredentials()
}
}

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
Loading