diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupDialog.java b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupDialog.java index bfe089c5ff..eed9e945b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupDialog.java @@ -4,19 +4,25 @@ 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.view.LayoutInflater; +import android.view.View; import android.widget.Button; import android.widget.CheckBox; +import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; + import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; +import org.thoughtcrime.securesms.registration.fragments.RestoreBackupFragment; import org.thoughtcrime.securesms.service.LocalBackupListener; import org.thoughtcrime.securesms.util.BackupUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.text.AfterTextChanged; public class BackupDialog { @@ -83,4 +89,35 @@ public class BackupDialog { .create() .show(); } + + public static void showVerifyBackupPassphraseDialog(@NonNull Context context) { + View view = LayoutInflater.from(context).inflate(R.layout.enter_backup_passphrase_dialog, null); + EditText prompt = view.findViewById(R.id.restore_passphrase_input); + AlertDialog dialog = new AlertDialog.Builder(context) + .setTitle(R.string.BackupDialog_enter_backup_passphrase_to_verify) + .setView(view) + .setPositiveButton(R.string.BackupDialog_verify, null) + .setNegativeButton(android.R.string.cancel, null) + .show(); + + Button positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE); + positiveButton.setEnabled(false); + + RestoreBackupFragment.PassphraseAsYouTypeFormatter formatter = new RestoreBackupFragment.PassphraseAsYouTypeFormatter(); + + prompt.addTextChangedListener(new AfterTextChanged(editable -> { + formatter.afterTextChanged(editable); + positiveButton.setEnabled(editable.length() == BackupUtil.PASSPHRASE_LENGTH); + })); + + positiveButton.setOnClickListener(v -> { + String passphrase = prompt.getText().toString(); + if (passphrase.equals(BackupPassphrase.get(context))) { + Toast.makeText(context, R.string.BackupDialog_you_successfully_entered_your_backup_passphrase, Toast.LENGTH_SHORT).show(); + dialog.dismiss(); + } else { + Toast.makeText(context, R.string.BackupDialog_passphrase_was_not_correct, Toast.LENGTH_SHORT).show(); + } + }); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java index fa37c51315..6dcb745e38 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.preferences; import android.Manifest; -import android.annotation.SuppressLint; import android.content.Context; import android.os.Bundle; import android.text.TextUtils; @@ -52,6 +51,8 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { .setOnPreferenceClickListener(new BackupClickListener()); findPreference(TextSecurePreferences.BACKUP_NOW) .setOnPreferenceClickListener(new BackupCreateListener()); + findPreference(TextSecurePreferences.BACKUP_PASSPHRASE_VERIFY) + .setOnPreferenceClickListener(new BackupVerifyListener()); initializeListSummary((ListPreference) findPreference(TextSecurePreferences.MESSAGE_BODY_TEXT_SIZE_PREF)); @@ -145,7 +146,6 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { } private class BackupCreateListener implements Preference.OnPreferenceClickListener { - @SuppressLint("StaticFieldLeak") @Override public boolean onPreferenceClick(Preference preference) { Permissions.with(ChatsPreferenceFragment.this) @@ -162,6 +162,14 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { } } + private class BackupVerifyListener implements Preference.OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(Preference preference) { + BackupDialog.showVerifyBackupPassphraseDialog(requireContext()); + return true; + } + } + private class MediaDownloadChangeListener implements Preference.OnPreferenceChangeListener { @SuppressWarnings("unchecked") @Override public boolean onPreferenceChange(Preference preference, Object newValue) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RestoreBackupFragment.java b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RestoreBackupFragment.java index 347224960c..6f1d9e4c2f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RestoreBackupFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RestoreBackupFragment.java @@ -281,7 +281,7 @@ public final class RestoreBackupFragment extends BaseRegistrationFragment { FAILURE_UNKNOWN } - private static class PassphraseAsYouTypeFormatter implements TextWatcher { + public static class PassphraseAsYouTypeFormatter implements TextWatcher { private static final int GROUP_SIZE = 5; @@ -292,7 +292,7 @@ public final class RestoreBackupFragment extends BaseRegistrationFragment { addSpans(editable); } - private void removeSpans(Editable editable) { + private static void removeSpans(Editable editable) { SpaceSpan[] paddingSpans = editable.getSpans(0, editable.length(), SpaceSpan.class); for (SpaceSpan span : paddingSpans) { @@ -300,15 +300,15 @@ public final class RestoreBackupFragment extends BaseRegistrationFragment { } } - private void addSpans(Editable editable) { + private static void addSpans(Editable editable) { final int length = editable.length(); for (int i = GROUP_SIZE; i < length; i += GROUP_SIZE) { editable.setSpan(new SpaceSpan(), i - 1, i, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } - if (editable.length() > 30) { - editable.delete(30, editable.length()); + if (editable.length() > BackupUtil.PASSPHRASE_LENGTH) { + editable.delete(BackupUtil.PASSPHRASE_LENGTH, editable.length()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.java index 02d91bc658..8bb85c96b7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.java @@ -23,6 +23,8 @@ public class BackupUtil { private static final String TAG = BackupUtil.class.getSimpleName(); + public static final int PASSPHRASE_LENGTH = 30; + public static @NonNull String getLastBackupTime(@NonNull Context context, @NonNull Locale locale) { try { BackupInfo backup = getLatestBackup(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 111ad68e6a..7eec21b5e9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -151,6 +151,7 @@ public class TextSecurePreferences { private static final String ENCRYPTED_BACKUP_PASSPHRASE = "pref_encrypted_backup_passphrase"; private static final String BACKUP_TIME = "pref_backup_next_time"; public static final String BACKUP_NOW = "pref_backup_create"; + public static final String BACKUP_PASSPHRASE_VERIFY = "pref_backup_passphrase_verify"; public static final String SCREEN_LOCK = "pref_android_screen_lock"; public static final String SCREEN_LOCK_TIMEOUT = "pref_android_screen_lock_timeout"; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fbf638d76d..c4e4ae7637 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1778,6 +1778,8 @@ Chat backups Backup chats to external storage Create backup + Verify backup passphrase + Test your backup passphrase and verify that it matches Enter backup passphrase Restore Cannot import backups from newer versions of Signal @@ -1795,6 +1797,10 @@ Disable and delete all local backups? Delete backups Copied to clipboard + Enter your backup passphrase to verify + Verify + You successfully entered your backup passphrase + Passphrase was not correct Signal 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/app/src/main/res/xml/preferences_chats.xml b/app/src/main/res/xml/preferences_chats.xml index 0069da2eb7..4d2f658df9 100644 --- a/app/src/main/res/xml/preferences_chats.xml +++ b/app/src/main/res/xml/preferences_chats.xml @@ -75,6 +75,13 @@ android:dependency="pref_backup_enabled" tools:summary="Last backup: 3 days ago"/> + +