diff options
author | Christian Schneppe <christian@pix-art.de> | 2019-08-31 14:45:36 +0200 |
---|---|---|
committer | Christian Schneppe <christian@pix-art.de> | 2019-08-31 14:45:36 +0200 |
commit | 47c29e584229f1fe98d5456d66a32bb9f081afa5 (patch) | |
tree | faafd4a0bfac960d66697512d96e8622b7fdcecb /src/main | |
parent | f7c293387b84f9fc416529ed6c08217a35b0474e (diff) |
allow backup to be restored from selected file
Diffstat (limited to 'src/main')
-rw-r--r-- | src/main/java/de/pixart/messenger/services/ImportBackupService.java | 82 | ||||
-rw-r--r-- | src/main/java/de/pixart/messenger/ui/ImportBackupActivity.java | 76 | ||||
-rw-r--r-- | src/main/res/drawable-hdpi/ic_cloud_download_white_24dp.png | bin | 0 -> 463 bytes | |||
-rw-r--r-- | src/main/res/drawable-mdpi/ic_cloud_download_white_24dp.png | bin | 0 -> 262 bytes | |||
-rw-r--r-- | src/main/res/drawable-xhdpi/ic_cloud_download_white_24dp.png | bin | 0 -> 491 bytes | |||
-rw-r--r-- | src/main/res/drawable-xxhdpi/ic_cloud_download_white_24dp.png | bin | 0 -> 900 bytes | |||
-rw-r--r-- | src/main/res/drawable-xxxhdpi/ic_cloud_download_white_24dp.png | bin | 0 -> 985 bytes | |||
-rw-r--r-- | src/main/res/menu/import_backup.xml | 11 | ||||
-rw-r--r-- | src/main/res/values/attrs.xml | 2 | ||||
-rw-r--r-- | src/main/res/values/strings.xml | 3 | ||||
-rw-r--r-- | src/main/res/values/themes.xml | 3 |
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 Binary files differnew file mode 100644 index 000000000..7bd405bf8 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_cloud_download_white_24dp.png 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 Binary files differnew file mode 100644 index 000000000..1936cf196 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_cloud_download_white_24dp.png 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 Binary files differnew file mode 100644 index 000000000..b7ce55e69 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_cloud_download_white_24dp.png 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 Binary files differnew file mode 100644 index 000000000..901546a91 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_cloud_download_white_24dp.png 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 Binary files differnew file mode 100644 index 000000000..1767bdbfd --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_cloud_download_white_24dp.png 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> |