diff --git a/src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java b/src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java index 17c76d167d3b7e1fdb22096ec4403eaa5f829d2d..dd96468ee87427a2fb4a1ef319650c5ab9ff1abb 100644 --- a/src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java +++ b/src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java @@ -23,6 +23,8 @@ import androidx.core.app.NotificationManagerCompat; import com.google.common.base.Charsets; import com.google.common.base.Stopwatch; import com.google.common.io.CountingInputStream; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; @@ -64,6 +66,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; @@ -73,6 +77,9 @@ import javax.crypto.BadPaddingException; public class ImportBackupService extends Service { + private static final ExecutorService BACKUP_FILE_READER_EXECUTOR = + Executors.newSingleThreadExecutor(); + private static final int NOTIFICATION_ID = 21; private static final AtomicBoolean running = new AtomicBoolean(false); private final ImportBackupServiceBinder binder = new ImportBackupServiceBinder(); @@ -433,6 +440,10 @@ public class ImportBackupService extends Service { } } + public static ListenableFuture read(final Context context, final Uri uri) { + return Futures.submit(() -> BackupFile.read(context, uri), BACKUP_FILE_READER_EXECUTOR); + } + @Override public IBinder onBind(Intent intent) { return this.binder; @@ -475,7 +486,7 @@ public class ImportBackupService extends Service { throw new FileNotFoundException(); } final DataInputStream dataInputStream = new DataInputStream(inputStream); - BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream); + final BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream); inputStream.close(); return new BackupFile(uri, backupFileHeader); } diff --git a/src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java b/src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java index 3cbde4d5a4fed5ca2a213294d2edb6e78917ae42..331857e29fb2242060ee8a77d8afd4b29f3b9e50 100644 --- a/src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java +++ b/src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java @@ -19,6 +19,7 @@ import android.view.View; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import androidx.databinding.DataBindingUtil; @@ -27,6 +28,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; @@ -34,6 +37,7 @@ import eu.siacs.conversations.databinding.ActivityImportBackupBinding; import eu.siacs.conversations.databinding.DialogEnterPasswordBinding; import eu.siacs.conversations.services.ImportBackupService; import eu.siacs.conversations.ui.adapter.BackupFileAdapter; +import eu.siacs.conversations.ui.util.MainThreadExecutor; import eu.siacs.conversations.utils.BackupFileHeader; import java.io.IOException; @@ -182,21 +186,36 @@ public class ImportBackupActivity extends ActionBarActivity } private void openBackupFileFromUri(final Uri uri, final boolean finishOnCancel) { - try { - final ImportBackupService.BackupFile backupFile = - ImportBackupService.BackupFile.read(this, uri); - showEnterPasswordDialog(backupFile, finishOnCancel); - } catch (final BackupFileHeader.OutdatedBackupFileVersion e) { + final var backupFileFuture = ImportBackupService.read(this, uri); + Futures.addCallback( + backupFileFuture, + new FutureCallback<>() { + @Override + public void onSuccess(final ImportBackupService.BackupFile backupFile) { + showEnterPasswordDialog(backupFile, finishOnCancel); + } + + @Override + public void onFailure(@NonNull final Throwable throwable) { + Log.d(Config.LOGTAG, "could not open backup file " + uri, throwable); + showBackupThrowable(throwable); + } + }, + MainThreadExecutor.getInstance()); + } + + private void showBackupThrowable(final Throwable throwable) { + if (throwable instanceof BackupFileHeader.OutdatedBackupFileVersion) { Snackbar.make( binding.coordinator, R.string.outdated_backup_file_format, Snackbar.LENGTH_LONG) .show(); - } catch (final IOException | IllegalArgumentException e) { - Log.d(Config.LOGTAG, "unable to open backup file " + uri, e); + } else if (throwable instanceof IOException + || throwable instanceof IllegalArgumentException) { Snackbar.make(binding.coordinator, R.string.not_a_backup_file, Snackbar.LENGTH_LONG) .show(); - } catch (final SecurityException e) { + } else if (throwable instanceof SecurityException e) { Snackbar.make( binding.coordinator, R.string.sharing_application_not_grant_permission, @@ -243,16 +262,7 @@ public class ImportBackupActivity extends ActionBarActivity getString(R.string.please_enter_password)); return; } - final Uri uri = backupFile.getUri(); - Intent intent = new Intent(this, ImportBackupService.class); - intent.setAction(Intent.ACTION_SEND); - intent.putExtra("password", password); - if ("file".equals(uri.getScheme())) { - intent.putExtra("file", uri.getPath()); - } else { - intent.setData(uri); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - } + final Intent intent = getIntent(backupFile, password); setLoadingState(true); ContextCompat.startForegroundService(this, intent); d.dismiss(); @@ -261,6 +271,21 @@ public class ImportBackupActivity extends ActionBarActivity dialog.show(); } + @NonNull + private Intent getIntent(ImportBackupService.BackupFile backupFile, String password) { + final Uri uri = backupFile.getUri(); + Intent intent = new Intent(this, ImportBackupService.class); + intent.setAction(Intent.ACTION_SEND); + intent.putExtra("password", password); + if ("file".equals(uri.getScheme())) { + intent.putExtra("file", uri.getPath()); + } else { + intent.setData(uri); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } + return intent; + } + private void setLoadingState(final boolean loadingState) { binding.coordinator.setVisibility(loadingState ? View.GONE : View.VISIBLE); binding.inProgress.setVisibility(loadingState ? View.VISIBLE : View.GONE);