aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Schneppe <christian@pix-art.de>2019-08-31 14:45:36 +0200
committerChristian Schneppe <christian@pix-art.de>2019-08-31 14:45:36 +0200
commit47c29e584229f1fe98d5456d66a32bb9f081afa5 (patch)
treefaafd4a0bfac960d66697512d96e8622b7fdcecb
parentf7c293387b84f9fc416529ed6c08217a35b0474e (diff)
allow backup to be restored from selected file
-rw-r--r--src/main/java/de/pixart/messenger/services/ImportBackupService.java82
-rw-r--r--src/main/java/de/pixart/messenger/ui/ImportBackupActivity.java76
-rw-r--r--src/main/res/drawable-hdpi/ic_cloud_download_white_24dp.pngbin0 -> 463 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_cloud_download_white_24dp.pngbin0 -> 262 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_cloud_download_white_24dp.pngbin0 -> 491 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_cloud_download_white_24dp.pngbin0 -> 900 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_cloud_download_white_24dp.pngbin0 -> 985 bytes
-rw-r--r--src/main/res/menu/import_backup.xml11
-rw-r--r--src/main/res/values/attrs.xml2
-rw-r--r--src/main/res/values/strings.xml3
-rw-r--r--src/main/res/values/themes.xml3
11 files changed, 155 insertions, 22 deletions
diff --git a/src/main/java/de/pixart/messenger/services/ImportBackupService.java b/src/main/java/de/pixart/messenger/services/ImportBackupService.java
index 9a1ddece2..8bb912b8b 100644
--- a/src/main/java/de/pixart/messenger/services/ImportBackupService.java
+++ b/src/main/java/de/pixart/messenger/services/ImportBackupService.java
@@ -7,6 +7,7 @@ import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
@@ -16,7 +17,9 @@ import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
@@ -27,6 +30,7 @@ import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.GZIPInputStream;
+import java.util.zip.ZipException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@@ -80,14 +84,22 @@ public class ImportBackupService extends Service {
return START_NOT_STICKY;
}
final String password = intent.getStringExtra("password");
- final String file = intent.getStringExtra("file");
- if (password == null || file == null) {
+ final Uri data = intent.getData();
+ final Uri uri;
+ if (data == null) {
+ final String file = intent.getStringExtra("file");
+ uri = file == null ? null : Uri.fromFile(new File(file));
+ } else {
+ uri = data;
+ }
+
+ if (password == null || uri == null) {
return START_NOT_STICKY;
}
if (running.compareAndSet(false, true)) {
executor.execute(() -> {
startForegroundService();
- final boolean success = importBackup(new File(file), password);
+ final boolean success = importBackup(uri, password);
stopForeground(true);
running.set(false);
if (success) {
@@ -144,21 +156,41 @@ public class ImportBackupService extends Service {
startForeground(NOTIFICATION_ID, mBuilder.build());
}
- private boolean importBackup(File file, String password) {
- Log.d(Config.LOGTAG, "importing backup from file " + file.getAbsolutePath());
+ private boolean importBackup(Uri uri, String password) {
+ Log.d(Config.LOGTAG, "importing backup from " + uri);
+ if (password == null || password.isEmpty()) {
+ synchronized (mOnBackupProcessedListeners) {
+ for (OnBackupProcessed l : mOnBackupProcessedListeners) {
+ l.onBackupDecryptionFailed();
+ }
+ }
+ return false;
+ }
try {
SQLiteDatabase db = mDatabaseBackend.getWritableDatabase();
- final FileInputStream fileInputStream = new FileInputStream(file);
- final DataInputStream dataInputStream = new DataInputStream(fileInputStream);
- BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream);
+ final InputStream inputStream;
+ if ("file".equals(uri.getScheme())) {
+ inputStream = new FileInputStream(new File(uri.getPath()));
+ } else {
+ inputStream = getContentResolver().openInputStream(uri);
+ }
+ final DataInputStream dataInputStream = new DataInputStream(inputStream);
+ final BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream);
Log.d(Config.LOGTAG, backupFileHeader.toString());
-
+ if (mDatabaseBackend.getAccountJids(false).contains(backupFileHeader.getJid())) {
+ synchronized (mOnBackupProcessedListeners) {
+ for (OnBackupProcessed l : mOnBackupProcessedListeners) {
+ l.onAccountAlreadySetup();
+ }
+ }
+ return false;
+ }
final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
byte[] key = ExportBackupService.getKey(password, backupFileHeader.getSalt());
SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(backupFileHeader.getIv());
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
- CipherInputStream cipherInputStream = new CipherInputStream(fileInputStream, cipher);
+ CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
GZIPInputStream gzipInputStream = new GZIPInputStream(cipherInputStream);
BufferedReader reader = new BufferedReader(new InputStreamReader(gzipInputStream, "UTF-8"));
@@ -196,8 +228,7 @@ public class ImportBackupService extends Service {
return true;
} catch (Exception e) {
Throwable throwable = e.getCause();
- final boolean reasonWasCrypto;
- reasonWasCrypto = throwable instanceof BadPaddingException;
+ final boolean reasonWasCrypto = throwable instanceof BadPaddingException || e instanceof ZipException;
synchronized (mOnBackupProcessedListeners) {
for (OnBackupProcessed l : mOnBackupProcessedListeners) {
if (reasonWasCrypto) {
@@ -207,7 +238,7 @@ public class ImportBackupService extends Service {
}
}
}
- Log.d(Config.LOGTAG, "error restoring backup " + file.getAbsolutePath(), e);
+ Log.d(Config.LOGTAG, "error restoring backup " + uri, e);
return false;
}
}
@@ -254,14 +285,16 @@ public class ImportBackupService extends Service {
void onBackupDecryptionFailed();
void onBackupRestoreFailed();
+
+ void onAccountAlreadySetup();
}
public static class BackupFile {
- private final File file;
+ private final Uri uri;
private final BackupFileHeader header;
- private BackupFile(File file, BackupFileHeader header) {
- this.file = file;
+ private BackupFile(Uri uri, BackupFileHeader header) {
+ this.uri = uri;
this.header = header;
}
@@ -270,15 +303,26 @@ public class ImportBackupService extends Service {
final DataInputStream dataInputStream = new DataInputStream(fileInputStream);
BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream);
fileInputStream.close();
- return new BackupFile(file, backupFileHeader);
+ return new BackupFile(Uri.fromFile(file), backupFileHeader);
+ }
+
+ public static BackupFile read(final Context context, final Uri uri) throws IOException {
+ final InputStream inputStream = context.getContentResolver().openInputStream(uri);
+ if (inputStream == null) {
+ throw new FileNotFoundException();
+ }
+ final DataInputStream dataInputStream = new DataInputStream(inputStream);
+ BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream);
+ inputStream.close();
+ return new BackupFile(uri, backupFileHeader);
}
public BackupFileHeader getHeader() {
return header;
}
- public File getFile() {
- return file;
+ public Uri getUri() {
+ return uri;
}
}
diff --git a/src/main/java/de/pixart/messenger/ui/ImportBackupActivity.java b/src/main/java/de/pixart/messenger/ui/ImportBackupActivity.java
index 5414e6bbf..e0cdf1431 100644
--- a/src/main/java/de/pixart/messenger/ui/ImportBackupActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ImportBackupActivity.java
@@ -5,6 +5,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.databinding.DataBindingUtil;
+import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.support.design.widget.Snackbar;
@@ -13,8 +15,11 @@ import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.View;
+import java.io.IOException;
import java.util.List;
import de.pixart.messenger.Config;
@@ -30,6 +35,8 @@ public class ImportBackupActivity extends XmppActivity implements ServiceConnect
private BackupFileAdapter backupFileAdapter;
private ImportBackupService service;
+ private boolean mLoadingState = false;
+
@Override
protected void onCreate(final Bundle savedInstanceState) {
@@ -43,6 +50,14 @@ public class ImportBackupActivity extends XmppActivity implements ServiceConnect
}
@Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ getMenuInflater().inflate(R.menu.import_backup, menu);
+ final MenuItem openBackup = menu.findItem(R.id.action_open_backup_file);
+ openBackup.setVisible(!this.mLoadingState);
+ return true;
+ }
+
+ @Override
protected void refreshUiReal() {
}
@@ -94,9 +109,22 @@ public class ImportBackupActivity extends XmppActivity implements ServiceConnect
}
@Override
- public void onClick(ImportBackupService.BackupFile backupFile) {
+ public void onClick(final ImportBackupService.BackupFile backupFile) {
+ showEnterPasswordDialog(backupFile);
+ }
+
+ private void openBackupFileFromUri(final Uri uri) {
+ try {
+ final ImportBackupService.BackupFile backupFile = ImportBackupService.BackupFile.read(this, uri);
+ showEnterPasswordDialog(backupFile);
+ } catch (IOException e) {
+ Snackbar.make(binding.coordinator, R.string.not_a_backup_file, Snackbar.LENGTH_LONG).show();
+ }
+ }
+
+ private void showEnterPasswordDialog(final ImportBackupService.BackupFile backupFile) {
final DialogEnterPasswordBinding enterPasswordBinding = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.dialog_enter_password, null, false);
- Log.d(Config.LOGTAG, "attempting to import " + backupFile.getFile().getAbsolutePath());
+ Log.d(Config.LOGTAG, "attempting to import " + backupFile.getUri());
enterPasswordBinding.explain.setText(getString(R.string.enter_password_to_restore, backupFile.getHeader().getJid().toString()));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(enterPasswordBinding.getRoot());
@@ -104,9 +132,16 @@ public class ImportBackupActivity extends XmppActivity implements ServiceConnect
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.restore, (dialog, which) -> {
final String password = enterPasswordBinding.accountPassword.getEditableText().toString();
+ final Uri uri = backupFile.getUri();
Intent intent = new Intent(this, ImportBackupService.class);
+ intent.setAction(Intent.ACTION_SEND);
intent.putExtra("password", password);
- intent.putExtra("file", backupFile.getFile().getAbsolutePath());
+ if ("file".equals(uri.getScheme())) {
+ intent.putExtra("file", uri.getPath());
+ } else {
+ intent.setData(uri);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
setLoadingState(true);
ContextCompat.startForegroundService(this, intent);
});
@@ -119,6 +154,25 @@ public class ImportBackupActivity extends XmppActivity implements ServiceConnect
binding.inProgress.setVisibility(loadingState ? View.VISIBLE : View.GONE);
setTitle(loadingState ? R.string.restoring_backup : R.string.restore_backup);
configureActionBar(getSupportActionBar(), !loadingState);
+ this.mLoadingState = loadingState;
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ if (resultCode == RESULT_OK) {
+ if (requestCode == 0xbac) {
+ openBackupFileFromUri(intent.getData());
+ }
+ }
+ }
+
+ @Override
+ public void onAccountAlreadySetup() {
+ runOnUiThread(() -> {
+ setLoadingState(false);
+ Snackbar.make(binding.coordinator, R.string.account_already_setup, Snackbar.LENGTH_LONG).show();
+ });
}
@Override
@@ -151,4 +205,20 @@ public class ImportBackupActivity extends XmppActivity implements ServiceConnect
Snackbar.make(binding.coordinator, R.string.unable_to_restore_backup, Snackbar.LENGTH_LONG).show();
});
}
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_open_backup_file:
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("*/*");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
+ }
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ startActivityForResult(Intent.createChooser(intent, getString(R.string.open_backup)), 0xbac);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
} \ No newline at end of file
diff --git a/src/main/res/drawable-hdpi/ic_cloud_download_white_24dp.png b/src/main/res/drawable-hdpi/ic_cloud_download_white_24dp.png
new file mode 100644
index 000000000..7bd405bf8
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_cloud_download_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_cloud_download_white_24dp.png b/src/main/res/drawable-mdpi/ic_cloud_download_white_24dp.png
new file mode 100644
index 000000000..1936cf196
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_cloud_download_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_cloud_download_white_24dp.png b/src/main/res/drawable-xhdpi/ic_cloud_download_white_24dp.png
new file mode 100644
index 000000000..b7ce55e69
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_cloud_download_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_cloud_download_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_cloud_download_white_24dp.png
new file mode 100644
index 000000000..901546a91
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_cloud_download_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_cloud_download_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_cloud_download_white_24dp.png
new file mode 100644
index 000000000..1767bdbfd
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_cloud_download_white_24dp.png
Binary files differ
diff --git a/src/main/res/menu/import_backup.xml b/src/main/res/menu/import_backup.xml
new file mode 100644
index 000000000..9d0453ac6
--- /dev/null
+++ b/src/main/res/menu/import_backup.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/action_open_backup_file"
+ android:icon="?attr/ic_cloud_download"
+ android:title="@string/open_backup"
+ app:showAsAction="always" />
+
+</menu> \ No newline at end of file
diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml
index e7d44519e..afea492b3 100644
--- a/src/main/res/values/attrs.xml
+++ b/src/main/res/values/attrs.xml
@@ -99,6 +99,8 @@
<attr name="ic_attach_video" format="reference" />
<attr name="ic_attach_videocam" format="reference" />
+ <attr name="ic_cloud_download" format="reference" />
+
<attr name="ic_file_apk" format="reference" />
<attr name="ic_file_pdf" format="reference" />
<attr name="ic_file_vcard" format="reference" />
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 8b97b2af6..d29ff110b 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -927,4 +927,7 @@
<string name="event">Event</string>
<string name="pref_vibrate_in_chat_summary">Vibrate when a new message arrives in an open chat</string>
<string name="pref_vibrate_in_chat">Vibrate in open chat</string>
+ <string name="open_backup">Open backup</string>
+ <string name="not_a_backup_file">The file you selected is not a Conversations backup file</string>
+ <string name="account_already_setup">This account has already been setup</string>
</resources>
diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml
index b9261a4d5..fb91cb4a1 100644
--- a/src/main/res/values/themes.xml
+++ b/src/main/res/values/themes.xml
@@ -104,6 +104,7 @@
<item name="icon_settings" type="reference">@drawable/ic_settings_black_24dp</item>
<item name="icon_delete" type="reference">@drawable/ic_delete_black_24dp</item>
<item name="icon_share" type="reference">@drawable/ic_share_black_24dp</item>
+ <item name="ic_cloud_download" type="reference">@drawable/ic_cloud_download_white_24dp</item>
<item name="icon_scan_qr_code" type="reference">@drawable/ic_qrcode_scan_white_24dp</item>
<item name="icon_scroll_down" type="reference">@drawable/ic_scroll_to_end_black</item>
@@ -280,6 +281,8 @@
<item name="icon_settings" type="reference">@drawable/ic_settings_white_24dp</item>
<item name="icon_delete" type="reference">@drawable/ic_delete_white_24dp</item>
<item name="icon_share" type="reference">@drawable/ic_share_white_24dp</item>
+ <item name="ic_cloud_download" type="reference">@drawable/ic_cloud_download_white_24dp
+ </item>
<item name="icon_scan_qr_code" type="reference">@drawable/ic_qrcode_scan_white_24dp</item>
<item name="icon_scroll_down" type="reference">@drawable/ic_scroll_to_end_white</item>