diff --git a/res/values/strings.xml b/res/values/strings.xml index 70b662aba8..d801f1dfa7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1499,6 +1499,7 @@ Disable and delete all local backups? Delete backups Copied to clipboard + Failed to activate backups. Please try again or contact support. Session requires external storage permission in order to create backups, but it has been permanently denied. Please continue to app settings, select \"Permissions\" and enable \"Storage\". Last backup: %s In progress diff --git a/src/org/thoughtcrime/securesms/backup/BackupDialog.java b/src/org/thoughtcrime/securesms/backup/BackupDialog.java index 053aec7673..c7ca55ad35 100644 --- a/src/org/thoughtcrime/securesms/backup/BackupDialog.java +++ b/src/org/thoughtcrime/securesms/backup/BackupDialog.java @@ -4,32 +4,36 @@ package org.thoughtcrime.securesms.backup; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; - import android.widget.Button; import android.widget.CheckBox; import android.widget.TextView; import android.widget.Toast; -import network.loki.messenger.R; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; -import org.thoughtcrime.securesms.service.LocalBackupListener; +import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.util.BackupDirSelector; import org.thoughtcrime.securesms.util.BackupUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; +import java.io.IOException; + +import network.loki.messenger.R; + public class BackupDialog { + private static final String TAG = "BackupDialog"; public static void showEnableBackupDialog( @NonNull Context context, @NonNull SwitchPreferenceCompat preference, @NonNull BackupDirSelector backupDirSelector) { - String[] password = BackupUtil.generateBackupPassphrase(); - AlertDialog dialog = new AlertDialog.Builder(context) + String[] password = BackupUtil.generateBackupPassphrase(); + String passwordSt = Util.join(password, " "); + + AlertDialog dialog = new AlertDialog.Builder(context) .setTitle(R.string.BackupDialog_enable_local_backups) .setView(R.layout.backup_enable_dialog) .setPositiveButton(R.string.BackupDialog_enable_backups, null) @@ -42,9 +46,16 @@ public class BackupDialog { CheckBox confirmationCheckBox = dialog.findViewById(R.id.confirmation_check); if (confirmationCheckBox.isChecked()) { backupDirSelector.selectBackupDir(true, uri -> { - BackupPassphrase.set(context, Util.join(password, " ")); - TextSecurePreferences.setBackupEnabled(context, true); - LocalBackupListener.schedule(context); + try { + BackupUtil.enableBackups(context, passwordSt); + } catch (IOException e) { + Log.e(TAG, "Failed to activate backups.", e); + Toast.makeText(context, + context.getString(R.string.BackupDialog_activation_error), + Toast.LENGTH_LONG) + .show(); + return; + } preference.setChecked(true); created.dismiss(); @@ -71,7 +82,7 @@ public class BackupDialog { textView.setOnClickListener(v -> checkBox.toggle()); dialog.findViewById(R.id.number_table).setOnClickListener(v -> { - ((ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("text", Util.join(password, " "))); + ((ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("text", passwordSt)); Toast.makeText(context, R.string.BackupDialog_copied_to_clipboard, Toast.LENGTH_LONG).show(); }); @@ -84,10 +95,7 @@ public class BackupDialog { .setMessage(R.string.BackupDialog_disable_and_delete_all_local_backups) .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(R.string.BackupDialog_delete_backups_statement, (dialog, which) -> { - BackupPassphrase.set(context, null); - TextSecurePreferences.setBackupEnabled(context, false); - BackupUtil.deleteAllBackupFiles(context); - BackupUtil.setBackupDirUri(context, null); + BackupUtil.disableBackups(context, true); preference.setChecked(false); }) .create() diff --git a/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java index 1814e90fa9..5d12ab3eb9 100644 --- a/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java +++ b/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java @@ -170,7 +170,6 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { ApplicationContext.getInstance(getContext()) .getJobManager() .add(new LocalBackupJob()); - return true; } } diff --git a/src/org/thoughtcrime/securesms/util/BackupUtil.kt b/src/org/thoughtcrime/securesms/util/BackupUtil.kt index 2a70b72874..c23dbc772c 100644 --- a/src/org/thoughtcrime/securesms/util/BackupUtil.kt +++ b/src/org/thoughtcrime/securesms/util/BackupUtil.kt @@ -18,8 +18,10 @@ import org.thoughtcrime.securesms.backup.FullBackupExporter import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.model.BackupFileRecord +import org.thoughtcrime.securesms.service.LocalBackupListener import org.whispersystems.libsignal.util.ByteUtil import java.io.IOException +import java.lang.IllegalStateException import java.security.SecureRandom import java.text.SimpleDateFormat import java.util.* @@ -28,6 +30,44 @@ import kotlin.jvm.Throws object BackupUtil { private const val TAG = "BackupUtil" + /** + * Set app-wide configuration to enable the backups and schedule them. + * + * Make sure that the backup dir is selected prior activating the backup. + * Use [BackupDirSelector] or [setBackupDirUri] manually. + */ + @JvmStatic + @Throws(IOException::class) + fun enableBackups(context: Context, password: String) { + val backupDir = getBackupDirUri(context) + if (backupDir == null || validateDirAccess(context, backupDir)) { + throw IOException("Backup dir is not set or invalid.") + } + + BackupPassphrase.set(context, password) + TextSecurePreferences.setBackupEnabled(context, true) + LocalBackupListener.schedule(context) + } + + /** + * Set app-wide configuration to disable the backups. + * + * This call resets the backup dir value. + * Make sure to call [setBackupDirUri] prior next call to [enableBackups]. + * + * @param deleteBackupFiles if true, deletes all the previously created backup files + * (if the app has access to them) + */ + @JvmStatic + fun disableBackups(context: Context, deleteBackupFiles: Boolean) { + BackupPassphrase.set(context, null) + TextSecurePreferences.setBackupEnabled(context, false) + if (deleteBackupFiles) { + deleteAllBackupFiles(context) + } + setBackupDirUri(context, null) + } + @JvmStatic fun getLastBackupTimeString(context: Context, locale: Locale): String { val timestamp = DatabaseFactory.getLokiBackupFilesDatabase(context).getLastBackupFileTime() diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 341cc25ccc..0390f5c580 100644 --- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -141,7 +141,7 @@ public class TextSecurePreferences { private static final String ACTIVE_SIGNED_PRE_KEY_ID = "pref_active_signed_pre_key_id"; private static final String NEXT_SIGNED_PRE_KEY_ID = "pref_next_signed_pre_key_id"; - public static final String BACKUP_ENABLED = "pref_backup_enabled"; + public static final String BACKUP_ENABLED = "pref_backup_enabled_v2"; private static final String BACKUP_PASSPHRASE = "pref_backup_passphrase"; private static final String ENCRYPTED_BACKUP_PASSPHRASE = "pref_encrypted_backup_passphrase"; private static final String BACKUP_TIME = "pref_backup_next_time";