diff options
Diffstat (limited to 'src')
18 files changed, 96 insertions, 61 deletions
diff --git a/src/main/java/de/pixart/messenger/services/ExportBackupService.java b/src/main/java/de/pixart/messenger/services/ExportBackupService.java index 4489eca9f..7640c6ea0 100644 --- a/src/main/java/de/pixart/messenger/services/ExportBackupService.java +++ b/src/main/java/de/pixart/messenger/services/ExportBackupService.java @@ -30,7 +30,9 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -67,6 +69,8 @@ public class ExportBackupService extends Service { public static final String CIPHERMODE = "AES/GCM/NoPadding"; public static final String PROVIDER = "BC"; + public static final String MIME_TYPE = "application/vnd.conversations.backup"; + boolean ReadableLogsEnabled = false; private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); private static final String DIRECTORY_STRING_FORMAT = FileBackend.getAppLogsDirectory() + "%s"; @@ -247,11 +251,19 @@ public class ExportBackupService extends Service { if (extras != null && extras.containsKey("NOTIFY_ON_BACKUP_COMPLETE")) { notify = extras.getBoolean("NOTIFY_ON_BACKUP_COMPLETE"); } - final boolean success = export(); + boolean success; + List<File> files; + try { + files = export(); + success = true; + } catch (Exception e) { + success = false; + files = Collections.emptyList(); + } stopForeground(true); running.set(false); if (success) { - notifySuccess(notify); + notifySuccess(files, notify); } else { notifyError(); } @@ -299,92 +311,97 @@ public class ExportBackupService extends Service { } } - private boolean export() { + private List<File> export() throws Exception { wakeLock.acquire(15 * 60 * 1000L /*15 minutes*/); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup"); mBuilder.setContentTitle(getString(R.string.notification_create_backup_title)) .setSmallIcon(R.drawable.ic_archive_white_24dp) .setProgress(1, 0, false); startForeground(NOTIFICATION_ID, mBuilder.build()); - try { - int count = 0; - final int max = this.mAccounts.size(); - final SecureRandom secureRandom = new SecureRandom(); - if (mAccounts.size() >= 1) { - if (ReadableLogsEnabled) { - List<Conversation> conversations = mDatabaseBackend.getConversations(Conversation.STATUS_AVAILABLE); - conversations.addAll(mDatabaseBackend.getConversations(Conversation.STATUS_ARCHIVED)); - for (Conversation conversation : conversations) { - writeToFile(conversation); - } - } + int count = 0; + final int max = this.mAccounts.size(); + final SecureRandom secureRandom = new SecureRandom(); + final List<File> files = new ArrayList<>(); + for (Account account : this.mAccounts) { + final byte[] IV = new byte[12]; + final byte[] salt = new byte[16]; + secureRandom.nextBytes(IV); + secureRandom.nextBytes(salt); + final BackupFileHeader backupFileHeader = new BackupFileHeader(getString(R.string.app_name), account.getJid(), System.currentTimeMillis(), IV, salt); + final Progress progress = new Progress(mBuilder, max, count); + final File file = new File(FileBackend.getBackupDirectory() + account.getJid().asBareJid().toEscapedString() + ".ceb"); + files.add(file); + if (file.getParentFile().mkdirs()) { + Log.d(Config.LOGTAG, "created backup directory " + file.getParentFile().getAbsolutePath()); } - for (Account account : this.mAccounts) { - final byte[] IV = new byte[12]; - final byte[] salt = new byte[16]; - secureRandom.nextBytes(IV); - secureRandom.nextBytes(salt); - final BackupFileHeader backupFileHeader = new BackupFileHeader(getString(R.string.app_name), account.getJid(), System.currentTimeMillis(), IV, salt); - final Progress progress = new Progress(mBuilder, max, count); - final File file = new File(FileBackend.getBackupDirectory() + account.getJid().asBareJid().toEscapedString() + ".ceb"); - if (file.getParentFile().mkdirs()) { - Log.d(Config.LOGTAG, "created backup directory " + file.getParentFile().getAbsolutePath()); - } - final FileOutputStream fileOutputStream = new FileOutputStream(file); - final DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream); - backupFileHeader.write(dataOutputStream); - dataOutputStream.flush(); - - final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER); - byte[] key = getKey(account.getPassword(), salt); - Log.d(Config.LOGTAG, backupFileHeader.toString()); - SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE); - IvParameterSpec ivSpec = new IvParameterSpec(IV); - cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); - CipherOutputStream cipherOutputStream = new CipherOutputStream(fileOutputStream, cipher); - - GZIPOutputStream gzipOutputStream = new GZIPOutputStream(cipherOutputStream); - PrintWriter writer = new PrintWriter(gzipOutputStream); - SQLiteDatabase db = this.mDatabaseBackend.getReadableDatabase(); - final String uuid = account.getUuid(); - accountExport(db, uuid, writer); - simpleExport(db, Conversation.TABLENAME, Conversation.ACCOUNT, uuid, writer); - messageExport(db, uuid, writer, progress); - for (String table : Arrays.asList(SQLiteAxolotlStore.PREKEY_TABLENAME, SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, SQLiteAxolotlStore.SESSION_TABLENAME, SQLiteAxolotlStore.IDENTITIES_TABLENAME)) { - simpleExport(db, table, SQLiteAxolotlStore.ACCOUNT, uuid, writer); - } - writer.flush(); - writer.close(); - Log.d(Config.LOGTAG, "written backup to " + file.getAbsoluteFile()); - count++; + final FileOutputStream fileOutputStream = new FileOutputStream(file); + final DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream); + backupFileHeader.write(dataOutputStream); + dataOutputStream.flush(); + + final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER); + byte[] key = getKey(account.getPassword(), salt); + Log.d(Config.LOGTAG, backupFileHeader.toString()); + SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE); + IvParameterSpec ivSpec = new IvParameterSpec(IV); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); + CipherOutputStream cipherOutputStream = new CipherOutputStream(fileOutputStream, cipher); + + GZIPOutputStream gzipOutputStream = new GZIPOutputStream(cipherOutputStream); + PrintWriter writer = new PrintWriter(gzipOutputStream); + SQLiteDatabase db = this.mDatabaseBackend.getReadableDatabase(); + final String uuid = account.getUuid(); + accountExport(db, uuid, writer); + simpleExport(db, Conversation.TABLENAME, Conversation.ACCOUNT, uuid, writer); + messageExport(db, uuid, writer, progress); + for (String table : Arrays.asList(SQLiteAxolotlStore.PREKEY_TABLENAME, SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, SQLiteAxolotlStore.SESSION_TABLENAME, SQLiteAxolotlStore.IDENTITIES_TABLENAME)) { + simpleExport(db, table, SQLiteAxolotlStore.ACCOUNT, uuid, writer); } - return true; - } catch (Exception e) { - Log.d(Config.LOGTAG, "unable to create backup ", e); - return false; + writer.flush(); + writer.close(); + Log.d(Config.LOGTAG, "written backup to " + file.getAbsoluteFile()); + count++; } + return files; } - private void notifySuccess(final boolean notify) { + private void notifySuccess(List<File> files, final boolean notify) { if (!notify) { return; } final String path = FileBackend.getBackupDirectory(); - PendingIntent pendingIntent = null; + PendingIntent openFolderIntent = null; for (Intent intent : getPossibleFileOpenIntents(this, path)) { if (intent.resolveActivityInfo(getPackageManager(), 0) != null) { - pendingIntent = PendingIntent.getActivity(this, 189, intent, PendingIntent.FLAG_UPDATE_CURRENT); + openFolderIntent = PendingIntent.getActivity(this, 189, intent, PendingIntent.FLAG_UPDATE_CURRENT); break; } } + PendingIntent shareFilesIntent = null; + if (files.size() > 0) { + final Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE); + ArrayList<Uri> uris = new ArrayList<>(); + for (File file : files) { + uris.add(FileBackend.getUriForFile(this, file)); + } + intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setType(MIME_TYPE); + final Intent chooser = Intent.createChooser(intent, getString(R.string.share_backup_files)); + shareFilesIntent = PendingIntent.getActivity(this, 190, chooser, PendingIntent.FLAG_UPDATE_CURRENT); + } + NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup"); mBuilder.setContentTitle(getString(R.string.notification_backup_created_title)) .setContentText(getString(R.string.notification_backup_created_subtitle, path)) .setStyle(new NotificationCompat.BigTextStyle().bigText(getString(R.string.notification_backup_created_subtitle, FileBackend.getBackupDirectory()))) .setAutoCancel(true) - .setContentIntent(pendingIntent) + .setContentIntent(openFolderIntent) .setSmallIcon(R.drawable.ic_archive_white_24dp); + if (shareFilesIntent != null) { + mBuilder.addAction(R.drawable.ic_share_white_24dp, getString(R.string.share_backup_files), shareFilesIntent); + } notificationManager.notify(NOTIFICATION_ID, mBuilder.build()); } diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java index 15efb7921..f7be6a365 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java @@ -118,6 +118,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte imageResource = activity.getThemeResource(R.attr.ic_attach_location, R.drawable.ic_attach_location); showPreviewText = false; } else { + //TODO move this into static MediaPreview method and use same icons as in MediaAdapter final String mime = message.getMimeType(); switch (mime == null ? "" : mime.split("/")[0]) { case "image": diff --git a/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java index e8968bbac..847ab35cf 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java @@ -23,6 +23,7 @@ import java.util.concurrent.RejectedExecutionException; import de.pixart.messenger.R; import de.pixart.messenger.databinding.MediaBinding; +import de.pixart.messenger.services.ExportBackupService; import de.pixart.messenger.ui.XmppActivity; import de.pixart.messenger.ui.util.Attachment; import de.pixart.messenger.ui.util.StyledAttributes; @@ -80,6 +81,8 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol attr = R.attr.media_preview_archive; } else if (mime.equals("application/epub+zip") || mime.equals("application/vnd.amazon.mobi8-ebook")) { attr = R.attr.media_preview_ebook; + } else if (mime.equals(ExportBackupService.MIME_TYPE)) { + attr = R.attr.media_preview_backup; } else if (DOCUMENT_MIMES.contains(mime)) { attr = R.attr.media_preview_document; } else { diff --git a/src/main/java/de/pixart/messenger/utils/MimeUtils.java b/src/main/java/de/pixart/messenger/utils/MimeUtils.java index 431cde39a..af170b082 100644 --- a/src/main/java/de/pixart/messenger/utils/MimeUtils.java +++ b/src/main/java/de/pixart/messenger/utils/MimeUtils.java @@ -30,6 +30,7 @@ import java.util.Properties; import de.pixart.messenger.Config; import de.pixart.messenger.entities.Transferable; +import de.pixart.messenger.services.ExportBackupService; /** * Utilities for dealing with MIME types. @@ -76,6 +77,7 @@ public final class MimeUtils { add("application/vnd.amazon.mobi8-ebook","kfx"); add("application/vnd.android.package-archive", "apk"); add("application/vnd.cinderella", "cdy"); + add(ExportBackupService.MIME_TYPE, "ceb"); add("application/vnd.ms-pki.stl", "stl"); add("application/vnd.oasis.opendocument.database", "odb"); add("application/vnd.oasis.opendocument.formula", "odf"); diff --git a/src/main/java/de/pixart/messenger/utils/UIHelper.java b/src/main/java/de/pixart/messenger/utils/UIHelper.java index b2e73fb46..097baa3f6 100644 --- a/src/main/java/de/pixart/messenger/utils/UIHelper.java +++ b/src/main/java/de/pixart/messenger/utils/UIHelper.java @@ -27,6 +27,7 @@ import de.pixart.messenger.entities.Message; import de.pixart.messenger.entities.MucOptions; import de.pixart.messenger.entities.Presence; import de.pixart.messenger.entities.Transferable; +import de.pixart.messenger.services.ExportBackupService; import rocks.xmpp.addr.Jid; public class UIHelper { @@ -477,8 +478,12 @@ public class UIHelper { return context.getString(R.string.pdf_document); } else if (mime.equals("application/vnd.android.package-archive")) { return context.getString(R.string.apk); + } else if (mime.equals(ExportBackupService.MIME_TYPE)) { + return context.getString(R.string.conversations_backup); } else if (mime.contains("vcard")) { return context.getString(R.string.vcard); + } else if (mime.equals("text/x-vcalendar") || mime.equals("text/calendar")) { + return context.getString(R.string.event); } else if (mime.equals("application/epub+zip") || mime.equals("application/vnd.amazon.mobi8-ebook")) { return context.getString(R.string.ebook); } else { diff --git a/src/main/res/drawable-hdpi/ic_backup_black_48dp.png b/src/main/res/drawable-hdpi/ic_backup_black_48dp.png Binary files differnew file mode 100644 index 000000000..e5add7f82 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_backup_black_48dp.png diff --git a/src/main/res/drawable-hdpi/ic_backup_white_48dp.png b/src/main/res/drawable-hdpi/ic_backup_white_48dp.png Binary files differnew file mode 100644 index 000000000..57aac8f23 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_backup_white_48dp.png diff --git a/src/main/res/drawable-mdpi/ic_backup_black_48dp.png b/src/main/res/drawable-mdpi/ic_backup_black_48dp.png Binary files differnew file mode 100644 index 000000000..f83b4a074 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_backup_black_48dp.png diff --git a/src/main/res/drawable-mdpi/ic_backup_white_48dp.png b/src/main/res/drawable-mdpi/ic_backup_white_48dp.png Binary files differnew file mode 100644 index 000000000..cc199bd78 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_backup_white_48dp.png diff --git a/src/main/res/drawable-xhdpi/ic_backup_black_48dp.png b/src/main/res/drawable-xhdpi/ic_backup_black_48dp.png Binary files differnew file mode 100644 index 000000000..ba468fa18 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_backup_black_48dp.png diff --git a/src/main/res/drawable-xhdpi/ic_backup_white_48dp.png b/src/main/res/drawable-xhdpi/ic_backup_white_48dp.png Binary files differnew file mode 100644 index 000000000..11e4afcae --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_backup_white_48dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_backup_black_48dp.png b/src/main/res/drawable-xxhdpi/ic_backup_black_48dp.png Binary files differnew file mode 100644 index 000000000..08e7857b2 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_backup_black_48dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_backup_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_backup_white_48dp.png Binary files differnew file mode 100644 index 000000000..08ca7efac --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_backup_white_48dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_backup_black_48dp.png b/src/main/res/drawable-xxxhdpi/ic_backup_black_48dp.png Binary files differnew file mode 100644 index 000000000..182f1b2eb --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_backup_black_48dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_backup_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_backup_white_48dp.png Binary files differnew file mode 100644 index 000000000..c1863568c --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_backup_white_48dp.png diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml index 5a5bbc62a..e7d44519e 100644 --- a/src/main/res/values/attrs.xml +++ b/src/main/res/values/attrs.xml @@ -125,6 +125,8 @@ <attr name="ic_settings_expert" format="reference" /> <attr name="ic_settings_about" format="reference" /> + <attr name="media_preview_backup" format="reference" /> + <declare-styleable name="UnreadCountCustomView"> <attr name="backgroundColor" format="reference|color" /> </declare-styleable> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 3407f6e9e..287ccd23a 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -922,4 +922,7 @@ <string name="change_password_in_next_step">You can change the generated password in the next step to your needs.</string> <string name="action_unblock_participant">Unblock participant</string> <string name="action_block_participant">Block participant</string> + <string name="share_backup_files">Share backup files</string> + <string name="conversations_backup">Conversations backup</string> + <string name="event">Event</string> </resources> diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml index 6f9898a75..b9261a4d5 100644 --- a/src/main/res/values/themes.xml +++ b/src/main/res/values/themes.xml @@ -78,6 +78,7 @@ <item name="media_preview_archive" type="reference">@drawable/ic_archive_black_48dp</item> <item name="media_preview_ebook" type="reference">@drawable/ic_book_black_48dp</item> <item name="media_preview_unknown" type="reference">@drawable/ic_help_black_48dp</item> + <item name="media_preview_backup" type="reference">@drawable/ic_backup_black_48dp</item> <item name="icon_add_group" type="reference">@drawable/ic_group_add_white_24dp</item> <item name="icon_add_person" type="reference">@drawable/ic_person_add_white_24dp</item> @@ -254,6 +255,7 @@ <item name="media_preview_archive" type="reference">@drawable/ic_archive_white_48dp</item> <item name="media_preview_ebook" type="reference">@drawable/ic_book_white_48dp</item> <item name="media_preview_unknown" type="reference">@drawable/ic_help_white_48dp</item> + <item name="media_preview_backup" type="reference">@drawable/ic_backup_white_48dp</item> <item name="icon_add_group" type="reference">@drawable/ic_group_add_white_24dp</item> <item name="icon_add_person" type="reference">@drawable/ic_person_add_white_24dp</item> |