kopia lustrzana https://github.com/ryukoposting/Signal-Android
Notify about accidentally disabled backups.
rodzic
d1478c5ce0
commit
b5d6cb2a8d
|
@ -18,10 +18,10 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public enum BackupFileIOError {
|
public enum BackupFileIOError {
|
||||||
ACCESS_ERROR(R.string.LocalBackupJobApi29_backups_disabled, R.string.LocalBackupJobApi29_your_backup_directory_has_been_deleted_or_moved),
|
ACCESS_ERROR(R.string.LocalBackupJobApi29_backup_failed, 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),
|
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),
|
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);
|
UNKNOWN(R.string.LocalBackupJobApi29_backup_failed, R.string.LocalBackupJobApi29_tap_to_manage_backups);
|
||||||
|
|
||||||
private static final short BACKUP_FAILED_ID = 31321;
|
private static final short BACKUP_FAILED_ID = 31321;
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ public enum BackupFileIOError {
|
||||||
intent.putExtra(ApplicationPreferencesActivity.LAUNCH_TO_BACKUPS_FRAGMENT, true);
|
intent.putExtra(ApplicationPreferencesActivity.LAUNCH_TO_BACKUPS_FRAGMENT, true);
|
||||||
|
|
||||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, -1, intent, 0);
|
PendingIntent pendingIntent = PendingIntent.getActivity(context, -1, intent, 0);
|
||||||
Notification backupFailedNotification = new NotificationCompat.Builder(context, NotificationChannels.BACKUPS)
|
Notification backupFailedNotification = new NotificationCompat.Builder(context, NotificationChannels.FAILURES)
|
||||||
.setSmallIcon(R.drawable.ic_signal_backup)
|
.setSmallIcon(R.drawable.ic_signal_backup)
|
||||||
.setContentTitle(context.getString(titleId))
|
.setContentTitle(context.getString(titleId))
|
||||||
.setContentText(context.getString(messageId))
|
.setContentText(context.getString(messageId))
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.jobmanager.migrations.SendReadReceiptsJobMigra
|
||||||
import org.thoughtcrime.securesms.migrations.AttributesMigrationJob;
|
import org.thoughtcrime.securesms.migrations.AttributesMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.AvatarIdRemovalMigrationJob;
|
import org.thoughtcrime.securesms.migrations.AvatarIdRemovalMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.AvatarMigrationJob;
|
import org.thoughtcrime.securesms.migrations.AvatarMigrationJob;
|
||||||
|
import org.thoughtcrime.securesms.migrations.BackupNotificationMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.CachedAttachmentsMigrationJob;
|
import org.thoughtcrime.securesms.migrations.CachedAttachmentsMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.DatabaseMigrationJob;
|
import org.thoughtcrime.securesms.migrations.DatabaseMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.DirectoryRefreshMigrationJob;
|
import org.thoughtcrime.securesms.migrations.DirectoryRefreshMigrationJob;
|
||||||
|
@ -135,6 +136,7 @@ public final class JobManagerFactories {
|
||||||
put(AttributesMigrationJob.KEY, new AttributesMigrationJob.Factory());
|
put(AttributesMigrationJob.KEY, new AttributesMigrationJob.Factory());
|
||||||
put(AvatarIdRemovalMigrationJob.KEY, new AvatarIdRemovalMigrationJob.Factory());
|
put(AvatarIdRemovalMigrationJob.KEY, new AvatarIdRemovalMigrationJob.Factory());
|
||||||
put(AvatarMigrationJob.KEY, new AvatarMigrationJob.Factory());
|
put(AvatarMigrationJob.KEY, new AvatarMigrationJob.Factory());
|
||||||
|
put(BackupNotificationMigrationJob.KEY, new BackupNotificationMigrationJob.Factory());
|
||||||
put(CachedAttachmentsMigrationJob.KEY, new CachedAttachmentsMigrationJob.Factory());
|
put(CachedAttachmentsMigrationJob.KEY, new CachedAttachmentsMigrationJob.Factory());
|
||||||
put(DatabaseMigrationJob.KEY, new DatabaseMigrationJob.Factory());
|
put(DatabaseMigrationJob.KEY, new DatabaseMigrationJob.Factory());
|
||||||
put(DirectoryRefreshMigrationJob.KEY, new DirectoryRefreshMigrationJob.Factory());
|
put(DirectoryRefreshMigrationJob.KEY, new DirectoryRefreshMigrationJob.Factory());
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
@ -94,7 +93,7 @@ public final class LocalBackupJob extends BaseJob {
|
||||||
notification.setIndeterminateProgress();
|
notification.setIndeterminateProgress();
|
||||||
|
|
||||||
String backupPassword = BackupPassphrase.get(context);
|
String backupPassword = BackupPassphrase.get(context);
|
||||||
File backupDirectory = StorageUtil.getBackupDirectory();
|
File backupDirectory = StorageUtil.getOrCreateBackupDirectory();
|
||||||
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US).format(new Date());
|
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US).format(new Date());
|
||||||
String fileName = String.format("signal-%s.backup", timestamp);
|
String fileName = String.format("signal-%s.backup", timestamp);
|
||||||
File backupFile = new File(backupDirectory, fileName);
|
File backupFile = new File(backupDirectory, fileName);
|
||||||
|
|
|
@ -89,7 +89,6 @@ public final class LocalBackupJobApi29 extends BaseJob {
|
||||||
String fileName = String.format("signal-%s.backup", timestamp);
|
String fileName = String.format("signal-%s.backup", timestamp);
|
||||||
|
|
||||||
if (backupDirectory == null || !backupDirectory.canWrite()) {
|
if (backupDirectory == null || !backupDirectory.canWrite()) {
|
||||||
BackupUtil.disableBackups(context);
|
|
||||||
BackupFileIOError.ACCESS_ERROR.postNotification(context);
|
BackupFileIOError.ACCESS_ERROR.postNotification(context);
|
||||||
throw new IOException("Cannot write to backup directory location.");
|
throw new IOException("Cannot write to backup directory location.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class ApplicationMigrations {
|
||||||
|
|
||||||
private static final int LEGACY_CANONICAL_VERSION = 455;
|
private static final int LEGACY_CANONICAL_VERSION = 455;
|
||||||
|
|
||||||
public static final int CURRENT_VERSION = 22;
|
public static final int CURRENT_VERSION = 23;
|
||||||
|
|
||||||
private static final class Version {
|
private static final class Version {
|
||||||
static final int LEGACY = 1;
|
static final int LEGACY = 1;
|
||||||
|
@ -64,6 +64,7 @@ public class ApplicationMigrations {
|
||||||
static final int GV2 = 20;
|
static final int GV2 = 20;
|
||||||
static final int GV2_2 = 21;
|
static final int GV2_2 = 21;
|
||||||
static final int CDS = 22;
|
static final int CDS = 22;
|
||||||
|
static final int BACKUP_NOTIFICATION = 23;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -267,6 +268,10 @@ public class ApplicationMigrations {
|
||||||
jobs.put(Version.CDS, new DirectoryRefreshMigrationJob());
|
jobs.put(Version.CDS, new DirectoryRefreshMigrationJob());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastSeenVersion < Version.BACKUP_NOTIFICATION) {
|
||||||
|
jobs.put(Version.BACKUP_NOTIFICATION, new BackupNotificationMigrationJob());
|
||||||
|
}
|
||||||
|
|
||||||
return jobs;
|
return jobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package org.thoughtcrime.securesms.migrations;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.backup.BackupFileIOError;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.util.BackupUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles showing a notification if we think backups were unintentionally disabled.
|
||||||
|
*/
|
||||||
|
public final class BackupNotificationMigrationJob extends MigrationJob {
|
||||||
|
|
||||||
|
private static final String TAG = Log.tag(BackupNotificationMigrationJob.class);
|
||||||
|
|
||||||
|
public static final String KEY = "BackupNotificationMigrationJob";
|
||||||
|
|
||||||
|
BackupNotificationMigrationJob() {
|
||||||
|
this(new Parameters.Builder().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private BackupNotificationMigrationJob(@NonNull Parameters parameters) {
|
||||||
|
super(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUiBlocking() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull String getFactoryKey() {
|
||||||
|
return KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void performMigration() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 29 && !TextSecurePreferences.isBackupEnabled(context) && BackupUtil.hasBackupFiles(context)) {
|
||||||
|
Log.w(TAG, "Stranded backup! Notifying.");
|
||||||
|
BackupFileIOError.UNKNOWN.postNotification(context);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, String.format(Locale.US, "Does not meet criteria. API: %d, BackupsEnabled: %s, HasFiles: %s",
|
||||||
|
Build.VERSION.SDK_INT,
|
||||||
|
TextSecurePreferences.isBackupEnabled(context),
|
||||||
|
BackupUtil.hasBackupFiles(context)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean shouldRetry(@NonNull Exception e) {
|
||||||
|
return e instanceof IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements Job.Factory<BackupNotificationMigrationJob> {
|
||||||
|
@Override
|
||||||
|
public @NonNull BackupNotificationMigrationJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||||
|
return new BackupNotificationMigrationJob(parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,6 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.text.HtmlCompat;
|
import androidx.core.text.HtmlCompat;
|
||||||
import androidx.documentfile.provider.DocumentFile;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
|
@ -165,7 +164,7 @@ public class BackupsPreferenceFragment extends Fragment {
|
||||||
} else if (StorageUtil.canWriteInSignalStorageDir()) {
|
} else if (StorageUtil.canWriteInSignalStorageDir()) {
|
||||||
try {
|
try {
|
||||||
folder.setVisibility(View.VISIBLE);
|
folder.setVisibility(View.VISIBLE);
|
||||||
folderName.setText(StorageUtil.getBackupDirectory().getPath());
|
folderName.setText(StorageUtil.getOrCreateBackupDirectory().getPath());
|
||||||
} catch (NoExternalStorageException e) {
|
} catch (NoExternalStorageException e) {
|
||||||
Log.w(TAG, "Could not display folder name.", e);
|
Log.w(TAG, "Could not display folder name.", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import androidx.documentfile.provider.DocumentFile;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.backup.BackupPassphrase;
|
import org.thoughtcrime.securesms.backup.BackupPassphrase;
|
||||||
import org.thoughtcrime.securesms.database.NoExternalStorageException;
|
import org.thoughtcrime.securesms.database.NoExternalStorageException;
|
||||||
import org.thoughtcrime.securesms.database.documents.Document;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
@ -181,7 +180,7 @@ public class BackupUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<BackupInfo> getAllBackupsNewestFirstLegacy() throws NoExternalStorageException {
|
private static List<BackupInfo> getAllBackupsNewestFirstLegacy() throws NoExternalStorageException {
|
||||||
File backupDirectory = StorageUtil.getBackupDirectory();
|
File backupDirectory = StorageUtil.getOrCreateBackupDirectory();
|
||||||
File[] files = backupDirectory.listFiles();
|
File[] files = backupDirectory.listFiles();
|
||||||
List<BackupInfo> backups = new ArrayList<>(files.length);
|
List<BackupInfo> backups = new ArrayList<>(files.length);
|
||||||
|
|
||||||
|
@ -213,6 +212,20 @@ public class BackupUtil {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean hasBackupFiles(@NonNull Context context) {
|
||||||
|
if (Permissions.hasAll(context, Manifest.permission.READ_EXTERNAL_STORAGE)) {
|
||||||
|
try {
|
||||||
|
File directory = StorageUtil.getBackupDirectory();
|
||||||
|
return directory.exists() && directory.isDirectory() && directory.listFiles().length > 0;
|
||||||
|
} catch (NoExternalStorageException e) {
|
||||||
|
Log.w(TAG, "Failed to read storage!", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static long getBackupTimestamp(@NonNull String backupName) {
|
private static long getBackupTimestamp(@NonNull String backupName) {
|
||||||
String[] prefixSuffix = backupName.split("[.]");
|
String[] prefixSuffix = backupName.split("[.]");
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.database.NoExternalStorageException;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -28,13 +29,26 @@ public class StorageUtil {
|
||||||
|
|
||||||
private static final String PRODUCTION_PACKAGE_ID = "org.thoughtcrime.securesms";
|
private static final String PRODUCTION_PACKAGE_ID = "org.thoughtcrime.securesms";
|
||||||
|
|
||||||
public static File getBackupDirectory() throws NoExternalStorageException {
|
public static File getOrCreateBackupDirectory() throws NoExternalStorageException {
|
||||||
File storage = Environment.getExternalStorageDirectory();
|
File storage = Environment.getExternalStorageDirectory();
|
||||||
|
|
||||||
if (!storage.canWrite()) {
|
if (!storage.canWrite()) {
|
||||||
throw new NoExternalStorageException();
|
throw new NoExternalStorageException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File backups = getBackupDirectory();
|
||||||
|
|
||||||
|
if (!backups.exists()) {
|
||||||
|
if (!backups.mkdirs()) {
|
||||||
|
throw new NoExternalStorageException("Unable to create backup directory...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return backups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getBackupDirectory() throws NoExternalStorageException {
|
||||||
|
File storage = Environment.getExternalStorageDirectory();
|
||||||
File signal = new File(storage, "Signal");
|
File signal = new File(storage, "Signal");
|
||||||
File backups = new File(signal, "Backups");
|
File backups = new File(signal, "Backups");
|
||||||
|
|
||||||
|
@ -43,12 +57,6 @@ public class StorageUtil {
|
||||||
backups = new File(backups, BuildConfig.APPLICATION_ID.substring(PRODUCTION_PACKAGE_ID.length() + 1));
|
backups = new File(backups, BuildConfig.APPLICATION_ID.substring(PRODUCTION_PACKAGE_ID.length() + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!backups.exists()) {
|
|
||||||
if (!backups.mkdirs()) {
|
|
||||||
throw new NoExternalStorageException("Unable to create backup directory...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return backups;
|
return backups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2625,12 +2625,11 @@
|
||||||
<string name="ChatsPreferenceFragment_last_backup_s">Last backup: %s</string>
|
<string name="ChatsPreferenceFragment_last_backup_s">Last backup: %s</string>
|
||||||
<string name="ChatsPreferenceFragment_in_progress">In progress</string>
|
<string name="ChatsPreferenceFragment_in_progress">In progress</string>
|
||||||
<string name="LocalBackupJob_creating_backup">Creating backup…</string>
|
<string name="LocalBackupJob_creating_backup">Creating backup…</string>
|
||||||
<string name="LocalBackupJobApi29_backups_disabled">Backups disabled.</string>
|
<string name="LocalBackupJobApi29_backup_failed">Backup failed</string>
|
||||||
<string name="LocalBackupJobApi29_backup_failed">Backup failed.</string>
|
|
||||||
<string name="LocalBackupJobApi29_your_backup_directory_has_been_deleted_or_moved">Your backup directory has been deleted or moved.</string>
|
<string name="LocalBackupJobApi29_your_backup_directory_has_been_deleted_or_moved">Your backup directory has been deleted or moved.</string>
|
||||||
<string name="LocalBackupJobApi29_your_backup_file_is_too_large">Your backup file is too large to store on this volume.</string>
|
<string name="LocalBackupJobApi29_your_backup_file_is_too_large">Your backup file is too large to store on this volume.</string>
|
||||||
<string name="LocalBackupJobApi29_there_is_not_enough_space">There is not enough space to store your backup.</string>
|
<string name="LocalBackupJobApi29_there_is_not_enough_space">There is not enough space to store your backup.</string>
|
||||||
<string name="LocalBackupJobApi29_backup_failed_for_an_unknown_reason">Backup failed for an unknown reason.</string>
|
<string name="LocalBackupJobApi29_tap_to_manage_backups">Tap to manage backups.</string>
|
||||||
<string name="ProgressPreference_d_messages_so_far">%d messages so far</string>
|
<string name="ProgressPreference_d_messages_so_far">%d messages so far</string>
|
||||||
<string name="RegistrationActivity_please_enter_the_verification_code_sent_to_s">Please enter the verification code sent to %s.</string>
|
<string name="RegistrationActivity_please_enter_the_verification_code_sent_to_s">Please enter the verification code sent to %s.</string>
|
||||||
<string name="RegistrationActivity_wrong_number">Wrong number</string>
|
<string name="RegistrationActivity_wrong_number">Wrong number</string>
|
||||||
|
|
Ładowanie…
Reference in New Issue