ImportBackupActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.content.ComponentName;
  4import android.content.Context;
  5import android.content.DialogInterface;
  6import android.content.Intent;
  7import android.content.ServiceConnection;
  8import android.net.Uri;
  9import android.os.Bundle;
 10import android.os.IBinder;
 11import android.util.Log;
 12import android.view.LayoutInflater;
 13import android.view.Menu;
 14import android.view.MenuItem;
 15import android.view.View;
 16
 17import androidx.appcompat.app.AlertDialog;
 18import androidx.core.content.ContextCompat;
 19import androidx.databinding.DataBindingUtil;
 20
 21import com.google.android.material.snackbar.Snackbar;
 22
 23import java.io.IOException;
 24import java.util.List;
 25
 26import eu.siacs.conversations.Config;
 27import eu.siacs.conversations.R;
 28import eu.siacs.conversations.databinding.ActivityImportBackupBinding;
 29import eu.siacs.conversations.databinding.DialogEnterPasswordBinding;
 30import eu.siacs.conversations.services.ImportBackupService;
 31import eu.siacs.conversations.ui.adapter.BackupFileAdapter;
 32
 33public class ImportBackupActivity extends ActionBarActivity implements ServiceConnection, ImportBackupService.OnBackupFilesLoaded, BackupFileAdapter.OnItemClickedListener, ImportBackupService.OnBackupProcessed {
 34
 35    private ActivityImportBackupBinding binding;
 36
 37    private BackupFileAdapter backupFileAdapter;
 38    private ImportBackupService service;
 39
 40    private boolean mLoadingState = false;
 41
 42    private int mTheme;
 43
 44    @Override
 45    protected void onCreate(final Bundle savedInstanceState) {
 46        super.onCreate(savedInstanceState);
 47        binding = DataBindingUtil.setContentView(this, R.layout.activity_import_backup);
 48        setSupportActionBar(binding.toolbar);
 49        setLoadingState(savedInstanceState != null && savedInstanceState.getBoolean("loading_state", false));
 50        this.backupFileAdapter = new BackupFileAdapter();
 51        this.binding.list.setAdapter(this.backupFileAdapter);
 52        this.backupFileAdapter.setOnItemClickedListener(this);
 53    }
 54
 55    @Override
 56    public boolean onCreateOptionsMenu(final Menu menu) {
 57        getMenuInflater().inflate(R.menu.import_backup, menu);
 58        final MenuItem openBackup = menu.findItem(R.id.action_open_backup_file);
 59        openBackup.setVisible(!this.mLoadingState);
 60        return true;
 61    }
 62
 63    @Override
 64    public void onSaveInstanceState(Bundle bundle) {
 65        bundle.putBoolean("loading_state", this.mLoadingState);
 66        super.onSaveInstanceState(bundle);
 67    }
 68
 69    @Override
 70    public void onStart() {
 71        super.onStart();
 72        bindService(new Intent(this, ImportBackupService.class), this, Context.BIND_AUTO_CREATE);
 73        final Intent intent = getIntent();
 74        if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction()) && !this.mLoadingState) {
 75            Uri uri = intent.getData();
 76            if (uri != null) {
 77                openBackupFileFromUri(uri, true);
 78            }
 79        }
 80    }
 81
 82    @Override
 83    public void onStop() {
 84        super.onStop();
 85        if (this.service != null) {
 86            this.service.removeOnBackupProcessedListener(this);
 87        }
 88        unbindService(this);
 89    }
 90
 91    @Override
 92    public void onServiceConnected(ComponentName name, IBinder service) {
 93        ImportBackupService.ImportBackupServiceBinder binder = (ImportBackupService.ImportBackupServiceBinder) service;
 94        this.service = binder.getService();
 95        this.service.addOnBackupProcessedListener(this);
 96        setLoadingState(this.service.getLoadingState());
 97        this.service.loadBackupFiles(this);
 98    }
 99
100    @Override
101    public void onServiceDisconnected(ComponentName name) {
102        this.service = null;
103    }
104
105    @Override
106    public void onBackupFilesLoaded(final List<ImportBackupService.BackupFile> files) {
107        runOnUiThread(() -> backupFileAdapter.setFiles(files));
108    }
109
110    @Override
111    public void onClick(final ImportBackupService.BackupFile backupFile) {
112        showEnterPasswordDialog(backupFile, false);
113    }
114
115    private void openBackupFileFromUri(final Uri uri, final boolean finishOnCancel) {
116        new Thread(() -> {
117                try {
118                    final ImportBackupService.BackupFile backupFile = ImportBackupService.BackupFile.read(this, uri);
119                    runOnUiThread(() -> showEnterPasswordDialog(backupFile, finishOnCancel));
120                } catch (final IOException | IllegalArgumentException e) {
121                    Log.d(Config.LOGTAG, "unable to open backup file " + uri, e);
122                    runOnUiThread(() -> Snackbar.make(binding.coordinator, R.string.not_a_backup_file, Snackbar.LENGTH_LONG).show());
123                }
124        }).start();
125    }
126
127    private void showEnterPasswordDialog(final ImportBackupService.BackupFile backupFile, final boolean finishOnCancel) {
128        final DialogEnterPasswordBinding enterPasswordBinding = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.dialog_enter_password, null, false);
129        Log.d(Config.LOGTAG, "attempting to import " + backupFile.getUri());
130        enterPasswordBinding.explain.setText(getString(R.string.enter_password_to_restore, backupFile.getHeader().getJid().toString()));
131        AlertDialog.Builder builder = new AlertDialog.Builder(this);
132        builder.setView(enterPasswordBinding.getRoot());
133        builder.setTitle(R.string.enter_password);
134        builder.setNegativeButton(R.string.cancel, (dialog, which) -> {
135            if (finishOnCancel) {
136                finish();
137            }
138        });
139        builder.setPositiveButton(R.string.restore, null);
140        builder.setCancelable(false);
141        final AlertDialog dialog = builder.create();
142        dialog.setOnShowListener((d) -> {
143            dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> {
144                final String password = enterPasswordBinding.accountPassword.getEditableText().toString();
145                if (password.isEmpty()) {
146                    enterPasswordBinding.accountPasswordLayout.setError(getString(R.string.please_enter_password));
147                    return;
148                }
149                final Uri uri = backupFile.getUri();
150                Intent intent = new Intent(this, ImportBackupService.class);
151                intent.setAction(Intent.ACTION_SEND);
152                intent.putExtra("password", password);
153                if ("file".equals(uri.getScheme())) {
154                    intent.putExtra("file", uri.getPath());
155                } else {
156                    intent.setData(uri);
157                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
158                }
159                setLoadingState(true);
160                ContextCompat.startForegroundService(this, intent);
161                d.dismiss();
162            });
163        });
164        dialog.show();
165    }
166
167    private void setLoadingState(final boolean loadingState) {
168        binding.coordinator.setVisibility(loadingState ? View.GONE : View.VISIBLE);
169        binding.inProgress.setVisibility(loadingState ? View.VISIBLE : View.GONE);
170        setTitle(loadingState ? R.string.restoring_backup : R.string.restore_backup);
171        configureActionBar(getSupportActionBar(), !loadingState);
172        this.mLoadingState = loadingState;
173        invalidateOptionsMenu();
174    }
175
176    @Override
177    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
178        super.onActivityResult(requestCode, resultCode, intent);
179        if (resultCode == RESULT_OK) {
180            if (requestCode == 0xbac) {
181                openBackupFileFromUri(intent.getData(), false);
182            }
183        }
184    }
185
186    @Override
187    public void onAccountAlreadySetup() {
188        runOnUiThread(() -> {
189            setLoadingState(false);
190            Snackbar.make(binding.coordinator, R.string.account_already_setup, Snackbar.LENGTH_LONG).show();
191        });
192    }
193
194    @Override
195    public void onBackupRestored() {
196        runOnUiThread(() -> {
197            Intent intent = new Intent(this, ConversationActivity.class);
198            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
199            startActivity(intent);
200            finish();
201        });
202    }
203
204    @Override
205    public void onBackupDecryptionFailed() {
206        runOnUiThread(() -> {
207            setLoadingState(false);
208            Snackbar.make(binding.coordinator, R.string.unable_to_decrypt_backup, Snackbar.LENGTH_LONG).show();
209        });
210    }
211
212    @Override
213    public void onBackupRestoreFailed() {
214        runOnUiThread(() -> {
215            setLoadingState(false);
216            Snackbar.make(binding.coordinator, R.string.unable_to_restore_backup, Snackbar.LENGTH_LONG).show();
217        });
218    }
219
220    @Override
221    public boolean onOptionsItemSelected(MenuItem item) {
222        if (item.getItemId() == R.id.action_open_backup_file) {
223            openBackupFile();
224            return true;
225        }
226        return super.onOptionsItemSelected(item);
227    }
228
229    private void openBackupFile() {
230        final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
231        intent.setType("*/*");
232        intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
233        intent.addCategory(Intent.CATEGORY_OPENABLE);
234        startActivityForResult(Intent.createChooser(intent, getString(R.string.open_backup)), 0xbac);
235    }
236}