diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index eb1ea9b4..1d12f571 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -30,8 +30,8 @@
android:name=".ui.AboutActivity"
android:label="@string/title_activity_about" />
+ android:name=".ui.ImportEntriesActivity"
+ android:label="@string/title_activity_import_entries" />
diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/AegisImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/AegisImporter.java
index e767b0be..706cb1c2 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/importers/AegisImporter.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/importers/AegisImporter.java
@@ -1,9 +1,14 @@
package com.beemdevelopment.aegis.importers;
import android.content.Context;
+import android.content.DialogInterface;
+
+import androidx.lifecycle.Lifecycle;
import com.beemdevelopment.aegis.encoding.EncodingException;
+import com.beemdevelopment.aegis.helpers.ContextHelper;
import com.beemdevelopment.aegis.otp.OtpInfoException;
+import com.beemdevelopment.aegis.ui.Dialogs;
import com.beemdevelopment.aegis.ui.tasks.PasswordSlotDecryptTask;
import com.beemdevelopment.aegis.util.IOUtils;
import com.beemdevelopment.aegis.vault.VaultEntry;
@@ -79,7 +84,26 @@ public class AegisImporter extends DatabaseImporter {
@Override
public void decrypt(Context context, DecryptListener listener) {
+ Dialogs.showPasswordInputDialog(context, (Dialogs.TextInputListener) password -> {
+ List slots = getSlots().findAll(PasswordSlot.class);
+ PasswordSlotDecryptTask.Params params = new PasswordSlotDecryptTask.Params(slots, password);
+ PasswordSlotDecryptTask task = new PasswordSlotDecryptTask(context, result -> {
+ try {
+ if (result == null) {
+ throw new DatabaseImporterException("Password incorrect");
+ }
+ VaultFileCredentials creds = new VaultFileCredentials(result.getKey(), getSlots());
+ State state = decrypt(creds);
+ listener.onStateDecrypted(state);
+ } catch (DatabaseImporterException e) {
+ listener.onError(e);
+ }
+ });
+
+ Lifecycle lifecycle = ContextHelper.getLifecycle(context);
+ task.execute(lifecycle, params);
+ }, (DialogInterface.OnCancelListener) dialog -> listener.onCanceled());
}
}
diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/AndOtpImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/AndOtpImporter.java
index 9049f28a..17a43c9b 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/importers/AndOtpImporter.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/importers/AndOtpImporter.java
@@ -190,7 +190,7 @@ public class AndOtpImporter extends DatabaseImporter {
} catch (DatabaseImporterException e) {
listener.onError(e);
}
- });
+ }, dialog1 -> listener.onCanceled());
})
.create());
}
diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/AuthenticatorPlusImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/AuthenticatorPlusImporter.java
index 9f49cc10..9b4df7cd 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/importers/AuthenticatorPlusImporter.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/importers/AuthenticatorPlusImporter.java
@@ -71,7 +71,7 @@ public class AuthenticatorPlusImporter extends DatabaseImporter {
} catch (DatabaseImporterException e) {
listener.onError(e);
}
- });
+ }, dialog1 -> listener.onCanceled());
}
}
}
diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/AuthyImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/AuthyImporter.java
index ec99294a..3e80e30d 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/importers/AuthyImporter.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/importers/AuthyImporter.java
@@ -204,7 +204,7 @@ public class AuthyImporter extends DatabaseImporter {
} catch (DatabaseImporterException e) {
listener.onError(e);
}
- });
+ }, dialog1 -> listener.onCanceled());
}
}
diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java
index 9ea576c8..f3f58864 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java
@@ -167,8 +167,9 @@ public abstract class DatabaseImporter {
}
}
- public interface DecryptListener {
- void onStateDecrypted(State state);
- void onError(Exception e);
+ public static abstract class DecryptListener {
+ protected abstract void onStateDecrypted(State state);
+ protected abstract void onError(Exception e);
+ protected abstract void onCanceled();
}
}
diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/TotpAuthenticatorImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/TotpAuthenticatorImporter.java
index b042541e..8b29f372 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/importers/TotpAuthenticatorImporter.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/importers/TotpAuthenticatorImporter.java
@@ -159,7 +159,7 @@ public class TotpAuthenticatorImporter extends DatabaseImporter {
.setPositiveButton(R.string.yes, (dialog, which) -> {
Dialogs.showPasswordInputDialog(context, password -> {
decrypt(password, listener);
- });
+ }, dialog1 -> listener.onCanceled());
})
.setNegativeButton(R.string.no, (dialog, which) -> {
decrypt(PASSWORD, listener);
diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/Dialogs.java b/app/src/main/java/com/beemdevelopment/aegis/ui/Dialogs.java
index ed7eb818..61891f74 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/ui/Dialogs.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/ui/Dialogs.java
@@ -188,7 +188,7 @@ public class Dialogs {
showSecureDialog(dialog);
}
- private static void showTextInputDialog(Context context, @StringRes int titleId, @StringRes int messageId, @StringRes int hintId, TextInputListener listener, DialogInterface.OnDismissListener dismissListener, boolean isSecret) {
+ private static void showTextInputDialog(Context context, @StringRes int titleId, @StringRes int messageId, @StringRes int hintId, TextInputListener listener, DialogInterface.OnCancelListener cancelListener, boolean isSecret) {
View view = LayoutInflater.from(context).inflate(R.layout.dialog_text_input, null);
TextInputEditText input = view.findViewById(R.id.text_input);
TextInputLayout inputLayout = view.findViewById(R.id.text_input_layout);
@@ -205,8 +205,8 @@ public class Dialogs {
listener.onTextInputResult(text);
});
- if (dismissListener != null) {
- builder.setOnDismissListener(dismissListener);
+ if (cancelListener != null) {
+ builder.setOnCancelListener(cancelListener);
}
if (messageId != 0) {
@@ -214,6 +214,7 @@ public class Dialogs {
}
AlertDialog dialog = builder.create();
+ dialog.setCanceledOnTouchOutside(true);
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
showSecureDialog(dialog);
}
@@ -230,12 +231,20 @@ public class Dialogs {
showTextInputDialog(context, R.string.set_password, R.string.password, listener, true);
}
+ public static void showPasswordInputDialog(Context context, TextInputListener listener, DialogInterface.OnCancelListener cancelListener) {
+ showTextInputDialog(context, R.string.set_password, 0, R.string.password, listener, cancelListener, true);
+ }
+
public static void showPasswordInputDialog(Context context, @StringRes int messageId, TextInputListener listener) {
showTextInputDialog(context, R.string.set_password, messageId, R.string.password, listener, null, true);
}
- public static void showPasswordInputDialog(Context context, @StringRes int setPasswordMessageId, @StringRes int messageId, TextInputListener listener, DialogInterface.OnDismissListener dismissListener) {
- showTextInputDialog(context, setPasswordMessageId, messageId, R.string.password, listener, dismissListener, true);
+ public static void showPasswordInputDialog(Context context, @StringRes int messageId, TextInputListener listener, DialogInterface.OnCancelListener cancelListener) {
+ showTextInputDialog(context, R.string.set_password, messageId, R.string.password, listener, cancelListener, true);
+ }
+
+ public static void showPasswordInputDialog(Context context, @StringRes int titleId, @StringRes int messageId, TextInputListener listener, DialogInterface.OnCancelListener cancelListener) {
+ showTextInputDialog(context, titleId, messageId, R.string.password, listener, cancelListener, true);
}
public static void showCheckboxDialog(Context context, @StringRes int titleId, @StringRes int messageId, @StringRes int checkboxMessageId, CheckboxInputListener listener) {
@@ -334,6 +343,7 @@ public class Dialogs {
AlertDialog dialog = new AlertDialog.Builder(context)
.setTitle(R.string.error_occurred)
.setView(view)
+ .setCancelable(false)
.setPositiveButton(android.R.string.ok, (dialog1, which) -> {
if (listener != null) {
listener.onClick(dialog1, which);
@@ -342,11 +352,6 @@ public class Dialogs {
.setNeutralButton(R.string.details, (dialog1, which) -> {
textDetails.setVisibility(View.VISIBLE);
})
- .setOnDismissListener(dialog12 -> {
- if (listener != null) {
- listener.onClick(dialog12, -1);
- }
- })
.create();
dialog.setOnShowListener(d -> {
diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java
new file mode 100644
index 00000000..10986050
--- /dev/null
+++ b/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java
@@ -0,0 +1,263 @@
+package com.beemdevelopment.aegis.ui;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AlertDialog;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.beemdevelopment.aegis.R;
+import com.beemdevelopment.aegis.helpers.FabScrollHelper;
+import com.beemdevelopment.aegis.importers.DatabaseImporter;
+import com.beemdevelopment.aegis.importers.DatabaseImporterEntryException;
+import com.beemdevelopment.aegis.importers.DatabaseImporterException;
+import com.beemdevelopment.aegis.ui.models.ImportEntry;
+import com.beemdevelopment.aegis.ui.views.ImportEntriesAdapter;
+import com.beemdevelopment.aegis.util.UUIDMap;
+import com.beemdevelopment.aegis.vault.VaultEntry;
+import com.beemdevelopment.aegis.vault.VaultManager;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import com.topjohnwu.superuser.Shell;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ImportEntriesActivity extends AegisActivity {
+ private ImportEntriesAdapter _adapter;
+ private FabScrollHelper _fabScrollHelper;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_import_entries);
+ setSupportActionBar(findViewById(R.id.toolbar));
+
+ ActionBar bar = getSupportActionBar();
+ bar.setHomeAsUpIndicator(R.drawable.ic_close);
+ bar.setDisplayHomeAsUpEnabled(true);
+
+ _adapter = new ImportEntriesAdapter();
+ RecyclerView entriesView = findViewById(R.id.list_entries);
+ entriesView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ super.onScrolled(recyclerView, dx, dy);
+ _fabScrollHelper.onScroll(dx, dy);
+ }
+ });
+
+ LinearLayoutManager layoutManager = new LinearLayoutManager(this);
+ entriesView.setLayoutManager(layoutManager);
+ entriesView.setAdapter(_adapter);
+ entriesView.setNestedScrollingEnabled(false);
+
+ FloatingActionButton fab = findViewById(R.id.fab);
+ fab.setOnClickListener(v -> {
+ if (getApp().getVaultManager().getEntries().size() > 0) {
+ showWipeEntriesDialog();
+ } else {
+ saveAndFinish(false);
+ }
+ });
+ _fabScrollHelper = new FabScrollHelper(fab);
+
+ Class extends DatabaseImporter> importerType = (Class extends DatabaseImporter>) getIntent().getSerializableExtra("importerType");
+ startImport(importerType, getIntent().getStringExtra("fileUri"));
+ }
+
+ private void startImport(@NonNull Class extends DatabaseImporter> importerType, @Nullable String fileUri) {
+ if (fileUri == null) {
+ startImportApp(importerType);
+ } else {
+ startImportFile(importerType, Uri.parse(fileUri));
+ }
+ }
+
+ private void startImportFile(@NonNull Class extends DatabaseImporter> importerType, @NonNull Uri fileUri) {
+ try (InputStream stream = getContentResolver().openInputStream(fileUri)) {
+ DatabaseImporter importer = DatabaseImporter.create(this, importerType);
+ DatabaseImporter.State state = importer.read(stream);
+ processImporterState(state);
+ } catch (FileNotFoundException e) {
+ Toast.makeText(this, R.string.file_not_found, Toast.LENGTH_SHORT).show();
+ } catch (DatabaseImporterException | IOException e) {
+ e.printStackTrace();
+ Dialogs.showErrorDialog(this, R.string.reading_file_error, e, (dialog, which) -> finish());
+ }
+ }
+
+ private void startImportApp(@NonNull Class extends DatabaseImporter> importerType) {
+ DatabaseImporter importer = DatabaseImporter.create(this, importerType);
+
+ // obtain the global root shell and close it immediately after we're done
+ // TODO: find a way to use SuFileInputStream with Shell.newInstance()
+ try (Shell shell = Shell.getShell()) {
+ if (!shell.isRoot()) {
+ Toast.makeText(this, R.string.root_error, Toast.LENGTH_SHORT).show();
+ finish();
+ return;
+ }
+
+ DatabaseImporter.State state = importer.readFromApp();
+ processImporterState(state);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ Toast.makeText(this, R.string.app_lookup_error, Toast.LENGTH_SHORT).show();
+ finish();
+ } catch (IOException | DatabaseImporterException e) {
+ e.printStackTrace();
+ Dialogs.showErrorDialog(this, R.string.reading_file_error, e, (dialog, which) -> finish());
+ }
+ }
+
+ private void processImporterState(DatabaseImporter.State state) {
+ try {
+ if (state.isEncrypted()) {
+ state.decrypt(this, new DatabaseImporter.DecryptListener() {
+ @Override
+ public void onStateDecrypted(DatabaseImporter.State state) {
+ importDatabase(state);
+ }
+
+ @Override
+ public void onError(Exception e) {
+ e.printStackTrace();
+ Dialogs.showErrorDialog(ImportEntriesActivity.this, R.string.decryption_error, e, (dialog, which) -> finish());
+ }
+
+ @Override
+ public void onCanceled() {
+ finish();
+ }
+ });
+ } else {
+ importDatabase(state);
+ }
+ } catch (DatabaseImporterException e) {
+ e.printStackTrace();
+ Dialogs.showErrorDialog(this, R.string.parsing_file_error, e, (dialog, which) -> finish());
+ }
+ }
+
+ private void importDatabase(DatabaseImporter.State state) {
+ DatabaseImporter.Result result;
+ try {
+ result = state.convert();
+ } catch (DatabaseImporterException e) {
+ e.printStackTrace();
+ Dialogs.showErrorDialog(this, R.string.parsing_file_error, e, (dialog, which) -> finish());
+ return;
+ }
+
+ UUIDMap entries = result.getEntries();
+ for (VaultEntry entry : entries.getValues()) {
+ _adapter.addEntry(new ImportEntry(entry));
+ }
+
+ List errors = result.getErrors();
+ if (errors.size() > 0) {
+ showErrorDialog(errors);
+ }
+ }
+
+ private void showErrorDialog(List errors) {
+ Dialogs.showSecureDialog(new AlertDialog.Builder(this)
+ .setTitle(R.string.import_error_title)
+ .setMessage(getResources().getQuantityString(R.plurals.import_error_dialog, errors.size(), errors.size()))
+ .setPositiveButton(android.R.string.ok, null)
+ .setNeutralButton(getString(R.string.details), (dialog, which) -> showDetailedErrorDialog(errors))
+ .create());
+ }
+
+ private void showDetailedErrorDialog(List errors) {
+ List messages = new ArrayList<>();
+ for (DatabaseImporterEntryException e : errors) {
+ messages.add(e.getMessage());
+ }
+
+ String message = TextUtils.join("\n\n", messages);
+ Dialogs.showSecureDialog(new AlertDialog.Builder(this)
+ .setTitle(R.string.import_error_title)
+ .setMessage(message)
+ .setPositiveButton(android.R.string.ok, null)
+ .setNeutralButton(android.R.string.copy, (dialog2, which2) -> {
+ ClipboardManager clipboard = (ClipboardManager) this.getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText("text/plain", message);
+ clipboard.setPrimaryClip(clip);
+ Toast.makeText(this, R.string.errors_copied, Toast.LENGTH_SHORT).show();
+ })
+ .create());
+ }
+
+ private void showWipeEntriesDialog() {
+ Dialogs.showCheckboxDialog(this, R.string.dialog_wipe_entries_title,
+ R.string.dialog_wipe_entries_message,
+ R.string.dialog_wipe_entries_checkbox,
+ this::saveAndFinish
+ );
+ }
+
+ private void saveAndFinish(boolean wipeEntries) {
+ VaultManager vault = getApp().getVaultManager();
+ if (wipeEntries) {
+ vault.wipeEntries();
+ }
+
+ List selectedEntries = _adapter.getCheckedEntries();
+ for (ImportEntry selectedEntry : selectedEntries) {
+ VaultEntry entry = selectedEntry.getEntry();
+
+ // temporary: randomize the UUID of duplicate entries and add them anyway
+ if (vault.isEntryDuplicate(entry)) {
+ entry.resetUUID();
+ }
+
+ vault.addEntry(entry);
+ }
+
+ if (saveVault(true)) {
+ String toastMessage = getResources().getQuantityString(R.plurals.imported_entries_count, selectedEntries.size(), selectedEntries.size());
+ Toast.makeText(this, toastMessage, Toast.LENGTH_SHORT).show();
+
+ setResult(RESULT_OK, null);
+ finish();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_import_entries, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ break;
+ case R.id.toggle_checkboxes:
+ _adapter.toggleCheckboxes();
+ break;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+
+ return true;
+ }
+}
diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/SelectEntriesActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/SelectEntriesActivity.java
deleted file mode 100644
index 69ea33bc..00000000
--- a/app/src/main/java/com/beemdevelopment/aegis/ui/SelectEntriesActivity.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package com.beemdevelopment.aegis.ui;
-
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.Toast;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AlertDialog;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.beemdevelopment.aegis.R;
-import com.beemdevelopment.aegis.helpers.FabScrollHelper;
-import com.beemdevelopment.aegis.importers.DatabaseImporterEntryException;
-import com.beemdevelopment.aegis.ui.models.ImportEntry;
-import com.beemdevelopment.aegis.ui.views.ImportEntriesAdapter;
-import com.google.android.material.floatingactionbutton.FloatingActionButton;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SelectEntriesActivity extends AegisActivity {
- private ImportEntriesAdapter _adapter;
- private FabScrollHelper _fabScrollHelper;
- private boolean _vaultContainsEntries;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_select_entries);
- setSupportActionBar(findViewById(R.id.toolbar));
-
- ActionBar bar = getSupportActionBar();
- bar.setHomeAsUpIndicator(R.drawable.ic_close);
- bar.setDisplayHomeAsUpEnabled(true);
-
- _adapter = new ImportEntriesAdapter();
- RecyclerView entriesView = findViewById(R.id.list_entries);
- entriesView.addOnScrollListener(new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
- super.onScrolled(recyclerView, dx, dy);
- onScroll(dx, dy);
- }
- });
-
- LinearLayoutManager layoutManager = new LinearLayoutManager(this);
- entriesView.setLayoutManager(layoutManager);
- entriesView.setAdapter(_adapter);
- entriesView.setNestedScrollingEnabled(false);
-
- Intent intent = getIntent();
- List entries = (ArrayList) intent.getSerializableExtra("entries");
- List errors = (ArrayList) intent.getSerializableExtra("errors");
- _vaultContainsEntries = intent.getBooleanExtra("vaultContainsEntries", false);
-
- for (ImportEntry entry : entries) {
- _adapter.addEntry(entry);
- }
-
- if (errors.size() > 0) {
- showErrorDialog(errors);
- }
-
- FloatingActionButton fab = findViewById(R.id.fab);
- fab.setOnClickListener(v -> {
- if (_vaultContainsEntries) {
- showWipeEntriesDialog();
- } else {
- returnSelectedEntries(false);
- }
- });
- _fabScrollHelper = new FabScrollHelper(fab);
- }
-
- private void showErrorDialog(List errors) {
- Dialogs.showSecureDialog(new AlertDialog.Builder(this)
- .setTitle(R.string.import_error_title)
- .setMessage(getResources().getQuantityString(R.plurals.import_error_dialog, errors.size(), errors.size()))
- .setPositiveButton(android.R.string.ok, null)
- .setNeutralButton(getString(R.string.details), (dialog, which) -> showDetailedErrorDialog(errors))
- .create());
- }
-
- private void showDetailedErrorDialog(List errors) {
- List messages = new ArrayList<>();
- for (DatabaseImporterEntryException e : errors) {
- messages.add(e.getMessage());
- }
-
- String message = TextUtils.join("\n\n", messages);
- Dialogs.showSecureDialog(new AlertDialog.Builder(this)
- .setTitle(R.string.import_error_title)
- .setMessage(message)
- .setPositiveButton(android.R.string.ok, null)
- .setNeutralButton(android.R.string.copy, (dialog2, which2) -> {
- ClipboardManager clipboard = (ClipboardManager) this.getSystemService(Context.CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText("text/plain", message);
- clipboard.setPrimaryClip(clip);
- Toast.makeText(this, R.string.errors_copied, Toast.LENGTH_SHORT).show();
- })
- .create());
- }
-
- private void showWipeEntriesDialog() {
- Dialogs.showCheckboxDialog(this, R.string.dialog_wipe_entries_title,
- R.string.dialog_wipe_entries_message,
- R.string.dialog_wipe_entries_checkbox,
- this::returnSelectedEntries
- );
- }
-
- private void returnSelectedEntries(boolean wipeEntries) {
- Intent intent = new Intent();
- List entries = _adapter.getCheckedEntries();
- intent.putExtra("entries", (ArrayList) entries);
- intent.putExtra("wipeEntries", wipeEntries);
- setResult(RESULT_OK, intent);
- finish();
- }
-
- public void onScroll(int dx, int dy) {
- _fabScrollHelper.onScroll(dx, dy);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_select_entries, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- break;
- case R.id.toggle_checkboxes:
- _adapter.toggleCheckboxes();
- break;
- default:
- return super.onOptionsItemSelected(item);
- }
-
- return true;
- }
-}
diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/ImportExportPreferencesFragment.java b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/ImportExportPreferencesFragment.java
index 9330b3b8..ce916cf4 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/ImportExportPreferencesFragment.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/ImportExportPreferencesFragment.java
@@ -2,7 +2,6 @@ package com.beemdevelopment.aegis.ui.fragments;
import android.app.Activity;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
@@ -14,6 +13,7 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.ArrayRes;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.FileProvider;
import androidx.preference.Preference;
@@ -21,47 +21,37 @@ import androidx.preference.Preference;
import com.beemdevelopment.aegis.BuildConfig;
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.helpers.DropdownHelper;
-import com.beemdevelopment.aegis.importers.AegisImporter;
import com.beemdevelopment.aegis.importers.DatabaseImporter;
-import com.beemdevelopment.aegis.importers.DatabaseImporterEntryException;
-import com.beemdevelopment.aegis.importers.DatabaseImporterException;
-import com.beemdevelopment.aegis.ui.AuthActivity;
import com.beemdevelopment.aegis.ui.Dialogs;
-import com.beemdevelopment.aegis.ui.SelectEntriesActivity;
-import com.beemdevelopment.aegis.ui.models.ImportEntry;
+import com.beemdevelopment.aegis.ui.ImportEntriesActivity;
import com.beemdevelopment.aegis.ui.tasks.ExportTask;
-import com.beemdevelopment.aegis.util.UUIDMap;
import com.beemdevelopment.aegis.vault.VaultBackupManager;
-import com.beemdevelopment.aegis.vault.VaultEntry;
import com.beemdevelopment.aegis.vault.VaultFileCredentials;
import com.beemdevelopment.aegis.vault.VaultManager;
import com.beemdevelopment.aegis.vault.VaultManagerException;
import com.beemdevelopment.aegis.vault.slots.Slot;
import com.beemdevelopment.aegis.vault.slots.SlotException;
-import com.topjohnwu.superuser.Shell;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
import javax.crypto.Cipher;
public class ImportExportPreferencesFragment extends PreferencesFragment {
- // keep a reference to the type of database converter the user selected
+ // keep a reference to the type of database converter that was selected
private Class extends DatabaseImporter> _importerType;
- private AegisImporter.State _importerState;
- private UUIDMap _importerEntries;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey);
addPreferencesFromResource(R.xml.preferences_import_export);
+ if (savedInstanceState != null) {
+ _importerType = (Class extends DatabaseImporter>) savedInstanceState.getSerializable("importerType");
+ }
+
Preference importPreference = findPreference("pref_import");
importPreference.setOnPreferenceClickListener(preference -> {
Dialogs.showImportersDialog(getContext(), false, definition -> {
@@ -69,7 +59,7 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
- startActivityForResult(intent, CODE_IMPORT);
+ startActivityForResult(intent, CODE_IMPORT_SELECT);
});
return true;
});
@@ -77,8 +67,7 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
Preference importAppPreference = findPreference("pref_import_app");
importAppPreference.setOnPreferenceClickListener(preference -> {
Dialogs.showImportersDialog(getContext(), true, definition -> {
- DatabaseImporter importer = DatabaseImporter.create(getContext(), definition.getType());
- importApp(importer);
+ startImportEntriesActivity(definition.getType(), null);
});
return true;
});
@@ -90,18 +79,20 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
});
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putSerializable("importerType", _importerType);
+ }
+
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (data != null) {
+ if (requestCode == CODE_IMPORT) {
+ getResult().putExtra("needsRecreate", true);
+ } else if (data != null) {
switch (requestCode) {
- case CODE_IMPORT:
- onImportResult(resultCode, data);
- break;
- case CODE_IMPORT_DECRYPT:
- onImportDecryptResult(resultCode, data);
- break;
- case CODE_SELECT_ENTRIES:
- onSelectEntriesResult(resultCode, data);
+ case CODE_IMPORT_SELECT:
+ onImportSelectResult(resultCode, data);
break;
case CODE_EXPORT:
// intentional fallthrough
@@ -114,118 +105,20 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
}
}
- private void importApp(DatabaseImporter importer) {
- // obtain the global root shell and close it immediately after we're done
- // TODO: find a way to use SuFileInputStream with Shell.newInstance()
- try (Shell shell = Shell.getShell()) {
- if (!shell.isRoot()) {
- Toast.makeText(getActivity(), R.string.root_error, Toast.LENGTH_SHORT).show();
- return;
- }
-
- DatabaseImporter.State state = importer.readFromApp();
- processImporterState(state);
- } catch (PackageManager.NameNotFoundException e) {
- e.printStackTrace();
- Toast.makeText(getActivity(), R.string.app_lookup_error, Toast.LENGTH_SHORT).show();
- } catch (IOException | DatabaseImporterException e) {
- e.printStackTrace();
- Toast.makeText(getActivity(), R.string.reading_file_error, Toast.LENGTH_SHORT).show();
- }
- }
-
- private void processImporterState(DatabaseImporter.State state) {
- try {
- if (state.isEncrypted()) {
- // temporary special case for encrypted Aegis vaults
- if (state instanceof AegisImporter.EncryptedState) {
- _importerState = state;
-
- Intent intent = new Intent(getActivity(), AuthActivity.class);
- intent.putExtra("slots", ((AegisImporter.EncryptedState) state).getSlots());
- startActivityForResult(intent, CODE_IMPORT_DECRYPT);
- } else {
- state.decrypt(getActivity(), new DatabaseImporter.DecryptListener() {
- @Override
- public void onStateDecrypted(DatabaseImporter.State state) {
- importDatabase(state);
- }
-
- @Override
- public void onError(Exception e) {
- e.printStackTrace();
- Dialogs.showErrorDialog(getContext(), R.string.decryption_error, e);
- }
- });
- }
- } else {
- importDatabase(state);
- }
- } catch (DatabaseImporterException e) {
- e.printStackTrace();
- Dialogs.showErrorDialog(getContext(), R.string.parsing_file_error, e);
- }
- }
-
- private void onImportDecryptResult(int resultCode, Intent data) {
- if (resultCode != Activity.RESULT_OK) {
- _importerState = null;
- return;
- }
-
- VaultFileCredentials creds = (VaultFileCredentials) data.getSerializableExtra("creds");
- DatabaseImporter.State state;
- try {
- state = ((AegisImporter.EncryptedState) _importerState).decrypt(creds);
- } catch (DatabaseImporterException e) {
- e.printStackTrace();
- Dialogs.showErrorDialog(getContext(), R.string.decryption_error, e);
- return;
- }
-
- importDatabase(state);
- _importerState = null;
- }
-
- private void onImportResult(int resultCode, Intent data) {
+ private void onImportSelectResult(int resultCode, Intent data) {
Uri uri = data.getData();
if (resultCode != Activity.RESULT_OK || uri == null) {
return;
}
- try (InputStream stream = getContext().getContentResolver().openInputStream(uri)) {
- DatabaseImporter importer = DatabaseImporter.create(getContext(), _importerType);
- DatabaseImporter.State state = importer.read(stream);
- processImporterState(state);
- } catch (FileNotFoundException e) {
- Toast.makeText(getActivity(), R.string.file_not_found, Toast.LENGTH_SHORT).show();
- } catch (DatabaseImporterException | IOException e) {
- e.printStackTrace();
- Dialogs.showErrorDialog(getContext(), R.string.reading_file_error, e);
- }
+ startImportEntriesActivity(_importerType, uri);
}
- private void importDatabase(DatabaseImporter.State state) {
- DatabaseImporter.Result result;
- try {
- result = state.convert();
- } catch (DatabaseImporterException e) {
- e.printStackTrace();
- Dialogs.showErrorDialog(getContext(), R.string.parsing_file_error, e);
- return;
- }
-
- _importerEntries = result.getEntries();
- List entries = new ArrayList<>();
- for (VaultEntry entry : _importerEntries) {
- entries.add(new ImportEntry(entry));
- }
-
- Intent intent = new Intent(getActivity(), SelectEntriesActivity.class);
- intent.putExtra("entries", (ArrayList) entries);
- intent.putExtra("errors", (ArrayList) result.getErrors());
- intent.putExtra("vaultContainsEntries", getVault().getEntries().size() > 0);
- startActivityForResult(intent, CODE_SELECT_ENTRIES);
+ private void startImportEntriesActivity(Class extends DatabaseImporter> importerType, Uri fileUri) {
+ Intent intent = new Intent(getActivity(), ImportEntriesActivity.class);
+ intent.putExtra("importerType", importerType);
+ intent.putExtra("fileUri", fileUri == null ? null : fileUri.toString());
+ startActivityForResult(intent, CODE_IMPORT);
}
private void startExport() {
@@ -326,39 +219,6 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
Dialogs.showSecureDialog(dialog);
}
- private void onSelectEntriesResult(int resultCode, Intent data) {
- if (resultCode != Activity.RESULT_OK) {
- return;
- }
-
- boolean wipeEntries = data.getBooleanExtra("wipeEntries", false);
- if (wipeEntries) {
- getVault().wipeEntries();
- }
-
- List selectedEntries = (ArrayList) data.getSerializableExtra("entries");
- for (ImportEntry selectedEntry : selectedEntries) {
- VaultEntry savedEntry = _importerEntries.getByUUID(selectedEntry.getUUID());
-
- // temporary: randomize the UUID of duplicate entries and add them anyway
- if (getVault().isEntryDuplicate(savedEntry)) {
- savedEntry.resetUUID();
- }
-
- getVault().addEntry(savedEntry);
- }
-
- _importerEntries = null;
- if (!saveVault()) {
- return;
- }
-
- String toastMessage = getResources().getQuantityString(R.plurals.imported_entries_count, selectedEntries.size(), selectedEntries.size());
- Toast.makeText(getContext(), toastMessage, Toast.LENGTH_SHORT).show();
-
- getResult().putExtra("needsRecreate", true);
- }
-
private static int getExportRequestCode(int spinnerPos, boolean encrypt) {
if (spinnerPos == 0) {
return encrypt ? CODE_EXPORT : CODE_EXPORT_PLAIN;
@@ -433,7 +293,6 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
return;
}
-
startExportVault(requestCode, cb -> {
File file;
OutputStream outStream = null;
diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/PreferencesFragment.java b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/PreferencesFragment.java
index c79e4aab..e8ce9777 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/PreferencesFragment.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/PreferencesFragment.java
@@ -16,11 +16,10 @@ import com.beemdevelopment.aegis.vault.VaultManagerException;
public abstract class PreferencesFragment extends PreferenceFragmentCompat {
// activity request codes
- public static final int CODE_IMPORT = 0;
- public static final int CODE_IMPORT_DECRYPT = 1;
+ public static final int CODE_IMPORT_SELECT = 0;
public static final int CODE_SLOTS = 2;
public static final int CODE_GROUPS = 3;
- public static final int CODE_SELECT_ENTRIES = 4;
+ public static final int CODE_IMPORT = 4;
public static final int CODE_EXPORT = 5;
public static final int CODE_EXPORT_PLAIN = 6;
public static final int CODE_EXPORT_GOOGLE_URI = 7;
diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/models/ImportEntry.java b/app/src/main/java/com/beemdevelopment/aegis/ui/models/ImportEntry.java
index 507039c0..be40d86a 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/ui/models/ImportEntry.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/ui/models/ImportEntry.java
@@ -3,32 +3,19 @@ package com.beemdevelopment.aegis.ui.models;
import com.beemdevelopment.aegis.vault.VaultEntry;
import java.io.Serializable;
-import java.util.UUID;
public class ImportEntry implements Serializable {
- private UUID _uuid;
- private String _name;
- private String _issuer;
+ private final VaultEntry _entry;
private transient Listener _listener;
private boolean _isChecked = true;
public ImportEntry(VaultEntry entry) {
- _uuid = entry.getUUID();
- _name = entry.getName();
- _issuer = entry.getIssuer();
+ _entry = entry;
}
- public UUID getUUID() {
- return _uuid;
- }
-
- public String getName() {
- return _name;
- }
-
- public String getIssuer() {
- return _issuer;
+ public VaultEntry getEntry() {
+ return _entry;
}
public void setOnCheckedChangedListener(Listener listener) {
diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/ImportEntriesAdapter.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/ImportEntriesAdapter.java
index cc64a799..024472b1 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/ImportEntriesAdapter.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/ImportEntriesAdapter.java
@@ -4,15 +4,15 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.ui.models.ImportEntry;
import java.util.ArrayList;
import java.util.List;
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
public class ImportEntriesAdapter extends RecyclerView.Adapter {
private List _entries;
@@ -47,7 +47,7 @@ public class ImportEntriesAdapter extends RecyclerView.Adapter _entry.setIsChecked(!_entry.isChecked()));
+ view.setOnClickListener(v -> _data.setIsChecked(!_data.isChecked()));
}
- public void setData(ImportEntry entry) {
- _entry = entry;
+ public void setData(ImportEntry data) {
+ _data = data;
Context context = itemView.getContext();
- _issuer.setText(!entry.getIssuer().isEmpty() ? entry.getIssuer() : context.getString(R.string.unknown_issuer));
- _accountName.setText(!entry.getName().isEmpty() ? entry.getName() : context.getString(R.string.unknown_account_name));
- _checkbox.setChecked(entry.isChecked());
+ _issuer.setText(!_data.getEntry().getIssuer().isEmpty() ? _data.getEntry().getIssuer() : context.getString(R.string.unknown_issuer));
+ _accountName.setText(!_data.getEntry().getName().isEmpty() ? _data.getEntry().getName() : context.getString(R.string.unknown_account_name));
+ _checkbox.setChecked(_data.isChecked());
}
- public ImportEntry getEntry() {
- return _entry;
+ public ImportEntry getData() {
+ return _data;
}
@Override
diff --git a/app/src/main/res/layout/activity_select_entries.xml b/app/src/main/res/layout/activity_import_entries.xml
similarity index 96%
rename from app/src/main/res/layout/activity_select_entries.xml
rename to app/src/main/res/layout/activity_import_entries.xml
index e687dfe9..79f47a72 100644
--- a/app/src/main/res/layout/activity_select_entries.xml
+++ b/app/src/main/res/layout/activity_import_entries.xml
@@ -6,7 +6,7 @@
android:layout_height="match_parent"
android:background="?attr/background"
android:id="@+id/importEntriesRootView"
- tools:context="com.beemdevelopment.aegis.ui.SelectEntriesActivity">
+ tools:context="com.beemdevelopment.aegis.ui.ImportEntriesActivity">
-
+ android:orientation="horizontal">
-
-
+
-
-
-
-
-
-
-
-
-
+ android:fontFamily="sans-serif-light"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?attr/primaryText"
+ android:textSize="18sp"
+ android:textStyle="normal|bold"
+ android:gravity="center_vertical"
+ tools:text="@string/issuer" />
+
-
+
diff --git a/app/src/main/res/menu/menu_select_entries.xml b/app/src/main/res/menu/menu_import_entries.xml
similarity index 100%
rename from app/src/main/res/menu/menu_select_entries.xml
rename to app/src/main/res/menu/menu_import_entries.xml
diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml
index 833ee490..050fe3ec 100644
--- a/app/src/main/res/values-cs-rCZ/strings.xml
+++ b/app/src/main/res/values-cs-rCZ/strings.xml
@@ -296,7 +296,7 @@
Upravit položku
Naskenovat QR kód
Spravovat sloty klíčů
- Vybrat položky
+ Vybrat položky
Vyčistit položky
Váš trezor už obsahuje záznamy. Chcete tyto záznamy smazat před importem tohoto souboru?\n\nPřístup ke všem stávajícím položkám v trezoru trvale ztratíte.
Vymazat obsah trezoru
diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml
index cecab5d2..e74ba250 100644
--- a/app/src/main/res/values-de-rDE/strings.xml
+++ b/app/src/main/res/values-de-rDE/strings.xml
@@ -292,7 +292,7 @@
Eintrag bearbeiten
Scanne einen QR-Code
Schlüsselplätze verwalten
- Einträge auswählen
+ Einträge auswählen
Einträge löschen
Deine Datenbank enthält bereits Einträge. Möchtest du diese Einträge entfernen, bevor diese Datei importiert wird?\n\nWenn du dies tust, verlierst du dauerhaft den Zugriff auf die vorhandenen Einträge in der Datenbank.
Datenbankinhalt löschen
diff --git a/app/src/main/res/values-el-rGR/strings.xml b/app/src/main/res/values-el-rGR/strings.xml
index 65aee027..8ca68070 100644
--- a/app/src/main/res/values-el-rGR/strings.xml
+++ b/app/src/main/res/values-el-rGR/strings.xml
@@ -292,7 +292,7 @@
Επεξεργασία καταχώρησης
Σάρωση κωδικού QR
Διαχείριση κλειδιών υποδοχής
- Επιλέξτε καταχωρήσεις
+ Επιλέξτε καταχωρήσεις
Διαγραφή καταχωρήσεων
Η κρύπτη σας περιέχει ήδη καταχωρήσεις. Θέλετε να καταργήσετε αυτές τις καταχωρήσεις πριν από την εισαγωγή αυτού του αρχείου;\n\n Με αυτόν τον τρόπο, θα χάσετε μόνιμα την πρόσβαση στις υπάρχουσες καταχωρήσεις στην κρύπτη.
Διαγραφή περιεχομένων κρύπτης
diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml
index 7f95ff5a..0c34d96d 100644
--- a/app/src/main/res/values-es-rES/strings.xml
+++ b/app/src/main/res/values-es-rES/strings.xml
@@ -263,7 +263,7 @@
Acerca de
Editar entrada
Escanear un código QR
- Seleccionar entradas
+ Seleccionar entradas
Limpiar entradas
Su caja fuerte ya contiene entradas. ¿Desea eliminarlas antes de importar este archivo?\n\nAl hacerlo, perderá permanentemente el acceso a las entradas existentes en la caja fuerte.
Limpiar el contenido de la caja fuerte
diff --git a/app/src/main/res/values-eu-rES/strings.xml b/app/src/main/res/values-eu-rES/strings.xml
index 094efa2c..97d80b61 100644
--- a/app/src/main/res/values-eu-rES/strings.xml
+++ b/app/src/main/res/values-eu-rES/strings.xml
@@ -286,7 +286,7 @@
Editatu sarrera
Eskaneatu QR kodea
Kudeatu arteka giltzarriak
- Aukeratu sarrerak
+ Aukeratu sarrerak
Garbitu sarrerak
Zure biltegiak sarrerak ditu dagoeneko. Sarrera horiek ezabatu nahi dituzu fitxategi hau inportatu aurretik?\n\nHori egitean, biltegiko sarreretarako sarbidea galduko da betirako.
Garbitu biltegiaren sarrerak
diff --git a/app/src/main/res/values-fa-rIR/strings.xml b/app/src/main/res/values-fa-rIR/strings.xml
index ec83dcdc..21cfa81a 100644
--- a/app/src/main/res/values-fa-rIR/strings.xml
+++ b/app/src/main/res/values-fa-rIR/strings.xml
@@ -288,7 +288,7 @@
ویرایش آیتم
اسکن بارکد دوبعدی
مدیریت کلیدها
- انتخاب آیتمها
+ انتخاب آیتمها
پاک کردن موارد
مخزن شما در حال حاضر شامل آیتم میباشد. آیا مطمئن به حذف این آیتمها قبل از وارد کردن میباشید؟\n\nبعد از این کار شما به هیچ وجه دسترسی به آیتم های قبلی نخواهید داشت.
پاک کردن اطلاعات مخزن
diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml
index 472be8cc..74961762 100644
--- a/app/src/main/res/values-fi-rFI/strings.xml
+++ b/app/src/main/res/values-fi-rFI/strings.xml
@@ -288,7 +288,7 @@
Muokkaa kohdetta
Skannaa QR-koodi
Hallitse avainpaikkoja
- Valitse kohteet
+ Valitse kohteet
Tyhjennä kohteet
Holvisi sisältää jo kohteita. Haluatko poistaa nämä kohteet ennen tämän tiedoston tuontia?\n\nTehdessäsi näin menetät pysyvästi pääsyn holvin olemassa oleviin kohteisiin.
Tyhjennä holvin sisältö
diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml
index 48b127a7..7b19d332 100644
--- a/app/src/main/res/values-fr-rFR/strings.xml
+++ b/app/src/main/res/values-fr-rFR/strings.xml
@@ -292,7 +292,7 @@
Modifier l\'entrée
Scanner un code QR
Gérer les emplacements de clés
- Sélectionner les entrées
+ Sélectionner les entrées
Effacer les entrées
Votre coffre-fort contient déjà des entrées. Voulez-vous supprimer ces entrées avant d\'importer ce fichier ?\n\nEn faisant cela, vous perdrez définitivement l\'accès aux entrées existantes dans le coffre-fort.
Effacer le contenu du coffre-fort
diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml
index bef7a4a7..2c90abfd 100644
--- a/app/src/main/res/values-hi-rIN/strings.xml
+++ b/app/src/main/res/values-hi-rIN/strings.xml
@@ -283,7 +283,7 @@
प्रविष्टि संपादित करें
क्यूआर कोड स्कैन करें
कुंजी स्लॉट प्रबंधन करें
- प्रविष्टियाँ चयन करें
+ प्रविष्टियाँ चयन करें
प्रविष्टियों को साफ करें
आपके वॉल्ट में पहले से ही प्रविष्टियाँ हैं। क्या आप इस फ़ाइल को आयात करने से पहले इन प्रविष्टियों को निकालना चाहते हैं?\n\n ऐसा करने पर, आप स्थायी रूप से वॉल्ट में मौजूदा प्रविष्टियों तक पहुंच खो देंगे।
वॉल्ट सामग्री साफ करें
diff --git a/app/src/main/res/values-in-rID/strings.xml b/app/src/main/res/values-in-rID/strings.xml
index 36916936..5ad210f7 100644
--- a/app/src/main/res/values-in-rID/strings.xml
+++ b/app/src/main/res/values-in-rID/strings.xml
@@ -284,7 +284,7 @@
Sunting catatan
Pindai kode QR
Atur slot kunci
- Pilih catatan
+ Pilih catatan
Hapus catatan
Brankas Anda sudah memiliki catatan. Apakah Anda ingin menghapus catatan ini tanpa mengimpor berkas ini?\n\nJika ini dilakukan, Anda akan kehilangan akses secara permanen ke catatan yang ada di brankas.
Hapus konten brankas
diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml
index 69778ff3..35802679 100644
--- a/app/src/main/res/values-it-rIT/strings.xml
+++ b/app/src/main/res/values-it-rIT/strings.xml
@@ -292,7 +292,7 @@
Modifica voce
Scansiona un codice QR
Gestisci slot di chiavi
- Seleziona voci
+ Seleziona voci
Cancella voci
La tua cassaforte contiene già delle voci. Rimuovere queste voci prima di importare questo file?\n\nIn questo modo, perderai definitivamente l\'accesso alle voci esistenti nella cassaforte.
Cancella contenuto della cassaforte
diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml
index a35e1f23..9fe70bee 100644
--- a/app/src/main/res/values-ja-rJP/strings.xml
+++ b/app/src/main/res/values-ja-rJP/strings.xml
@@ -284,7 +284,7 @@
エントリーを編集
QRコードをスキャン
キースロットの管理
- 項目を選択
+ 項目を選択
項目を消去
保管庫の内容を消去
イージスはパニックトリガーを受信しましたが、設定は無効になっています。
diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml
index 8ad9d7cd..770812fe 100644
--- a/app/src/main/res/values-nl-rNL/strings.xml
+++ b/app/src/main/res/values-nl-rNL/strings.xml
@@ -292,7 +292,7 @@
Item bewerken
Scan een QR-code
Beheer sleutelslots
- Geselecteerde items
+ Geselecteerde items
Items verwijderen
Je kluis bevat al items. Wilt u deze invoergegevens verwijderen voordat u dit bestand importeert?\n\nHierdoor verliest u permanent toegang tot de bestaande items in de kluis.
Kluis inhoud wissen
diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml
index fede510e..1cffa135 100644
--- a/app/src/main/res/values-pl-rPL/strings.xml
+++ b/app/src/main/res/values-pl-rPL/strings.xml
@@ -304,7 +304,7 @@
Edytuj wpis
Skanuj kod QR
Zarządzaj slotami klucza
- Zaznacz wpisy
+ Zaznacz wpisy
Wyczyść wpisy
Twój sejf zawiera już wpisy. Czy chcesz usunąć te wpisy przed zaimportowaniem tego pliku?\n\nW ten sposób trwale stracisz dostęp do obecnych wpisów w sejfie.
Wyczyść zawartość sejfu
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 0e9c2aa9..513e0877 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -292,7 +292,7 @@
Editar entrada
Escanear QR code
Gerenciar slots de chaves
- Selecionar entradas
+ Selecionar entradas
Limpar entradas
O seu cofre já contém entradas. Você quer remover essas entradas antes de importar este arquivo?\n\nAo fazer isso, você perderá permanentemente o acesso às entradas existentes no cofre.
Limpar conteúdo do cofre
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index 858470f9..be0d1ce3 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -292,7 +292,7 @@
Editar entrada
Escanear um código QR
Gerenciar slots de chaves
- Selecionar entradas
+ Selecionar entradas
Limpar entradas
O seu cofre já contém entradas. Você quer remover essas entradas antes de importar este arquivo?\n\nAo fazer isso, você perderá permanentemente o acesso às entradas existentes no cofre.
Limpar conteúdo do cofre
diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml
index f4d58306..ccf85f42 100644
--- a/app/src/main/res/values-ro-rRO/strings.xml
+++ b/app/src/main/res/values-ro-rRO/strings.xml
@@ -296,7 +296,7 @@
Editați intrarea
Scanează cod QR
Gestionare sloturi cheie
- Selectați intrările
+ Selectați intrările
Ştergeţi intrările
Seiful dvs. conține deja intrări. Doriţi să ştergeţi aceste intrări înainte de a importa acest fişier?\n\nFăcând acest lucru, vei pierde permanent accesul la intrările existente din seif.
Ştergeţi conţinutul seifului
diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml
index df98b467..0426ec51 100644
--- a/app/src/main/res/values-ru-rRU/strings.xml
+++ b/app/src/main/res/values-ru-rRU/strings.xml
@@ -304,7 +304,7 @@
Редактировать запись
Сканировать QR-код
Управление ключевыми слотами
- Выбрать записи
+ Выбрать записи
Очистить записи
В вашем хранилище уже есть записи. Вы хотите удалить эти записи перед импортом файла?\n\nПри этом вы навсегда потеряете доступ к существующим записям в хранилище.
Очистить содержимое хранилища
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 06a79493..f96b8d76 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -286,7 +286,7 @@
编辑项目
扫描二维码
管理密钥槽位
- 选择项目
+ 选择项目
擦除条目
您的密码库已经包含条目。 您想要在导入此文件之前删除这些条目吗?\n\n这样做,您将永久失去对密码库中现有条目的访问权限。
擦除密码库内容
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index e45deed4..ee2f2b11 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -276,7 +276,7 @@
編輯條目
掃描QR碼
管理密鎖槽位
- 選擇條目
+ 選擇條目
清除條目
您的數據庫已包含某些條目,您是否想在匯入這個檔案前,先移除這些重複的條目?\n\n如果繼續,您將會失去之前擁有的重複條目。
清除條目的內容
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4fb376a7..8119d063 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -344,7 +344,7 @@
Edit entry
Scan a QR code
Manage key slots
- Select entries
+ Import entries
Wipe entries
Your vault already contains entries. Do you want to remove these entries before importing this file?\n\nIn doing so, you will permanently lose access to the existing entries in the vault.
Wipe vault contents