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