Show backup progress as a percentage.
This commit is contained in:
parent
4f73e36d72
commit
8014a70134
15 changed files with 310 additions and 46 deletions
|
@ -0,0 +1,30 @@
|
||||||
|
package org.thoughtcrime.securesms.backup
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.AttachmentDatabase
|
||||||
|
import org.thoughtcrime.securesms.database.GroupReceiptDatabase
|
||||||
|
import org.thoughtcrime.securesms.database.MmsDatabase
|
||||||
|
import org.thoughtcrime.securesms.database.SmsDatabase
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries used by backup exporter to estimate total counts for various complicated tables.
|
||||||
|
*/
|
||||||
|
object BackupCountQueries {
|
||||||
|
|
||||||
|
const val mmsCount: String = "SELECT COUNT(*) FROM ${MmsDatabase.TABLE_NAME} WHERE ${MmsDatabase.EXPIRES_IN} <= 0 AND ${MmsDatabase.VIEW_ONCE} <= 0"
|
||||||
|
|
||||||
|
const val smsCount: String = "SELECT COUNT(*) FROM ${SmsDatabase.TABLE_NAME} WHERE ${SmsDatabase.EXPIRES_IN} <= 0"
|
||||||
|
|
||||||
|
@get:JvmStatic
|
||||||
|
val groupReceiptCount: String = """
|
||||||
|
SELECT COUNT(*) FROM ${GroupReceiptDatabase.TABLE_NAME}
|
||||||
|
INNER JOIN ${MmsDatabase.TABLE_NAME} ON ${GroupReceiptDatabase.TABLE_NAME}.${GroupReceiptDatabase.MMS_ID} = ${MmsDatabase.TABLE_NAME}.${MmsDatabase.ID}
|
||||||
|
WHERE ${MmsDatabase.TABLE_NAME}.${MmsDatabase.EXPIRES_IN} <= 0 AND ${MmsDatabase.TABLE_NAME}.${MmsDatabase.VIEW_ONCE} <= 0
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
@get:JvmStatic
|
||||||
|
val attachmentCount: String = """
|
||||||
|
SELECT COUNT(*) FROM ${AttachmentDatabase.TABLE_NAME}
|
||||||
|
INNER JOIN ${MmsDatabase.TABLE_NAME} ON ${AttachmentDatabase.TABLE_NAME}.${AttachmentDatabase.MMS_ID} = ${MmsDatabase.TABLE_NAME}.${MmsDatabase.ID}
|
||||||
|
WHERE ${MmsDatabase.TABLE_NAME}.${MmsDatabase.EXPIRES_IN} <= 0 AND ${MmsDatabase.TABLE_NAME}.${MmsDatabase.VIEW_ONCE} <= 0
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
|
@ -13,13 +13,12 @@ import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
public abstract class FullBackupBase {
|
public abstract class FullBackupBase {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
private static final int DIGEST_ROUNDS = 250_000;
|
||||||
private static final String TAG = Log.tag(FullBackupBase.class);
|
|
||||||
|
|
||||||
static class BackupStream {
|
static class BackupStream {
|
||||||
static @NonNull byte[] getBackupKey(@NonNull String passphrase, @Nullable byte[] salt) {
|
static @NonNull byte[] getBackupKey(@NonNull String passphrase, @Nullable byte[] salt) {
|
||||||
try {
|
try {
|
||||||
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, 0));
|
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, 0, 0));
|
||||||
|
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
||||||
byte[] input = passphrase.replace(" ", "").getBytes();
|
byte[] input = passphrase.replace(" ", "").getBytes();
|
||||||
|
@ -27,8 +26,8 @@ public abstract class FullBackupBase {
|
||||||
|
|
||||||
if (salt != null) digest.update(salt);
|
if (salt != null) digest.update(salt);
|
||||||
|
|
||||||
for (int i=0;i<250000;i++) {
|
for (int i = 0; i < DIGEST_ROUNDS; i++) {
|
||||||
if (i % 1000 == 0) EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, 0));
|
if (i % 1000 == 0) EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, 0, 0));
|
||||||
digest.update(hash);
|
digest.update(hash);
|
||||||
hash = digest.digest(input);
|
hash = digest.digest(input);
|
||||||
}
|
}
|
||||||
|
@ -47,20 +46,34 @@ public abstract class FullBackupBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Type type;
|
private final Type type;
|
||||||
private final int count;
|
private final long count;
|
||||||
|
private final long estimatedTotalCount;
|
||||||
|
|
||||||
BackupEvent(Type type, int count) {
|
BackupEvent(Type type, long count, long estimatedTotalCount) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.count = count;
|
this.count = count;
|
||||||
|
this.estimatedTotalCount = estimatedTotalCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type getType() {
|
public Type getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCount() {
|
public long getCount() {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getEstimatedTotalCount() {
|
||||||
|
return estimatedTotalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCompletionPercentage() {
|
||||||
|
if (estimatedTotalCount == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.min(99.9f, (double) count * 100L / (double) estimatedTotalCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,11 @@ public class FullBackupExporter extends FullBackupBase {
|
||||||
|
|
||||||
private static final String TAG = Log.tag(FullBackupExporter.class);
|
private static final String TAG = Log.tag(FullBackupExporter.class);
|
||||||
|
|
||||||
|
private static final long DATABASE_VERSION_RECORD_COUNT = 1L;
|
||||||
|
private static final long TABLE_RECORD_COUNT_MULTIPLIER = 3L;
|
||||||
|
private static final long IDENTITY_KEY_BACKUP_RECORD_COUNT = 2L;
|
||||||
|
private static final long FINAL_MESSAGE_COUNT = 1L;
|
||||||
|
|
||||||
private static final Set<String> BLACKLISTED_TABLES = SetUtil.newHashSet(
|
private static final Set<String> BLACKLISTED_TABLES = SetUtil.newHashSet(
|
||||||
SignedPreKeyDatabase.TABLE_NAME,
|
SignedPreKeyDatabase.TABLE_NAME,
|
||||||
OneTimePreKeyDatabase.TABLE_NAME,
|
OneTimePreKeyDatabase.TABLE_NAME,
|
||||||
|
@ -134,58 +139,62 @@ public class FullBackupExporter extends FullBackupBase {
|
||||||
@NonNull BackupCancellationSignal cancellationSignal)
|
@NonNull BackupCancellationSignal cancellationSignal)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
BackupFrameOutputStream outputStream = new BackupFrameOutputStream(fileOutputStream, passphrase);
|
BackupFrameOutputStream outputStream = new BackupFrameOutputStream(fileOutputStream, passphrase);
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
long estimatedCountOutside = 0L;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
outputStream.writeDatabaseVersion(input.getVersion());
|
outputStream.writeDatabaseVersion(input.getVersion());
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
List<String> tables = exportSchema(input, outputStream);
|
List<String> tables = exportSchema(input, outputStream);
|
||||||
count += tables.size() * 3;
|
count += tables.size() * TABLE_RECORD_COUNT_MULTIPLIER;
|
||||||
|
|
||||||
|
final long estimatedCount = calculateCount(context, input, tables);
|
||||||
|
estimatedCountOutside = estimatedCount;
|
||||||
|
|
||||||
Stopwatch stopwatch = new Stopwatch("Backup");
|
Stopwatch stopwatch = new Stopwatch("Backup");
|
||||||
|
|
||||||
for (String table : tables) {
|
for (String table : tables) {
|
||||||
throwIfCanceled(cancellationSignal);
|
throwIfCanceled(cancellationSignal);
|
||||||
if (table.equals(MmsDatabase.TABLE_NAME)) {
|
if (table.equals(MmsDatabase.TABLE_NAME)) {
|
||||||
count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringMmsMessage, null, count, cancellationSignal);
|
count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringMmsMessage, null, count, estimatedCount, cancellationSignal);
|
||||||
} else if (table.equals(SmsDatabase.TABLE_NAME)) {
|
} else if (table.equals(SmsDatabase.TABLE_NAME)) {
|
||||||
count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringSmsMessage, null, count, cancellationSignal);
|
count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringSmsMessage, null, count, estimatedCount, cancellationSignal);
|
||||||
} else if (table.equals(GroupReceiptDatabase.TABLE_NAME)) {
|
} else if (table.equals(GroupReceiptDatabase.TABLE_NAME)) {
|
||||||
count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(GroupReceiptDatabase.MMS_ID))), null, count, cancellationSignal);
|
count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(GroupReceiptDatabase.MMS_ID))), null, count, estimatedCount, cancellationSignal);
|
||||||
} else if (table.equals(AttachmentDatabase.TABLE_NAME)) {
|
} else if (table.equals(AttachmentDatabase.TABLE_NAME)) {
|
||||||
count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.MMS_ID))), (cursor, innerCount) -> exportAttachment(attachmentSecret, cursor, outputStream, innerCount), count, cancellationSignal);
|
count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.MMS_ID))), (cursor, innerCount) -> exportAttachment(attachmentSecret, cursor, outputStream, innerCount, estimatedCount), count, estimatedCount, cancellationSignal);
|
||||||
} else if (table.equals(StickerDatabase.TABLE_NAME)) {
|
} else if (table.equals(StickerDatabase.TABLE_NAME)) {
|
||||||
count = exportTable(table, input, outputStream, cursor -> true, (cursor, innerCount) -> exportSticker(attachmentSecret, cursor, outputStream, innerCount), count, cancellationSignal);
|
count = exportTable(table, input, outputStream, cursor -> true, (cursor, innerCount) -> exportSticker(attachmentSecret, cursor, outputStream, innerCount, estimatedCount), count, estimatedCount, cancellationSignal);
|
||||||
} else if (!BLACKLISTED_TABLES.contains(table) && !table.startsWith("sqlite_")) {
|
} else if (!BLACKLISTED_TABLES.contains(table) && !table.startsWith("sqlite_")) {
|
||||||
count = exportTable(table, input, outputStream, null, null, count, cancellationSignal);
|
count = exportTable(table, input, outputStream, null, null, count, estimatedCount, cancellationSignal);
|
||||||
}
|
}
|
||||||
stopwatch.split("table::" + table);
|
stopwatch.split("table::" + table);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (BackupProtos.SharedPreference preference : IdentityKeyUtil.getBackupRecord(context)) {
|
for (BackupProtos.SharedPreference preference : IdentityKeyUtil.getBackupRecord(context)) {
|
||||||
throwIfCanceled(cancellationSignal);
|
throwIfCanceled(cancellationSignal);
|
||||||
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
|
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count, estimatedCount));
|
||||||
outputStream.write(preference);
|
outputStream.write(preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (BackupProtos.SharedPreference preference : TextSecurePreferences.getPreferencesToSaveToBackup(context)) {
|
for (BackupProtos.SharedPreference preference : TextSecurePreferences.getPreferencesToSaveToBackup(context)) {
|
||||||
throwIfCanceled(cancellationSignal);
|
throwIfCanceled(cancellationSignal);
|
||||||
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
|
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count, estimatedCount));
|
||||||
outputStream.write(preference);
|
outputStream.write(preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
stopwatch.split("prefs");
|
stopwatch.split("prefs");
|
||||||
|
|
||||||
count = exportKeyValues(outputStream, SignalStore.getKeysToIncludeInBackup(), count, cancellationSignal);
|
count = exportKeyValues(outputStream, SignalStore.getKeysToIncludeInBackup(), count, estimatedCount, cancellationSignal);
|
||||||
|
|
||||||
stopwatch.split("key_values");
|
stopwatch.split("key_values");
|
||||||
|
|
||||||
for (AvatarHelper.Avatar avatar : AvatarHelper.getAvatars(context)) {
|
for (AvatarHelper.Avatar avatar : AvatarHelper.getAvatars(context)) {
|
||||||
throwIfCanceled(cancellationSignal);
|
throwIfCanceled(cancellationSignal);
|
||||||
if (avatar != null) {
|
if (avatar != null) {
|
||||||
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
|
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count, estimatedCount));
|
||||||
outputStream.write(avatar.getFilename(), avatar.getInputStream(), avatar.getLength());
|
outputStream.write(avatar.getFilename(), avatar.getInputStream(), avatar.getLength());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,7 +207,49 @@ public class FullBackupExporter extends FullBackupBase {
|
||||||
if (closeOutputStream) {
|
if (closeOutputStream) {
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
}
|
}
|
||||||
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, ++count));
|
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, ++count, estimatedCountOutside));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long calculateCount(@NonNull Context context, @NonNull SQLiteDatabase input, List<String> tables) {
|
||||||
|
long count = DATABASE_VERSION_RECORD_COUNT + TABLE_RECORD_COUNT_MULTIPLIER * tables.size();
|
||||||
|
|
||||||
|
for (String table : tables) {
|
||||||
|
if (table.equals(MmsDatabase.TABLE_NAME)) {
|
||||||
|
count += getCount(input, BackupCountQueries.mmsCount);
|
||||||
|
} else if (table.equals(SmsDatabase.TABLE_NAME)) {
|
||||||
|
count += getCount(input, BackupCountQueries.smsCount);
|
||||||
|
} else if (table.equals(GroupReceiptDatabase.TABLE_NAME)) {
|
||||||
|
count += getCount(input, BackupCountQueries.getGroupReceiptCount());
|
||||||
|
} else if (table.equals(AttachmentDatabase.TABLE_NAME)) {
|
||||||
|
count += getCount(input, BackupCountQueries.getAttachmentCount());
|
||||||
|
} else if (table.equals(StickerDatabase.TABLE_NAME)) {
|
||||||
|
count += getCount(input, "SELECT COUNT(*) FROM " + table);
|
||||||
|
} else if (!BLACKLISTED_TABLES.contains(table) && !table.startsWith("sqlite_")) {
|
||||||
|
count += getCount(input, "SELECT COUNT(*) FROM " + table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count += IDENTITY_KEY_BACKUP_RECORD_COUNT;
|
||||||
|
|
||||||
|
count += TextSecurePreferences.getPreferencesToSaveToBackupCount(context);
|
||||||
|
|
||||||
|
KeyValueDataSet dataSet = KeyValueDatabase.getInstance(ApplicationDependencies.getApplication())
|
||||||
|
.getDataSet();
|
||||||
|
for (String key : SignalStore.getKeysToIncludeInBackup()) {
|
||||||
|
if (dataSet.containsKey(key)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count += AvatarHelper.getAvatarCount(context);
|
||||||
|
|
||||||
|
return count + FINAL_MESSAGE_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long getCount(@NonNull SQLiteDatabase input, @NonNull String query) {
|
||||||
|
try (Cursor cursor = input.rawQuery(query)) {
|
||||||
|
return cursor.moveToFirst() ? cursor.getLong(0) : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,6 +295,7 @@ public class FullBackupExporter extends FullBackupBase {
|
||||||
@Nullable Predicate<Cursor> predicate,
|
@Nullable Predicate<Cursor> predicate,
|
||||||
@Nullable PostProcessor postProcess,
|
@Nullable PostProcessor postProcess,
|
||||||
int count,
|
int count,
|
||||||
|
long estimatedCount,
|
||||||
@NonNull BackupCancellationSignal cancellationSignal)
|
@NonNull BackupCancellationSignal cancellationSignal)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
@ -283,7 +335,7 @@ public class FullBackupExporter extends FullBackupBase {
|
||||||
|
|
||||||
statement.append(')');
|
statement.append(')');
|
||||||
|
|
||||||
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
|
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count, estimatedCount));
|
||||||
outputStream.write(statementBuilder.setStatement(statement.toString()).build());
|
outputStream.write(statementBuilder.setStatement(statement.toString()).build());
|
||||||
|
|
||||||
if (postProcess != null) {
|
if (postProcess != null) {
|
||||||
|
@ -296,7 +348,7 @@ public class FullBackupExporter extends FullBackupBase {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int exportAttachment(@NonNull AttachmentSecret attachmentSecret, @NonNull Cursor cursor, @NonNull BackupFrameOutputStream outputStream, int count) {
|
private static int exportAttachment(@NonNull AttachmentSecret attachmentSecret, @NonNull Cursor cursor, @NonNull BackupFrameOutputStream outputStream, int count, long estimatedCount) {
|
||||||
try {
|
try {
|
||||||
long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.ROW_ID));
|
long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.ROW_ID));
|
||||||
long uniqueId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.UNIQUE_ID));
|
long uniqueId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.UNIQUE_ID));
|
||||||
|
@ -321,7 +373,7 @@ public class FullBackupExporter extends FullBackupBase {
|
||||||
if (random != null && random.length == 32) inputStream = ModernDecryptingPartInputStream.createFor(attachmentSecret, random, new File(data), 0);
|
if (random != null && random.length == 32) inputStream = ModernDecryptingPartInputStream.createFor(attachmentSecret, random, new File(data), 0);
|
||||||
else inputStream = ClassicDecryptingPartInputStream.createFor(attachmentSecret, new File(data));
|
else inputStream = ClassicDecryptingPartInputStream.createFor(attachmentSecret, new File(data));
|
||||||
|
|
||||||
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
|
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count, estimatedCount));
|
||||||
outputStream.write(new AttachmentId(rowId, uniqueId), inputStream, size);
|
outputStream.write(new AttachmentId(rowId, uniqueId), inputStream, size);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -331,7 +383,7 @@ public class FullBackupExporter extends FullBackupBase {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int exportSticker(@NonNull AttachmentSecret attachmentSecret, @NonNull Cursor cursor, @NonNull BackupFrameOutputStream outputStream, int count) {
|
private static int exportSticker(@NonNull AttachmentSecret attachmentSecret, @NonNull Cursor cursor, @NonNull BackupFrameOutputStream outputStream, int count, long estimatedCount) {
|
||||||
try {
|
try {
|
||||||
long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(StickerDatabase._ID));
|
long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(StickerDatabase._ID));
|
||||||
long size = cursor.getLong(cursor.getColumnIndexOrThrow(StickerDatabase.FILE_LENGTH));
|
long size = cursor.getLong(cursor.getColumnIndexOrThrow(StickerDatabase.FILE_LENGTH));
|
||||||
|
@ -340,7 +392,7 @@ public class FullBackupExporter extends FullBackupBase {
|
||||||
byte[] random = cursor.getBlob(cursor.getColumnIndexOrThrow(StickerDatabase.FILE_RANDOM));
|
byte[] random = cursor.getBlob(cursor.getColumnIndexOrThrow(StickerDatabase.FILE_RANDOM));
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(data) && size > 0) {
|
if (!TextUtils.isEmpty(data) && size > 0) {
|
||||||
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
|
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count, estimatedCount));
|
||||||
InputStream inputStream = ModernDecryptingPartInputStream.createFor(attachmentSecret, random, new File(data), 0);
|
InputStream inputStream = ModernDecryptingPartInputStream.createFor(attachmentSecret, random, new File(data), 0);
|
||||||
outputStream.writeSticker(rowId, inputStream, size);
|
outputStream.writeSticker(rowId, inputStream, size);
|
||||||
}
|
}
|
||||||
|
@ -371,6 +423,7 @@ public class FullBackupExporter extends FullBackupBase {
|
||||||
private static int exportKeyValues(@NonNull BackupFrameOutputStream outputStream,
|
private static int exportKeyValues(@NonNull BackupFrameOutputStream outputStream,
|
||||||
@NonNull List<String> keysToIncludeInBackup,
|
@NonNull List<String> keysToIncludeInBackup,
|
||||||
int count,
|
int count,
|
||||||
|
long estimatedCount,
|
||||||
BackupCancellationSignal cancellationSignal) throws IOException
|
BackupCancellationSignal cancellationSignal) throws IOException
|
||||||
{
|
{
|
||||||
KeyValueDataSet dataSet = KeyValueDatabase.getInstance(ApplicationDependencies.getApplication())
|
KeyValueDataSet dataSet = KeyValueDatabase.getInstance(ApplicationDependencies.getApplication())
|
||||||
|
@ -401,7 +454,7 @@ public class FullBackupExporter extends FullBackupBase {
|
||||||
throw new AssertionError("Unknown type: " + type);
|
throw new AssertionError("Unknown type: " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
|
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count, estimatedCount));
|
||||||
outputStream.write(builder.build());
|
outputStream.write(builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class FullBackupImporter extends FullBackupBase {
|
||||||
BackupFrame frame;
|
BackupFrame frame;
|
||||||
|
|
||||||
while (!(frame = inputStream.readFrame()).getEnd()) {
|
while (!(frame = inputStream.readFrame()).getEnd()) {
|
||||||
if (count % 100 == 0) EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, count));
|
if (count % 100 == 0) EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, count, 0));
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
if (frame.hasVersion()) processVersion(db, frame.getVersion());
|
if (frame.hasVersion()) processVersion(db, frame.getVersion());
|
||||||
|
@ -115,7 +115,7 @@ public class FullBackupImporter extends FullBackupBase {
|
||||||
keyValueDatabase.endTransaction();
|
keyValueDatabase.endTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, count));
|
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, count, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NonNull InputStream getInputStream(@NonNull Context context, @NonNull Uri uri) throws IOException{
|
private static @NonNull InputStream getInputStream(@NonNull Context context, @NonNull Uri uri) throws IOException{
|
||||||
|
|
|
@ -59,7 +59,7 @@ final class OldDeviceClientTask implements ClientTask {
|
||||||
public void onEvent(FullBackupBase.BackupEvent event) {
|
public void onEvent(FullBackupBase.BackupEvent event) {
|
||||||
if (event.getType() == FullBackupBase.BackupEvent.Type.PROGRESS) {
|
if (event.getType() == FullBackupBase.BackupEvent.Type.PROGRESS) {
|
||||||
if (System.currentTimeMillis() > lastProgressUpdate + PROGRESS_UPDATE_THROTTLE) {
|
if (System.currentTimeMillis() > lastProgressUpdate + PROGRESS_UPDATE_THROTTLE) {
|
||||||
EventBus.getDefault().post(new Status(event.getCount(), false));
|
EventBus.getDefault().post(new Status(event.getCount(), event.getEstimatedTotalCount(), event.getCompletionPercentage(), false));
|
||||||
lastProgressUpdate = System.currentTimeMillis();
|
lastProgressUpdate = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,22 +68,34 @@ final class OldDeviceClientTask implements ClientTask {
|
||||||
@Override
|
@Override
|
||||||
public void success() {
|
public void success() {
|
||||||
SignalStore.misc().markOldDeviceTransferLocked();
|
SignalStore.misc().markOldDeviceTransferLocked();
|
||||||
EventBus.getDefault().post(new Status(0, true));
|
EventBus.getDefault().post(new Status(0, 0, 0,true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Status {
|
public static final class Status {
|
||||||
private final long messages;
|
private final long messages;
|
||||||
|
private final long estimatedMessages;
|
||||||
|
private final double completionPercentage;
|
||||||
private final boolean done;
|
private final boolean done;
|
||||||
|
|
||||||
public Status(long messages, boolean done) {
|
public Status(long messages, long estimatedMessages, double completionPercentage, boolean done) {
|
||||||
this.messages = messages;
|
this.messages = messages;
|
||||||
this.done = done;
|
this.estimatedMessages = estimatedMessages;
|
||||||
|
this.completionPercentage = completionPercentage;
|
||||||
|
this.done = done;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMessageCount() {
|
public long getMessageCount() {
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getEstimatedMessageCount() {
|
||||||
|
return estimatedMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCompletionPercentage() {
|
||||||
|
return completionPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isDone() {
|
public boolean isDone() {
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,9 @@ import org.signal.devicetransfer.TransferStatus;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.devicetransfer.DeviceTransferFragment;
|
import org.thoughtcrime.securesms.devicetransfer.DeviceTransferFragment;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows transfer progress on the old device. Most logic is in {@link DeviceTransferFragment}
|
* Shows transfer progress on the old device. Most logic is in {@link DeviceTransferFragment}
|
||||||
* and it delegates to this class for strings, navigation, and updating progress.
|
* and it delegates to this class for strings, navigation, and updating progress.
|
||||||
|
@ -52,6 +55,14 @@ public final class OldDeviceTransferFragment extends DeviceTransferFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ClientTaskListener {
|
private class ClientTaskListener {
|
||||||
|
private final NumberFormat formatter;
|
||||||
|
|
||||||
|
public ClientTaskListener() {
|
||||||
|
formatter = NumberFormat.getInstance();
|
||||||
|
formatter.setMinimumFractionDigits(1);
|
||||||
|
formatter.setMaximumFractionDigits(1);
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
public void onEventMainThread(@NonNull OldDeviceClientTask.Status event) {
|
public void onEventMainThread(@NonNull OldDeviceClientTask.Status event) {
|
||||||
if (event.isDone()) {
|
if (event.isDone()) {
|
||||||
|
@ -61,7 +72,11 @@ public final class OldDeviceTransferFragment extends DeviceTransferFragment {
|
||||||
DeviceToDeviceTransferService.stop(requireContext());
|
DeviceToDeviceTransferService.stop(requireContext());
|
||||||
NavHostFragment.findNavController(OldDeviceTransferFragment.this).navigate(R.id.action_oldDeviceTransfer_to_oldDeviceTransferComplete);
|
NavHostFragment.findNavController(OldDeviceTransferFragment.this).navigate(R.id.action_oldDeviceTransfer_to_oldDeviceTransferComplete);
|
||||||
} else {
|
} else {
|
||||||
status.setText(getString(R.string.DeviceTransfer__d_messages_so_far, event.getMessageCount()));
|
if (event.getEstimatedMessageCount() == 0) {
|
||||||
|
status.setText(getString(R.string.DeviceTransfer__d_messages_so_far, event.getMessageCount()));
|
||||||
|
} else {
|
||||||
|
status.setText(getString(R.string.DeviceTransfer__s_of_messages_so_far, formatter.format(event.getCompletionPercentage())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,14 @@ import android.Manifest;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.greenrobot.eventbus.EventBus;
|
||||||
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.backup.BackupFileIOError;
|
import org.thoughtcrime.securesms.backup.BackupFileIOError;
|
||||||
import org.thoughtcrime.securesms.backup.BackupPassphrase;
|
import org.thoughtcrime.securesms.backup.BackupPassphrase;
|
||||||
|
import org.thoughtcrime.securesms.backup.FullBackupBase;
|
||||||
import org.thoughtcrime.securesms.backup.FullBackupExporter;
|
import org.thoughtcrime.securesms.backup.FullBackupExporter;
|
||||||
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
|
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
|
||||||
import org.thoughtcrime.securesms.database.NoExternalStorageException;
|
import org.thoughtcrime.securesms.database.NoExternalStorageException;
|
||||||
|
@ -85,11 +89,14 @@ public final class LocalBackupJob extends BaseJob {
|
||||||
throw new IOException("No external storage permission!");
|
throw new IOException("No external storage permission!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProgressUpdater updater = new ProgressUpdater();
|
||||||
try (NotificationController notification = GenericForegroundService.startForegroundTask(context,
|
try (NotificationController notification = GenericForegroundService.startForegroundTask(context,
|
||||||
context.getString(R.string.LocalBackupJob_creating_backup),
|
context.getString(R.string.LocalBackupJob_creating_signal_backup),
|
||||||
NotificationChannels.BACKUPS,
|
NotificationChannels.BACKUPS,
|
||||||
R.drawable.ic_signal_backup))
|
R.drawable.ic_signal_backup))
|
||||||
{
|
{
|
||||||
|
updater.setNotification(notification);
|
||||||
|
EventBus.getDefault().register(updater);
|
||||||
notification.setIndeterminateProgress();
|
notification.setIndeterminateProgress();
|
||||||
|
|
||||||
String backupPassword = BackupPassphrase.get(context);
|
String backupPassword = BackupPassphrase.get(context);
|
||||||
|
@ -139,6 +146,9 @@ public final class LocalBackupJob extends BaseJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
BackupUtil.deleteOldBackups();
|
BackupUtil.deleteOldBackups();
|
||||||
|
} finally {
|
||||||
|
EventBus.getDefault().unregister(updater);
|
||||||
|
updater.setNotification(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +176,29 @@ public final class LocalBackupJob extends BaseJob {
|
||||||
public void onFailure() {
|
public void onFailure() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ProgressUpdater {
|
||||||
|
private NotificationController notification;
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.POSTING)
|
||||||
|
public void onEvent(FullBackupBase.BackupEvent event) {
|
||||||
|
if (notification == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.getType() == FullBackupBase.BackupEvent.Type.PROGRESS) {
|
||||||
|
if (event.getEstimatedTotalCount() == 0) {
|
||||||
|
notification.setIndeterminateProgress();
|
||||||
|
} else {
|
||||||
|
notification.setProgress(100, (int) event.getCompletionPercentage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotification(NotificationController notification) {
|
||||||
|
this.notification = notification;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class Factory implements Job.Factory<LocalBackupJob> {
|
public static class Factory implements Job.Factory<LocalBackupJob> {
|
||||||
@Override
|
@Override
|
||||||
public @NonNull LocalBackupJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
public @NonNull LocalBackupJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||||
|
|
|
@ -6,10 +6,14 @@ import android.net.Uri;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.documentfile.provider.DocumentFile;
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
|
|
||||||
|
import org.greenrobot.eventbus.EventBus;
|
||||||
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.backup.BackupFileIOError;
|
import org.thoughtcrime.securesms.backup.BackupFileIOError;
|
||||||
import org.thoughtcrime.securesms.backup.BackupPassphrase;
|
import org.thoughtcrime.securesms.backup.BackupPassphrase;
|
||||||
|
import org.thoughtcrime.securesms.backup.FullBackupBase;
|
||||||
import org.thoughtcrime.securesms.backup.FullBackupExporter;
|
import org.thoughtcrime.securesms.backup.FullBackupExporter;
|
||||||
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
|
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||||
|
@ -70,11 +74,14 @@ public final class LocalBackupJobApi29 extends BaseJob {
|
||||||
throw new IOException("Backup Directory has not been selected!");
|
throw new IOException("Backup Directory has not been selected!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProgressUpdater updater = new ProgressUpdater();
|
||||||
try (NotificationController notification = GenericForegroundService.startForegroundTask(context,
|
try (NotificationController notification = GenericForegroundService.startForegroundTask(context,
|
||||||
context.getString(R.string.LocalBackupJob_creating_backup),
|
context.getString(R.string.LocalBackupJob_creating_signal_backup),
|
||||||
NotificationChannels.BACKUPS,
|
NotificationChannels.BACKUPS,
|
||||||
R.drawable.ic_signal_backup))
|
R.drawable.ic_signal_backup))
|
||||||
{
|
{
|
||||||
|
updater.setNotification(notification);
|
||||||
|
EventBus.getDefault().register(updater);
|
||||||
notification.setIndeterminateProgress();
|
notification.setIndeterminateProgress();
|
||||||
|
|
||||||
String backupPassword = BackupPassphrase.get(context);
|
String backupPassword = BackupPassphrase.get(context);
|
||||||
|
@ -135,6 +142,9 @@ public final class LocalBackupJobApi29 extends BaseJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
BackupUtil.deleteOldBackups();
|
BackupUtil.deleteOldBackups();
|
||||||
|
} finally {
|
||||||
|
EventBus.getDefault().unregister(updater);
|
||||||
|
updater.setNotification(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,6 +172,29 @@ public final class LocalBackupJobApi29 extends BaseJob {
|
||||||
public void onFailure() {
|
public void onFailure() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ProgressUpdater {
|
||||||
|
private NotificationController notification;
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.POSTING)
|
||||||
|
public void onEvent(FullBackupBase.BackupEvent event) {
|
||||||
|
if (notification == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.getType() == FullBackupBase.BackupEvent.Type.PROGRESS) {
|
||||||
|
if (event.getEstimatedTotalCount() == 0) {
|
||||||
|
notification.setIndeterminateProgress();
|
||||||
|
} else {
|
||||||
|
notification.setProgress(100, (int) event.getCompletionPercentage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotification(NotificationController notification) {
|
||||||
|
this.notification = notification;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class Factory implements Job.Factory<LocalBackupJobApi29> {
|
public static class Factory implements Job.Factory<LocalBackupJobApi29> {
|
||||||
@Override
|
@Override
|
||||||
public @NonNull
|
public @NonNull
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
import org.thoughtcrime.securesms.util.BackupUtil;
|
import org.thoughtcrime.securesms.util.BackupUtil;
|
||||||
import org.thoughtcrime.securesms.util.StorageUtil;
|
import org.thoughtcrime.securesms.util.StorageUtil;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@ -54,6 +55,8 @@ public class BackupsPreferenceFragment extends Fragment {
|
||||||
private ProgressBar progress;
|
private ProgressBar progress;
|
||||||
private TextView progressSummary;
|
private TextView progressSummary;
|
||||||
|
|
||||||
|
private final NumberFormat formatter = NumberFormat.getInstance();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
return inflater.inflate(R.layout.fragment_backups, container, false);
|
return inflater.inflate(R.layout.fragment_backups, container, false);
|
||||||
|
@ -75,6 +78,9 @@ public class BackupsPreferenceFragment extends Fragment {
|
||||||
create.setOnClickListener(unused -> onCreateClicked());
|
create.setOnClickListener(unused -> onCreateClicked());
|
||||||
verify.setOnClickListener(unused -> BackupDialog.showVerifyBackupPassphraseDialog(requireContext()));
|
verify.setOnClickListener(unused -> BackupDialog.showVerifyBackupPassphraseDialog(requireContext()));
|
||||||
|
|
||||||
|
formatter.setMinimumFractionDigits(1);
|
||||||
|
formatter.setMaximumFractionDigits(1);
|
||||||
|
|
||||||
EventBus.getDefault().register(this);
|
EventBus.getDefault().register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,8 +126,19 @@ public class BackupsPreferenceFragment extends Fragment {
|
||||||
create.setEnabled(false);
|
create.setEnabled(false);
|
||||||
summary.setText(getString(R.string.BackupsPreferenceFragment__in_progress));
|
summary.setText(getString(R.string.BackupsPreferenceFragment__in_progress));
|
||||||
progress.setVisibility(View.VISIBLE);
|
progress.setVisibility(View.VISIBLE);
|
||||||
progressSummary.setVisibility(View.VISIBLE);
|
progressSummary.setVisibility(event.getCount() > 0 ? View.VISIBLE : View.GONE);
|
||||||
progressSummary.setText(getString(R.string.BackupsPreferenceFragment__d_so_far, event.getCount()));
|
|
||||||
|
if (event.getEstimatedTotalCount() == 0) {
|
||||||
|
progress.setIndeterminate(true);
|
||||||
|
progressSummary.setText(getString(R.string.BackupsPreferenceFragment__d_so_far, event.getCount()));
|
||||||
|
} else {
|
||||||
|
double completionPercentage = event.getCompletionPercentage();
|
||||||
|
|
||||||
|
progress.setIndeterminate(false);
|
||||||
|
progress.setMax(100);
|
||||||
|
progress.setProgress((int) completionPercentage);
|
||||||
|
progressSummary.setText(getString(R.string.BackupsPreferenceFragment__s_so_far, formatter.format(completionPercentage)));
|
||||||
|
}
|
||||||
} else if (event.getType() == FullBackupBase.BackupEvent.Type.FINISHED) {
|
} else if (event.getType() == FullBackupBase.BackupEvent.Type.FINISHED) {
|
||||||
create.setEnabled(true);
|
create.setEnabled(true);
|
||||||
progress.setVisibility(View.GONE);
|
progress.setVisibility(View.GONE);
|
||||||
|
|
|
@ -34,6 +34,13 @@ public class AvatarHelper {
|
||||||
|
|
||||||
private static final String AVATAR_DIRECTORY = "avatars";
|
private static final String AVATAR_DIRECTORY = "avatars";
|
||||||
|
|
||||||
|
public static long getAvatarCount(@NonNull Context context) {
|
||||||
|
File avatarDirectory = context.getDir(AVATAR_DIRECTORY, Context.MODE_PRIVATE);
|
||||||
|
String[] results = avatarDirectory.list();
|
||||||
|
|
||||||
|
return results == null ? 0 : results.length;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves an iterable set of avatars. Only intended to be used during backup.
|
* Retrieves an iterable set of avatars. Only intended to be used during backup.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -328,7 +328,7 @@ public final class RestoreBackupFragment extends LoggingFragment {
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
public void onEvent(@NonNull FullBackupBase.BackupEvent event) {
|
public void onEvent(@NonNull FullBackupBase.BackupEvent event) {
|
||||||
int count = event.getCount();
|
long count = event.getCount();
|
||||||
|
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
restoreBackupProgress.setText(R.string.RegistrationActivity_checking);
|
restoreBackupProgress.setText(R.string.RegistrationActivity_checking);
|
||||||
|
|
|
@ -230,6 +230,31 @@ public class TextSecurePreferences {
|
||||||
MEDIA_DOWNLOAD_WIFI_PREF,
|
MEDIA_DOWNLOAD_WIFI_PREF,
|
||||||
MEDIA_DOWNLOAD_ROAMING_PREF};
|
MEDIA_DOWNLOAD_ROAMING_PREF};
|
||||||
|
|
||||||
|
public static long getPreferencesToSaveToBackupCount(@NonNull Context context) {
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
long count = 0;
|
||||||
|
|
||||||
|
for (String booleanPreference : booleanPreferencesToBackup) {
|
||||||
|
if (preferences.contains(booleanPreference)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String stringPreference : stringPreferencesToBackup) {
|
||||||
|
if (preferences.contains(stringPreference)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String stringSetPreference : stringSetPreferencesToBackup) {
|
||||||
|
if (preferences.contains(stringSetPreference)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
public static List<BackupProtos.SharedPreference> getPreferencesToSaveToBackup(@NonNull Context context) {
|
public static List<BackupProtos.SharedPreference> getPreferencesToSaveToBackup(@NonNull Context context) {
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
List<BackupProtos.SharedPreference> backupProtos = new ArrayList<>();
|
List<BackupProtos.SharedPreference> backupProtos = new ArrayList<>();
|
||||||
|
|
|
@ -64,7 +64,6 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:indeterminate="true"
|
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintBottom_toTopOf="@id/fragment_backup_progress_summary"
|
app:layout_constraintBottom_toTopOf="@id/fragment_backup_progress_summary"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
|
|
@ -470,6 +470,8 @@
|
||||||
<string name="BackupsPreferenceFragment__learn_more">Learn more</string>
|
<string name="BackupsPreferenceFragment__learn_more">Learn more</string>
|
||||||
<string name="BackupsPreferenceFragment__in_progress">In progress…</string>
|
<string name="BackupsPreferenceFragment__in_progress">In progress…</string>
|
||||||
<string name="BackupsPreferenceFragment__d_so_far">%1$d so far…</string>
|
<string name="BackupsPreferenceFragment__d_so_far">%1$d so far…</string>
|
||||||
|
<!-- Show percentage of completion of backup -->
|
||||||
|
<string name="BackupsPreferenceFragment__s_so_far">%1$s%% so far…</string>
|
||||||
<string name="BackupsPreferenceFragment_signal_requires_external_storage_permission_in_order_to_create_backups">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\".</string>
|
<string name="BackupsPreferenceFragment_signal_requires_external_storage_permission_in_order_to_create_backups">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\".</string>
|
||||||
|
|
||||||
|
|
||||||
|
@ -3084,7 +3086,7 @@
|
||||||
<string name="BackupDialog_verify">Verify</string>
|
<string name="BackupDialog_verify">Verify</string>
|
||||||
<string name="BackupDialog_you_successfully_entered_your_backup_passphrase">You successfully entered your backup passphrase</string>
|
<string name="BackupDialog_you_successfully_entered_your_backup_passphrase">You successfully entered your backup passphrase</string>
|
||||||
<string name="BackupDialog_passphrase_was_not_correct">Passphrase was not correct</string>
|
<string name="BackupDialog_passphrase_was_not_correct">Passphrase was not correct</string>
|
||||||
<string name="LocalBackupJob_creating_backup">Creating backup…</string>
|
<string name="LocalBackupJob_creating_signal_backup">Creating Signal backup…</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>
|
||||||
|
@ -3213,6 +3215,8 @@
|
||||||
<string name="DeviceTransfer__transferring_data">Transferring data</string>
|
<string name="DeviceTransfer__transferring_data">Transferring data</string>
|
||||||
<string name="DeviceTransfer__keep_both_devices_near_each_other">Keep both devices near each other. Do not turn off the devices and keep Signal open. Transfers are end-to-end encrypted.</string>
|
<string name="DeviceTransfer__keep_both_devices_near_each_other">Keep both devices near each other. Do not turn off the devices and keep Signal open. Transfers are end-to-end encrypted.</string>
|
||||||
<string name="DeviceTransfer__d_messages_so_far">%1$d messages so far…</string>
|
<string name="DeviceTransfer__d_messages_so_far">%1$d messages so far…</string>
|
||||||
|
<!-- Filled in with total percentage of messages transferred -->
|
||||||
|
<string name="DeviceTransfer__s_of_messages_so_far">%1$s%% of messages so far…</string>
|
||||||
<string name="DeviceTransfer__cancel">Cancel</string>
|
<string name="DeviceTransfer__cancel">Cancel</string>
|
||||||
<string name="DeviceTransfer__try_again">Try again</string>
|
<string name="DeviceTransfer__try_again">Try again</string>
|
||||||
<string name="DeviceTransfer__stop_transfer_question">Stop transfer?</string>
|
<string name="DeviceTransfer__stop_transfer_question">Stop transfer?</string>
|
||||||
|
|
|
@ -8,10 +8,12 @@ import android.content.Intent;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
|
@ -21,6 +23,7 @@ import org.signal.core.util.logging.Log;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Foreground service to help manage interactions with the {@link DeviceTransferClient} and
|
* Foreground service to help manage interactions with the {@link DeviceTransferClient} and
|
||||||
|
@ -44,6 +47,7 @@ public class DeviceToDeviceTransferService extends Service implements ShutdownCa
|
||||||
private PendingIntent pendingIntent;
|
private PendingIntent pendingIntent;
|
||||||
private DeviceTransferServer server;
|
private DeviceTransferServer server;
|
||||||
private DeviceTransferClient client;
|
private DeviceTransferClient client;
|
||||||
|
private PowerManager.WakeLock wakeLock;
|
||||||
|
|
||||||
public static void startServer(@NonNull Context context,
|
public static void startServer(@NonNull Context context,
|
||||||
@NonNull ServerTask serverTask,
|
@NonNull ServerTask serverTask,
|
||||||
|
@ -114,6 +118,10 @@ public class DeviceToDeviceTransferService extends Service implements ShutdownCa
|
||||||
server = null;
|
server = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wakeLock != null) {
|
||||||
|
wakeLock.release();
|
||||||
|
}
|
||||||
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,6 +151,7 @@ public class DeviceToDeviceTransferService extends Service implements ShutdownCa
|
||||||
server = new DeviceTransferServer(getApplicationContext(),
|
server = new DeviceTransferServer(getApplicationContext(),
|
||||||
(ServerTask) Objects.requireNonNull(intent.getSerializableExtra(EXTRA_TASK)),
|
(ServerTask) Objects.requireNonNull(intent.getSerializableExtra(EXTRA_TASK)),
|
||||||
this);
|
this);
|
||||||
|
acquireWakeLock();
|
||||||
server.start();
|
server.start();
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "Can't start server, already started.");
|
Log.i(TAG, "Can't start server, already started.");
|
||||||
|
@ -156,6 +165,7 @@ public class DeviceToDeviceTransferService extends Service implements ShutdownCa
|
||||||
client = new DeviceTransferClient(getApplicationContext(),
|
client = new DeviceTransferClient(getApplicationContext(),
|
||||||
(ClientTask) Objects.requireNonNull(intent.getSerializableExtra(EXTRA_TASK)),
|
(ClientTask) Objects.requireNonNull(intent.getSerializableExtra(EXTRA_TASK)),
|
||||||
this);
|
this);
|
||||||
|
acquireWakeLock();
|
||||||
client.start();
|
client.start();
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "Can't start client, client already started.");
|
Log.i(TAG, "Can't start client, client already started.");
|
||||||
|
@ -187,6 +197,19 @@ public class DeviceToDeviceTransferService extends Service implements ShutdownCa
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void acquireWakeLock() {
|
||||||
|
if (wakeLock == null) {
|
||||||
|
PowerManager powerManager = ContextCompat.getSystemService(this, PowerManager.class);
|
||||||
|
if (powerManager != null) {
|
||||||
|
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "signal:d2dpartial");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wakeLock.isHeld()) {
|
||||||
|
wakeLock.acquire(TimeUnit.HOURS.toMillis(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateNotification(@NonNull TransferStatus transferStatus) {
|
private void updateNotification(@NonNull TransferStatus transferStatus) {
|
||||||
if (notificationData != null && (client != null || server != null)) {
|
if (notificationData != null && (client != null || server != null)) {
|
||||||
startForeground(notificationData.notificationId, createNotification(transferStatus, notificationData));
|
startForeground(notificationData.notificationId, createNotification(transferStatus, notificationData));
|
||||||
|
|
Loading…
Add table
Reference in a new issue