diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupFileIOError.java b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupFileIOError.java new file mode 100644 index 0000000000..ea0bc6ed61 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupFileIOError.java @@ -0,0 +1,77 @@ +package org.thoughtcrime.securesms.backup; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; + +import org.thoughtcrime.securesms.ApplicationPreferencesActivity; +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.notifications.NotificationChannels; + +import java.io.IOException; + +public enum BackupFileIOError { + ACCESS_ERROR(R.string.LocalBackupJobApi29_backups_disabled, R.string.LocalBackupJobApi29_your_backup_directory_has_been_deleted_or_moved), + FILE_TOO_LARGE(R.string.LocalBackupJobApi29_backup_failed, R.string.LocalBackupJobApi29_your_backup_file_is_too_large), + NOT_ENOUGH_SPACE(R.string.LocalBackupJobApi29_backup_failed, R.string.LocalBackupJobApi29_there_is_not_enough_space), + UNKNOWN(R.string.LocalBackupJobApi29_backup_failed, R.string.LocalBackupJobApi29_backup_failed_for_an_unknown_reason); + + private static final short BACKUP_FAILED_ID = 31321; + + private final @StringRes int titleId; + private final @StringRes int messageId; + + BackupFileIOError(@StringRes int titleId, @StringRes int messageId) { + this.titleId = titleId; + this.messageId = messageId; + } + + public static void clearNotification(@NonNull Context context) { + NotificationManagerCompat.from(context).cancel(BACKUP_FAILED_ID); + } + + public void postNotification(@NonNull Context context) { + Intent intent = new Intent(context, ApplicationPreferencesActivity.class); + + intent.putExtra(ApplicationPreferencesActivity.LAUNCH_TO_BACKUPS_FRAGMENT, true); + + PendingIntent pendingIntent = PendingIntent.getActivity(context, -1, intent, 0); + Notification backupFailedNotification = new NotificationCompat.Builder(context, NotificationChannels.BACKUPS) + .setSmallIcon(R.drawable.ic_signal_backup) + .setContentTitle(context.getString(titleId)) + .setContentText(context.getString(messageId)) + .setContentIntent(pendingIntent) + .build(); + + NotificationManagerCompat.from(context) + .notify(BACKUP_FAILED_ID, backupFailedNotification); + } + + public static void postNotificationForException(@NonNull Context context, @NonNull IOException e, int runAttempt) { + BackupFileIOError error = getFromException(e); + + if (error != null) { + error.postNotification(context); + } + + if (error == null && runAttempt > 0) { + UNKNOWN.postNotification(context); + } + } + + private static @Nullable BackupFileIOError getFromException(@NonNull IOException e) { + if (e.getMessage() != null) { + if (e.getMessage().contains("EFBIG")) return FILE_TOO_LARGE; + else if (e.getMessage().contains("ENOSPC")) return NOT_ENOUGH_SPACE; + } + + return null; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java index d0bbfa0473..622793eb2c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java @@ -7,6 +7,7 @@ import android.content.Context; import androidx.annotation.NonNull; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.backup.BackupFileIOError; import org.thoughtcrime.securesms.backup.BackupPassphrase; import org.thoughtcrime.securesms.backup.FullBackupExporter; import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; @@ -79,6 +80,8 @@ public final class LocalBackupJob extends BaseJob { public void onRun() throws NoExternalStorageException, IOException { Log.i(TAG, "Executing backup job..."); + BackupFileIOError.clearNotification(context); + if (!Permissions.hasAll(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { throw new IOException("No external storage permission!"); } @@ -119,6 +122,9 @@ public final class LocalBackupJob extends BaseJob { Log.w(TAG, "Failed to rename temp file"); throw new IOException("Renaming temporary backup file failed!"); } + } catch (IOException e) { + BackupFileIOError.postNotificationForException(context, e, getRunAttempt()); + throw e; } finally { if (tempFile.exists()) { if (tempFile.delete()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJobApi29.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJobApi29.java index d89f47e611..2f384ba508 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJobApi29.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJobApi29.java @@ -13,6 +13,7 @@ import androidx.documentfile.provider.DocumentFile; import org.thoughtcrime.securesms.ApplicationPreferencesActivity; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.backup.BackupFileIOError; import org.thoughtcrime.securesms.backup.BackupPassphrase; import org.thoughtcrime.securesms.backup.FullBackupExporter; import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; @@ -43,8 +44,6 @@ public final class LocalBackupJobApi29 extends BaseJob { private static final String TAG = Log.tag(LocalBackupJobApi29.class); - private static final short BACKUP_FAILED_ID = 31321; - public static final String TEMP_BACKUP_FILE_PREFIX = ".backup"; public static final String TEMP_BACKUP_FILE_SUFFIX = ".tmp"; @@ -66,7 +65,7 @@ public final class LocalBackupJobApi29 extends BaseJob { public void onRun() throws IOException { Log.i(TAG, "Executing backup job..."); - NotificationManagerCompat.from(context).cancel(BACKUP_FAILED_ID); + BackupFileIOError.clearNotification(context); if (!BackupUtil.isUserSelectionRequired(context)) { throw new IOException("Wrong backup job!"); @@ -91,7 +90,7 @@ public final class LocalBackupJobApi29 extends BaseJob { if (backupDirectory == null || !backupDirectory.canWrite()) { BackupUtil.disableBackups(context); - postBackupsDisabledNotification(); + BackupFileIOError.ACCESS_ERROR.postNotification(context); throw new IOException("Cannot write to backup directory location."); } @@ -123,6 +122,9 @@ public final class LocalBackupJobApi29 extends BaseJob { Log.w(TAG, "Failed to rename temp file"); throw new IOException("Renaming temporary backup file failed!"); } + } catch (IOException e) { + BackupFileIOError.postNotificationForException(context, e, getRunAttempt()); + throw e; } finally { DocumentFile fileToCleanUp = backupDirectory.findFile(temporaryName); if (fileToCleanUp != null) { @@ -162,23 +164,6 @@ public final class LocalBackupJobApi29 extends BaseJob { public void onFailure() { } - private void postBackupsDisabledNotification() { - Intent intent = new Intent(context, ApplicationPreferencesActivity.class); - - intent.putExtra(ApplicationPreferencesActivity.LAUNCH_TO_BACKUPS_FRAGMENT, true); - - PendingIntent pendingIntent = PendingIntent.getActivity(context, -1, intent, 0); - Notification backupFailedNotification = new NotificationCompat.Builder(context, NotificationChannels.BACKUPS) - .setSmallIcon(R.drawable.ic_signal_backup) - .setContentTitle(context.getString(R.string.LocalBackupJobApi29_backups_disabled)) - .setContentText(context.getString(R.string.LocalBackupJobApi29_your_backup_directory_has_been_deleted_or_moved)) - .setContentIntent(pendingIntent) - .build(); - - NotificationManagerCompat.from(context) - .notify(BACKUP_FAILED_ID, backupFailedNotification); - } - public static class Factory implements Job.Factory { @Override public @NonNull diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 60f0f1ca17..f2b3e487fa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2621,7 +2621,11 @@ In progress Creating backup… Backups disabled. + Backup failed. Your backup directory has been deleted or moved. + Your backup file is too large to store on this volume. + There is not enough space to store your backup. + Backup failed for an unknown reason. %d messages so far Please enter the verification code sent to %s. Wrong number