Require key rotation to disable pins.

This commit is contained in:
Greyson Parrelli
2025-11-04 12:33:38 -05:00
committed by Michelle Tang
parent 4cce6d3c86
commit e6f11c7443
9 changed files with 123 additions and 14 deletions

View File

@@ -18,11 +18,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.fragment.app.setFragmentResultListener
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
@@ -33,11 +35,13 @@ import org.signal.core.ui.compose.Rows
import org.signal.core.ui.compose.Scaffolds
import org.signal.core.ui.compose.Snackbars
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.settings.app.backups.remote.BackupKeyDisplayFragment
import org.thoughtcrime.securesms.compose.ComposeFragment
import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity
import org.thoughtcrime.securesms.payments.backup.PaymentsRecoveryStartFragmentArgs.Builder
import org.thoughtcrime.securesms.payments.preferences.PaymentsActivity
import org.thoughtcrime.securesms.pin.PinOptOutDialog
import org.thoughtcrime.securesms.util.navigation.safeNavigate
/**
* Fragment which allows user to enable or disable their PIN
@@ -56,7 +60,7 @@ class AdvancedPinSettingsFragment : ComposeFragment() {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
viewModel.event.collectLatest {
when (it) {
AdvancedPinSettingsViewModel.Event.SHOW_OPT_OUT_DIALOG -> PinOptOutDialog.show(requireContext()) {
AdvancedPinSettingsViewModel.Event.SHOW_BACKUPS_DISABLED_OPT_OUT_DIALOG -> PinOptOutDialog.show(requireContext(), true) {
viewModel.onPinOptOutSuccess()
displayOptOutSnackbar()
}
@@ -70,10 +74,20 @@ class AdvancedPinSettingsFragment : ComposeFragment() {
startActivity(intent)
}
AdvancedPinSettingsViewModel.Event.SHOW_PIN_DISABLED_SNACKBAR -> {
displayOptOutSnackbar()
}
}
}
}
}
setFragmentResultListener(BackupKeyDisplayFragment.AEP_ROTATION_KEY) { key, bundle ->
val didRotate = bundle.getBoolean(BackupKeyDisplayFragment.AEP_ROTATION_KEY, false)
if (didRotate) {
viewModel.onAepRotatedForPinDisable()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@@ -109,7 +123,22 @@ class AdvancedPinSettingsFragment : ComposeFragment() {
viewModel.dismissDialog()
}
)
else -> Unit
AdvancedPinSettingsViewModel.Dialog.ROTATE_AEP -> RotateAepDialog(
onConfirm = {
viewModel.dismissDialog()
val bundle = Bundle()
bundle.putBoolean("start_with_key_rotation", true)
findNavController().safeNavigate(
AdvancedPinSettingsFragmentDirections
.actionAdvancedPinSettingsFragmentToBackupKeyDisplayFragment()
.setStartWithKeyRotation(true)
)
},
onDismiss = {
viewModel.dismissDialog()
}
)
AdvancedPinSettingsViewModel.Dialog.NONE -> Unit
}
}
@@ -191,6 +220,21 @@ private fun RecordPaymentsRecoveryPhraseDialog(
)
}
@Composable
private fun RotateAepDialog(
onConfirm: () -> Unit,
onDismiss: () -> Unit
) {
Dialogs.SimpleAlertDialog(
title = stringResource(R.string.AdvancedPinSettingsFragment_rotate_aep_dialog_title),
body = stringResource(R.string.AdvancedPinSettingsFragment_rotate_aep_dialog_body),
confirm = stringResource(R.string.AdvancedPinSettingsFragment_rotate_aep_dialog_positive_button),
onConfirm = onConfirm,
dismiss = stringResource(android.R.string.cancel),
onDismiss = onDismiss
)
}
@DayNightPreviews
@Composable
private fun AdvancedPinSettingsFragmentContentEnabledPreview() {
@@ -226,3 +270,11 @@ private fun RecordPaymentsRecoveryPhraseDialogPreview() {
RecordPaymentsRecoveryPhraseDialog({}, {})
}
}
@DayNightPreviews
@Composable
private fun RotateAepDialogPreview() {
Previews.Preview {
RotateAepDialog({}, {})
}
}

View File

@@ -14,19 +14,22 @@ import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.pin.SvrRepository
class AdvancedPinSettingsViewModel : ViewModel() {
enum class Dialog {
NONE,
REGISTRATION_LOCK,
RECORD_PAYMENTS_RECOVERY_PHRASE
RECORD_PAYMENTS_RECOVERY_PHRASE,
ROTATE_AEP
}
enum class Event {
SHOW_OPT_OUT_DIALOG,
SHOW_BACKUPS_DISABLED_OPT_OUT_DIALOG,
LAUNCH_PIN_CREATION_FLOW,
LAUNCH_RECOVERY_PHRASE_HANDLING
LAUNCH_RECOVERY_PHRASE_HANDLING,
SHOW_PIN_DISABLED_SNACKBAR
}
private val internalDialog = MutableStateFlow(Dialog.NONE)
@@ -52,9 +55,12 @@ class AdvancedPinSettingsViewModel : ViewModel() {
!enabled && SignalStore.payments.mobileCoinPaymentsEnabled() && !SignalStore.payments.userConfirmedMnemonic -> {
internalDialog.value = Dialog.RECORD_PAYMENTS_RECOVERY_PHRASE
}
!enabled -> {
!enabled && SignalStore.backup.areBackupsEnabled -> {
internalDialog.value = Dialog.ROTATE_AEP
}
!enabled && !SignalStore.backup.areBackupsEnabled -> {
dismissDialog()
emitEvent(Event.SHOW_OPT_OUT_DIALOG)
emitEvent(Event.SHOW_BACKUPS_DISABLED_OPT_OUT_DIALOG)
}
else -> {
dismissDialog()
@@ -75,6 +81,14 @@ class AdvancedPinSettingsViewModel : ViewModel() {
internalDialog.value = Dialog.NONE
}
fun onAepRotatedForPinDisable() {
internalDialog.value = Dialog.NONE
viewModelScope.launch {
SvrRepository.optOutOfPin(rotateAep = false)
emitEvent(Event.SHOW_PIN_DISABLED_SNACKBAR)
}
}
private fun emitEvent(event: Event) {
viewModelScope.launch {
internalEvent.emit(event)