Implement password manager button in login details screen.

This commit is contained in:
Greyson Parrelli
2026-09-02 16:11:27 -03:00
committed by Alex Hart
parent 483e7d24bd
commit 478b18aed8
8 changed files with 49 additions and 4 deletions
@@ -16,6 +16,9 @@ sealed interface SignalLoginViewDetailsAction {
/** Leave the screen. */
data object NavigateBack : SignalLoginViewDetailsAction
/** Launch the system credential manager UI so the user can store the login in their password manager. */
data object LaunchSaveToPasswordManager : SignalLoginViewDetailsAction
/** Launch the system document picker so the user can choose where to save the login PDF. */
data object LaunchSaveAsPdf : SignalLoginViewDetailsAction
}
@@ -17,6 +17,7 @@ import kotlinx.coroutines.launch
import org.signal.core.ui.compose.CollectActions
import org.signal.core.ui.compose.ComposeFragment
import org.signal.core.util.Result
import org.signal.passwordmanager.SignalCredentialManager
import org.signal.signallogin.pdf.SignalLoginPdfRenderer
import org.signal.signallogin.viewdetails.SignalLoginViewDetailsScreen
@@ -54,6 +55,15 @@ class SignalLoginViewDetailsFragment : ComposeFragment() {
private fun handleAction(action: SignalLoginViewDetailsAction) {
when (action) {
SignalLoginViewDetailsAction.NavigateBack -> requireActivity().onBackPressedDispatcher.onBackPressed()
SignalLoginViewDetailsAction.LaunchSaveToPasswordManager -> {
lifecycleScope.launch {
SignalCredentialManager.saveCredential(
activityContext = requireActivity(),
username = viewModel.state.value.accountKey,
password = viewModel.state.value.recoveryKey
)
}
}
SignalLoginViewDetailsAction.LaunchSaveAsPdf -> savePdfLauncher.launch(SignalLoginPdfRenderer.suggestedFileName(requireContext()))
}
}
@@ -45,8 +45,7 @@ class SignalLoginViewDetailsViewModel(
_actions.send(SignalLoginViewDetailsAction.NavigateBack)
}
SignalLoginViewDetailsScreenEvents.SaveToPasswordManagerClicked -> {
// TODO [phonenumberless] Store the credentials via the credential manager.
Log.i(TAG, "Save to password manager clicked, but the flow isn't implemented yet.")
_actions.send(SignalLoginViewDetailsAction.LaunchSaveToPasswordManager)
}
SignalLoginViewDetailsScreenEvents.SaveAsPdfClicked -> {
_actions.send(SignalLoginViewDetailsAction.LaunchSaveAsPdf)
@@ -77,6 +77,17 @@ class SignalLoginViewDetailsViewModelTest {
assertThat(actions).containsExactly(SignalLoginViewDetailsAction.NavigateBack)
}
@Test
fun `SaveToPasswordManagerClicked launches the save to password manager flow`() = runTest(testDispatcher) {
val viewModel = SignalLoginViewDetailsViewModel(repository)
val actions = mutableListOf<SignalLoginViewDetailsAction>()
backgroundScope.launch { viewModel.actions.toList(actions) }
viewModel.onEvent(SignalLoginViewDetailsScreenEvents.SaveToPasswordManagerClicked)
assertThat(actions).containsExactly(SignalLoginViewDetailsAction.LaunchSaveToPasswordManager)
}
@Test
fun `SaveAsPdfClicked launches the save as PDF flow`() = runTest(testDispatcher) {
val viewModel = SignalLoginViewDetailsViewModel(repository)
@@ -709,6 +709,16 @@ private fun EntryProviderScope<NavKey>.navigationEntries(
CollectActions(viewModel.actions) { action ->
when (action) {
SignalLoginViewDetailsScreenActions.LaunchSaveToPasswordManager -> {
scope.launch {
SignalCredentialManager.saveCredential(
activityContext = context,
username = viewModel.state.value.accountKey,
password = viewModel.state.value.recoveryKey
)
}
}
SignalLoginViewDetailsScreenActions.LaunchSaveAsPdf -> savePdfLauncher.launch(SignalLoginPdfRenderer.suggestedFileName(context))
}
}
@@ -6,6 +6,9 @@
package org.signal.registration.screens.signallogindetails
sealed interface SignalLoginViewDetailsScreenActions {
/** Launch the system credential manager UI so the user can store the login in their password manager. */
data object LaunchSaveToPasswordManager : SignalLoginViewDetailsScreenActions
/** Launch the system document picker so the user can choose where to save the login PDF. */
data object LaunchSaveAsPdf : SignalLoginViewDetailsScreenActions
}
@@ -67,8 +67,7 @@ class SignalLoginViewDetailsViewModel(
}
is SignalLoginViewDetailsScreenEvents.SaveToPasswordManagerClicked -> {
// TODO [phonenumberless] Store the credentials via the credential manager.
Log.i(TAG, "Save to password manager clicked, but the flow isn't implemented yet.")
_actions.trySend(SignalLoginViewDetailsScreenActions.LaunchSaveToPasswordManager)
}
is SignalLoginViewDetailsScreenEvents.SaveAsPdfClicked -> {
@@ -71,6 +71,16 @@ class SignalLoginViewDetailsViewModelTest {
assertThat(parentEvents).containsExactly(RegistrationFlowEvent.NavigateBack)
}
@Test
fun `SaveToPasswordManagerClicked launches the save to password manager flow`() = runTest(testDispatcher) {
val actions = mutableListOf<SignalLoginViewDetailsScreenActions>()
backgroundScope.launch { viewModel.actions.toList(actions) }
viewModel.onEvent(SignalLoginViewDetailsScreenEvents.SaveToPasswordManagerClicked)
assertThat(actions).containsExactly(SignalLoginViewDetailsScreenActions.LaunchSaveToPasswordManager)
}
@Test
fun `SaveAsPdfClicked launches the save as PDF flow`() = runTest(testDispatcher) {
val actions = mutableListOf<SignalLoginViewDetailsScreenActions>()