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/java | |
parent | f7c293387b84f9fc416529ed6c08217a35b0474e (diff) |
allow backup to be restored from selected file
Diffstat (limited to 'src/main/java')
-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 |
2 files changed, 136 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 |