Add capability support on quick restore QR codes.

This commit is contained in:
Greyson Parrelli
2026-09-21 11:53:45 -04:00
parent 30445437cd
commit 38d11da00b
8 changed files with 65 additions and 2 deletions
@@ -50,6 +50,11 @@ object MediaSendV3QrRepository : MediaSendQrRepository {
}
private fun handleReReg(qrData: String): MediaSendQrRepository.QrCheckResult {
return MediaSendQrRepository.QrCheckResult.ReRegistration(qrData)
return if (QuickRegistrationRepository.isNewDeviceCompatible(qrData)) {
MediaSendQrRepository.QrCheckResult.ReRegistration(qrData)
} else {
Log.w(TAG, "New device cannot accept a phone-numberless account")
MediaSendQrRepository.QrCheckResult.ReRegistrationOutdatedDevice
}
}
}
@@ -48,6 +48,26 @@ object QuickRegistrationRepository {
return uri.host == REREG_URI_HOST && ephemeralId.isNotNullOrBlank() && publicKeyEncoded.isNotNullOrBlank()
}
/**
* Whether the new device that generated [data] can handle this account. A phone-numberless account can only be
* transferred to a new device that advertises [Capability.Numberless].
*/
fun isNewDeviceCompatible(data: String): Boolean {
if (!SignalStore.account.isPhoneNumberless) {
return true
}
return Capability.Numberless in Uri.parse(data).getCapabilities()
}
private fun Uri.getCapabilities(): Set<Capability> {
return this.getQueryParameter("capabilities")
?.split(",")
?.mapNotNull { value -> Capability.entries.firstOrNull { it.value == value } }
?.toSet()
?: emptySet()
}
/**
* Send registration provisioning message to new device.
*/
@@ -57,6 +77,11 @@ object QuickRegistrationRepository {
return TransferAccountResult.FAILED
}
if (!isNewDeviceCompatible(reRegisterUri)) {
Log.w(TAG, "New device cannot accept a phone-numberless account")
return TransferAccountResult.NEW_DEVICE_OUTDATED
}
val uri = Uri.parse(reRegisterUri)
try {
@@ -165,6 +190,11 @@ object QuickRegistrationRepository {
enum class TransferAccountResult {
SUCCESS,
FAILED
FAILED,
NEW_DEVICE_OUTDATED
}
private enum class Capability(val value: String) {
Numberless("nopni")
}
}
@@ -131,6 +131,14 @@ fun TransferAccountScreen(
)
}
QuickRegistrationRepository.TransferAccountResult.NEW_DEVICE_OUTDATED -> {
Dialogs.SimpleMessageDialog(
message = stringResource(R.string.TransferAccount_the_device_you_are_transferring_to_is_out_of_date),
dismiss = stringResource(android.R.string.ok),
onDismiss = { emitter(TransferScreenEvents.ErrorDialogDismissed) }
)
}
null -> Unit
}
}
+2
View File
@@ -9021,6 +9021,8 @@
<string name="TransferAccount_continue_on_your_other_device">Continue on your other device</string>
<!-- Old device Transfer account bottom sheet dialog body -->
<string name="TransferAccount_continue_on_your_other_device_details">Continue transferring your account on your other device.</string>
<!-- Old device Transfer account error dialog body shown when the new device is too old to accept an account that has no phone number -->
<string name="TransferAccount_the_device_you_are_transferring_to_is_out_of_date">The device you\'re transferring to is out of date. Update Signal on that device and try again.</string>
<!-- Prepare Device screen title for quick restore flow -->
<string name="PrepareDevice_title">Getting your device ready</string>
@@ -125,6 +125,7 @@ class MediaSendFlowViewModel(
internal val usernameScannedDialog = DialogController<String>()
internal val linkedDeviceScannedDialog = DialogController<Unit>()
internal val reRegistrationOutdatedDeviceDialog = DialogController<Unit>()
internal val discardMediaDialog = DialogController<Unit>()
internal val addToGroupStoryDialog = DialogController<MediaRecipientId>()
@@ -189,6 +190,7 @@ class MediaSendFlowViewModel(
}
MediaSendQrRepository.QrCheckResult.None -> Unit
is MediaSendQrRepository.QrCheckResult.ReRegistration -> sendHudCommand(MediaSendFlowHudCommand.GoToQuickTransfer(qrData))
MediaSendQrRepository.QrCheckResult.ReRegistrationOutdatedDevice -> reRegistrationOutdatedDeviceDialog.show(Unit)
is MediaSendQrRepository.QrCheckResult.Username -> {
when (usernameScannedDialog.show(result.username)) {
DialogResult.POSITIVE -> sendHudCommand(MediaSendFlowHudCommand.GoToConversation(result.recipientId))
@@ -13,5 +13,8 @@ interface MediaSendQrRepository {
data class Username(val recipientId: MediaRecipientId, val username: String) : QrCheckResult
data object LinkDevice : QrCheckResult
data class ReRegistration(val qrData: String) : QrCheckResult
/** Scanned a valid re-registration QR code from a new device that can't accept this account. */
data object ReRegistrationOutdatedDevice : QrCheckResult
}
}
@@ -60,6 +60,14 @@ fun MediaSendScreen(
onDismissRequest = onDismissRequest
)
}
viewModel.reRegistrationOutdatedDeviceDialog.Content { _, onDismissRequest, _, _, _ ->
Dialogs.SimpleMessageDialog(
title = stringResource(R.string.ReRegistrationScannedDialog__transfer_failed_title),
message = stringResource(R.string.ReRegistrationScannedDialog__the_device_you_are_transferring_to_is_out_of_date),
dismiss = stringResource(android.R.string.ok),
onDismiss = onDismissRequest
)
}
viewModel.addToGroupStoryDialog.Content { recipientId, onDismissRequest, onConfirm, _, onDeny ->
val groupName: String by LocalDisplayNameProvider.current(recipientId.id)
@@ -231,6 +231,11 @@
<!-- The label of a dialog asking the user if they would like to continue to the linked device settings screen. -->
<string name="LinkedDeviceScannedDialog__device_link_dialog_continue">Continue</string>
<!-- The title of a dialog shown when the user scanned a transfer QR code from a device that is too old to accept their account. -->
<string name="ReRegistrationScannedDialog__transfer_failed_title">Can\'t transfer account</string>
<!-- The body of a dialog shown when the user scanned a transfer QR code from a device that is too old to accept their account. -->
<string name="ReRegistrationScannedDialog__the_device_you_are_transferring_to_is_out_of_date">The device you\'re transferring to is out of date. Update Signal on that device and try again.</string>
<!-- Gif label displayed in a pill in the editor specifying the type of media being sent. -->
<plurals name="MediaEditScreen__gif">
<item quantity="one">%1$d Gif</item>