diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupFileIOError.java b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupFileIOError.java index 6edb15daea..812d771f00 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupFileIOError.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupFileIOError.java @@ -64,7 +64,7 @@ public enum BackupFileIOError { } } - private static @Nullable BackupFileIOError getFromException(@NonNull IOException e) { + public static @Nullable BackupFileIOError getFromException(@NonNull IOException e) { if (e instanceof FullBackupExporter.InvalidBackupStreamException) { return ATTACHMENT_TOO_LARGE; } else if (e.getMessage() != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt index 12dc9155ba..a0f0aa4a33 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt @@ -314,27 +314,38 @@ fun DeviceListScreen( ) } is DialogState.SyncingFailed -> { - if (state.dialogState.canRetry) { - Dialogs.SimpleAlertDialog( - title = stringResource(R.string.LinkDeviceFragment__sync_failure_title), - body = stringResource(R.string.LinkDeviceFragment__sync_failure_body), - confirm = stringResource(R.string.LinkDeviceFragment__sync_failure_retry_button), - onConfirm = onSyncFailureRetryRequested, - dismiss = stringResource(R.string.LinkDeviceFragment__sync_failure_dismiss_button), - onDismissRequest = onSyncFailureIgnored, - onDeny = onSyncFailureIgnored - ) - } else { - Dialogs.AdvancedAlertDialog( - title = stringResource(R.string.LinkDeviceFragment__sync_failure_title), - body = stringResource(R.string.LinkDeviceFragment__sync_failure_body_unretryable), - positive = stringResource(R.string.LinkDeviceFragment__contact_support), - onPositive = onSyncFailureContactSupport, - neutral = stringResource(R.string.LinkDeviceFragment__learn_more), - onNeutral = onSyncFailureLearnMore, - negative = stringResource(R.string.LinkDeviceFragment__continue), - onNegative = onSyncFailureIgnored - ) + when (state.dialogState.syncFailType) { + LinkDeviceSettingsState.SyncFailType.NOT_RETRYABLE -> { + Dialogs.AdvancedAlertDialog( + title = stringResource(R.string.LinkDeviceFragment__sync_failure_title), + body = stringResource(R.string.LinkDeviceFragment__sync_failure_body_unretryable), + positive = stringResource(R.string.LinkDeviceFragment__contact_support), + onPositive = onSyncFailureContactSupport, + neutral = stringResource(R.string.LinkDeviceFragment__learn_more), + onNeutral = onSyncFailureLearnMore, + negative = stringResource(R.string.LinkDeviceFragment__continue), + onNegative = onSyncFailureIgnored + ) + } + LinkDeviceSettingsState.SyncFailType.RETRYABLE -> { + Dialogs.SimpleAlertDialog( + title = stringResource(R.string.LinkDeviceFragment__sync_failure_title), + body = stringResource(R.string.LinkDeviceFragment__sync_failure_body), + confirm = stringResource(R.string.LinkDeviceFragment__sync_failure_retry_button), + onConfirm = onSyncFailureRetryRequested, + dismiss = stringResource(R.string.LinkDeviceFragment__sync_failure_dismiss_button), + onDismissRequest = onSyncFailureIgnored, + onDeny = onSyncFailureIgnored + ) + } + LinkDeviceSettingsState.SyncFailType.NOT_ENOUGH_SPACE -> { + Dialogs.SimpleMessageDialog( + message = stringResource(R.string.LinkDeviceFragment__you_dont_have_enough), + dismiss = stringResource(id = R.string.LinkDeviceFragment__ok), + onDismiss = onSyncFailureRetryRequested, + title = stringResource(R.string.LinkDeviceFragment__not_enough_storage_space) + ) + } } } DialogState.SyncingTimedOut -> { @@ -681,7 +692,11 @@ private fun DeviceListScreenSyncingFailedPreview() { Previews.Preview { DeviceListScreen( state = LinkDeviceSettingsState( - dialogState = DialogState.SyncingFailed(1, 1, false), + dialogState = DialogState.SyncingFailed( + deviceId = 1, + deviceCreatedAt = 1, + syncFailType = LinkDeviceSettingsState.SyncFailType.NOT_RETRYABLE + ), seenQrEducationSheet = true, seenBioAuthEducationSheet = true ) @@ -716,3 +731,21 @@ private fun DeviceListScreenDeviceUnlinkedPreview() { ) } } + +@SignalPreview +@Composable +private fun DeviceListScreenNotEnoughStoragePreview() { + Previews.Preview { + DeviceListScreen( + state = LinkDeviceSettingsState( + dialogState = DialogState.SyncingFailed( + deviceId = 1, + deviceCreatedAt = 1, + syncFailType = LinkDeviceSettingsState.SyncFailType.NOT_ENOUGH_SPACE + ), + seenQrEducationSheet = true, + seenBioAuthEducationSheet = true + ) + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt index 38ce770d7a..71617baf7c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt @@ -10,6 +10,7 @@ import org.signal.core.util.logging.logI import org.signal.core.util.logging.logW import org.signal.libsignal.protocol.InvalidKeyException import org.signal.libsignal.protocol.ecc.Curve +import org.thoughtcrime.securesms.backup.BackupFileIOError import org.thoughtcrime.securesms.backup.v2.ArchiveValidator import org.thoughtcrime.securesms.backup.v2.BackupRepository import org.thoughtcrime.securesms.crypto.ProfileKeyUtil @@ -263,7 +264,12 @@ object LinkDeviceRepository { ) } catch (e: Exception) { Log.w(TAG, "[createAndUploadArchive] Failed to export a backup!", e) - return LinkUploadArchiveResult.BackupCreationFailure(e) + val cause = e.cause + return if (cause is IOException && BackupFileIOError.getFromException(cause) == BackupFileIOError.NOT_ENOUGH_SPACE) { + LinkUploadArchiveResult.NotEnoughSpace + } else { + LinkUploadArchiveResult.BackupCreationFailure(e) + } } Log.d(TAG, "[createAndUploadArchive] Successfully created backup.") stopwatch.split("create-backup") @@ -440,6 +446,7 @@ object LinkDeviceRepository { data object Success : LinkUploadArchiveResult data object BackupCreationCancelled : LinkUploadArchiveResult data class BackupCreationFailure(val exception: Exception) : LinkUploadArchiveResult + data object NotEnoughSpace : LinkUploadArchiveResult data class BadRequest(val exception: IOException) : LinkUploadArchiveResult data class NetworkError(val exception: IOException) : LinkUploadArchiveResult } diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt index 82056b38fe..6d416cf7a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt @@ -32,7 +32,7 @@ data class LinkDeviceSettingsState( data object Unlinking : DialogState data class SyncingMessages(val deviceId: Int, val deviceCreatedAt: Long) : DialogState data object SyncingTimedOut : DialogState - data class SyncingFailed(val deviceId: Int, val deviceCreatedAt: Long, val canRetry: Boolean) : DialogState + data class SyncingFailed(val deviceId: Int, val deviceCreatedAt: Long, val syncFailType: SyncFailType) : DialogState data class DeviceUnlinked(val deviceCreatedAt: Long) : DialogState data object LoadingDebugLog : DialogState data object ContactSupport : DialogState @@ -55,4 +55,10 @@ data class LinkDeviceSettingsState( enum class QrCodeState { NONE, VALID_WITH_SYNC, VALID_WITHOUT_SYNC, INVALID } + + enum class SyncFailType { + NOT_RETRYABLE, + RETRYABLE, + NOT_ENOUGH_SPACE + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt index 6cf02e3b67..de71be0c90 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt @@ -339,17 +339,39 @@ class LinkDeviceViewModel : ViewModel() { } loadDevices() } - is LinkDeviceRepository.LinkUploadArchiveResult.BackupCreationFailure, - is LinkDeviceRepository.LinkUploadArchiveResult.BadRequest, - is LinkDeviceRepository.LinkUploadArchiveResult.NetworkError -> { - Log.w(TAG, "[addDeviceWithSync] Failed to upload the archive! Result: $uploadResult") - val canRetry = uploadResult !is LinkDeviceRepository.LinkUploadArchiveResult.BackupCreationFailure + is LinkDeviceRepository.LinkUploadArchiveResult.NotEnoughSpace -> { + Log.w(TAG, "[addDeviceWithSync] Failed to upload the archive because there is not enough space") _state.update { it.copy( dialogState = DialogState.SyncingFailed( deviceId = waitResult.id, deviceCreatedAt = waitResult.created, - canRetry = canRetry + syncFailType = LinkDeviceSettingsState.SyncFailType.NOT_ENOUGH_SPACE + ) + ) + } + } + is LinkDeviceRepository.LinkUploadArchiveResult.BackupCreationFailure -> { + Log.w(TAG, "[addDeviceWithSync] Failed to upload the archive because of backup creation failure") + _state.update { + it.copy( + dialogState = DialogState.SyncingFailed( + deviceId = waitResult.id, + deviceCreatedAt = waitResult.created, + syncFailType = LinkDeviceSettingsState.SyncFailType.NOT_RETRYABLE + ) + ) + } + } + is LinkDeviceRepository.LinkUploadArchiveResult.BadRequest, + is LinkDeviceRepository.LinkUploadArchiveResult.NetworkError -> { + Log.w(TAG, "[addDeviceWithSync] Failed to upload the archive! Result: $uploadResult") + _state.update { + it.copy( + dialogState = DialogState.SyncingFailed( + deviceId = waitResult.id, + deviceCreatedAt = waitResult.created, + syncFailType = LinkDeviceSettingsState.SyncFailType.RETRYABLE ) ) } @@ -457,11 +479,13 @@ class LinkDeviceViewModel : ViewModel() { } } + val shouldLaunchQrScanner = !(dialogState is DialogState.SyncingFailed && dialogState.syncFailType != LinkDeviceSettingsState.SyncFailType.NOT_ENOUGH_SPACE) + _state.update { it.copy( linkDeviceResult = LinkDeviceResult.None, dialogState = DialogState.None, - oneTimeEvent = OneTimeEvent.LaunchQrCodeScanner + oneTimeEvent = if (shouldLaunchQrScanner) OneTimeEvent.LaunchQrCodeScanner else OneTimeEvent.None ) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 75c5339165..bd0a4f2f79 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1091,6 +1091,10 @@ The device that was linked %1$s is no longer linked. OK + + Not enough storage space + + You don\'t have enough free storage space on your device to transfer your messages. Free up some space and try again, or link your device without transferring your messages.