2019-02-07 22:39:33 +01:00
|
|
|
package com.beemdevelopment.aegis.ui;
|
2018-05-10 14:54:58 +02:00
|
|
|
|
|
|
|
import android.Manifest;
|
|
|
|
import android.app.Activity;
|
2018-05-10 19:34:42 +02:00
|
|
|
import android.content.DialogInterface;
|
2018-05-10 14:54:58 +02:00
|
|
|
import android.content.Intent;
|
2018-05-10 19:34:42 +02:00
|
|
|
import android.media.MediaScannerConnection;
|
2018-05-10 18:42:47 +02:00
|
|
|
import android.net.Uri;
|
2018-05-10 14:54:58 +02:00
|
|
|
import android.os.Bundle;
|
2018-09-25 16:26:57 +02:00
|
|
|
import androidx.appcompat.app.AlertDialog;
|
|
|
|
import androidx.preference.Preference;
|
2019-03-28 00:56:29 +01:00
|
|
|
|
|
|
|
import android.view.Gravity;
|
|
|
|
import android.view.View;
|
2018-05-11 19:33:20 +02:00
|
|
|
import android.view.Window;
|
|
|
|
import android.view.WindowManager;
|
2019-03-28 00:56:29 +01:00
|
|
|
import android.widget.AdapterView;
|
2018-05-10 14:54:58 +02:00
|
|
|
import android.widget.Toast;
|
|
|
|
|
2019-03-26 00:06:39 +01:00
|
|
|
import com.beemdevelopment.aegis.Preferences;
|
2019-03-28 00:56:29 +01:00
|
|
|
import com.beemdevelopment.aegis.Theme;
|
2019-02-07 22:39:33 +01:00
|
|
|
import com.beemdevelopment.aegis.db.DatabaseFileCredentials;
|
|
|
|
import com.beemdevelopment.aegis.helpers.FingerprintHelper;
|
|
|
|
import com.beemdevelopment.aegis.helpers.PermissionHelper;
|
2019-03-23 20:48:02 +01:00
|
|
|
import com.beemdevelopment.aegis.importers.AegisFileImporter;
|
|
|
|
import com.beemdevelopment.aegis.importers.DatabaseAppImporter;
|
|
|
|
import com.beemdevelopment.aegis.importers.DatabaseFileImporter;
|
2019-02-07 22:39:33 +01:00
|
|
|
import com.beemdevelopment.aegis.importers.DatabaseImporter;
|
|
|
|
import com.beemdevelopment.aegis.importers.DatabaseImporterException;
|
|
|
|
import com.beemdevelopment.aegis.ui.preferences.SwitchPreference;
|
|
|
|
import com.beemdevelopment.aegis.util.ByteInputStream;
|
2018-10-02 21:05:54 +02:00
|
|
|
import com.takisoft.preferencex.PreferenceFragmentCompat;
|
|
|
|
|
2018-05-10 14:54:58 +02:00
|
|
|
import java.io.FileNotFoundException;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
2018-12-18 22:46:35 +01:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.HashSet;
|
2018-05-10 14:54:58 +02:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.Locale;
|
2018-05-13 21:58:41 +02:00
|
|
|
import java.util.Map;
|
2018-11-15 21:36:10 +01:00
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
2018-05-10 14:54:58 +02:00
|
|
|
|
2018-05-14 16:53:27 +02:00
|
|
|
import javax.crypto.Cipher;
|
|
|
|
|
2019-02-07 22:39:33 +01:00
|
|
|
import com.beemdevelopment.aegis.AegisApplication;
|
|
|
|
import com.beemdevelopment.aegis.BuildConfig;
|
|
|
|
import com.beemdevelopment.aegis.R;
|
|
|
|
import com.beemdevelopment.aegis.db.DatabaseEntry;
|
|
|
|
import com.beemdevelopment.aegis.db.DatabaseManager;
|
|
|
|
import com.beemdevelopment.aegis.db.DatabaseManagerException;
|
|
|
|
import com.beemdevelopment.aegis.db.slots.FingerprintSlot;
|
|
|
|
import com.beemdevelopment.aegis.db.slots.PasswordSlot;
|
|
|
|
import com.beemdevelopment.aegis.db.slots.Slot;
|
|
|
|
import com.beemdevelopment.aegis.db.slots.SlotException;
|
|
|
|
import com.beemdevelopment.aegis.db.slots.SlotList;
|
2019-03-23 20:48:02 +01:00
|
|
|
import com.topjohnwu.superuser.Shell;
|
2018-05-10 14:54:58 +02:00
|
|
|
|
2018-11-15 21:23:18 +01:00
|
|
|
public class PreferencesFragment extends PreferenceFragmentCompat {
|
2018-05-10 14:54:58 +02:00
|
|
|
// activity request codes
|
|
|
|
private static final int CODE_IMPORT = 0;
|
2018-05-10 18:42:47 +02:00
|
|
|
private static final int CODE_IMPORT_DECRYPT = 1;
|
2018-05-10 21:46:27 +02:00
|
|
|
private static final int CODE_SLOTS = 2;
|
2018-12-18 22:46:35 +01:00
|
|
|
private static final int CODE_GROUPS = 3;
|
2018-05-10 14:54:58 +02:00
|
|
|
|
|
|
|
// permission request codes
|
|
|
|
private static final int CODE_PERM_IMPORT = 0;
|
2018-05-10 19:34:42 +02:00
|
|
|
private static final int CODE_PERM_EXPORT = 1;
|
2018-05-10 14:54:58 +02:00
|
|
|
|
2018-05-11 21:30:15 +02:00
|
|
|
private Intent _result;
|
2018-05-10 14:54:58 +02:00
|
|
|
private DatabaseManager _db;
|
|
|
|
|
2018-05-10 18:42:47 +02:00
|
|
|
// this is used to keep a reference to a database converter
|
|
|
|
// while the user provides credentials to decrypt it
|
2019-03-23 20:48:02 +01:00
|
|
|
private DatabaseFileImporter _importer;
|
|
|
|
private Class<? extends DatabaseFileImporter> _importerType;
|
2018-05-10 18:42:47 +02:00
|
|
|
|
2018-05-14 16:53:27 +02:00
|
|
|
private SwitchPreference _encryptionPreference;
|
2018-11-15 21:23:18 +01:00
|
|
|
private SwitchPreference _fingerprintPreference;
|
|
|
|
private Preference _setPasswordPreference;
|
2018-05-14 16:53:27 +02:00
|
|
|
private Preference _slotsPreference;
|
2018-12-16 22:57:04 +01:00
|
|
|
private Preference _groupsPreference;
|
2018-05-14 16:53:27 +02:00
|
|
|
|
2018-05-10 14:54:58 +02:00
|
|
|
@Override
|
2018-10-02 21:05:54 +02:00
|
|
|
public void onCreatePreferencesFix(Bundle savedInstanceState, String rootKey) {
|
2018-05-10 14:54:58 +02:00
|
|
|
addPreferencesFromResource(R.xml.preferences);
|
2018-05-10 18:42:47 +02:00
|
|
|
|
|
|
|
AegisApplication app = (AegisApplication) getActivity().getApplication();
|
|
|
|
_db = app.getDatabaseManager();
|
2018-05-10 14:54:58 +02:00
|
|
|
|
|
|
|
// set the result intent in advance
|
2018-05-11 21:30:15 +02:00
|
|
|
setResult(new Intent());
|
2018-05-10 14:54:58 +02:00
|
|
|
|
2019-03-28 01:27:07 +01:00
|
|
|
int currentTheme = app.getPreferences().getCurrentTheme();
|
2018-05-10 23:30:38 +02:00
|
|
|
Preference darkModePreference = findPreference("pref_dark_mode");
|
2019-03-28 01:27:07 +01:00
|
|
|
darkModePreference.setSummary("Selected: " + Theme.getThemeName(currentTheme));
|
2019-03-28 00:56:29 +01:00
|
|
|
darkModePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onPreferenceClick(Preference preference) {
|
2019-03-28 01:27:07 +01:00
|
|
|
String[] themeNames = Theme.getThemeNames();
|
2019-03-28 00:56:29 +01:00
|
|
|
int checkedTheme = app.getPreferences().getCurrentTheme();
|
|
|
|
|
|
|
|
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
|
|
|
.setTitle(getString(R.string.choose_theme))
|
|
|
|
.setSingleChoiceItems(themeNames, checkedTheme, (dialog, which) -> {
|
|
|
|
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
2019-03-28 01:27:07 +01:00
|
|
|
app.getPreferences().setCurrentTheme(Theme.fromInteger(i));
|
2019-03-28 00:56:29 +01:00
|
|
|
|
2019-03-28 13:01:14 +01:00
|
|
|
dialog.dismiss();
|
|
|
|
|
2019-03-28 00:56:29 +01:00
|
|
|
_result.putExtra("needsRecreate", true);
|
|
|
|
getActivity().recreate();
|
|
|
|
})
|
|
|
|
.setPositiveButton(android.R.string.ok, null)
|
|
|
|
.create());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-05-10 23:30:38 +02:00
|
|
|
darkModePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
2018-05-10 14:54:58 +02:00
|
|
|
@Override
|
|
|
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
2018-05-11 19:33:20 +02:00
|
|
|
_result.putExtra("needsRecreate", true);
|
2018-05-11 21:30:15 +02:00
|
|
|
getActivity().recreate();
|
2018-05-10 14:54:58 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-03-23 20:48:02 +01:00
|
|
|
Preference importPreference = findPreference("pref_import");
|
|
|
|
importPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
2018-05-10 14:54:58 +02:00
|
|
|
@Override
|
|
|
|
public boolean onPreferenceClick(Preference preference) {
|
2018-05-10 19:34:42 +02:00
|
|
|
onImport();
|
2018-05-10 14:54:58 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-03-23 20:48:02 +01:00
|
|
|
Preference importAppPreference = findPreference("pref_import_app");
|
|
|
|
importAppPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onPreferenceClick(Preference preference) {
|
|
|
|
onImportApp();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
Preference exportPreference = findPreference("pref_export");
|
|
|
|
exportPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
2018-05-10 14:54:58 +02:00
|
|
|
@Override
|
|
|
|
public boolean onPreferenceClick(Preference preference) {
|
2018-05-10 19:34:42 +02:00
|
|
|
onExport();
|
2018-05-10 14:54:58 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-09-14 20:54:07 +02:00
|
|
|
/*EditTextPreference timeoutPreference = (EditTextPreference) findPreference("pref_timeout");
|
2018-05-10 14:54:58 +02:00
|
|
|
timeoutPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
|
|
preference.setSummary(String.format(getString(R.string.pref_timeout_summary), (String) newValue));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
2018-09-14 20:54:07 +02:00
|
|
|
timeoutPreference.getOnPreferenceChangeListener().onPreferenceChange(timeoutPreference, timeoutPreference.getText());*/
|
2018-05-10 14:54:58 +02:00
|
|
|
|
2018-09-19 00:10:03 +02:00
|
|
|
Preference issuerPreference = findPreference("pref_account_name");
|
2018-05-10 14:54:58 +02:00
|
|
|
issuerPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
|
|
_result.putExtra("needsRefresh", true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
2018-05-11 19:33:20 +02:00
|
|
|
|
2019-03-25 21:32:29 +01:00
|
|
|
Preference tapToRevealPreference = findPreference("pref_tap_to_reveal");
|
|
|
|
tapToRevealPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
|
|
|
_result.putExtra("needsRefresh", true);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
2018-05-11 19:33:20 +02:00
|
|
|
Preference screenPreference = findPreference("pref_secure_screen");
|
|
|
|
screenPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
|
|
_result.putExtra("needsRecreate", true);
|
|
|
|
Window window = getActivity().getWindow();
|
|
|
|
if ((boolean)newValue) {
|
|
|
|
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
|
|
|
} else {
|
|
|
|
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
2018-05-14 16:53:27 +02:00
|
|
|
|
2019-03-26 00:06:39 +01:00
|
|
|
Preference tapToRevealTimePreference = findPreference("pref_tap_to_reveal_time");
|
|
|
|
tapToRevealTimePreference.setSummary(app.getPreferences().getTapToRevealTime() + " seconds");
|
|
|
|
tapToRevealTimePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onPreferenceClick(Preference preference) {
|
|
|
|
Dialogs.showNumberPickerDialog(getActivity(), new Dialogs.NumberInputListener() {
|
|
|
|
@Override
|
|
|
|
public void onNumberInputResult(int number) {
|
|
|
|
app.getPreferences().setTapToRevealTime(number);
|
|
|
|
tapToRevealTimePreference.setSummary(number + " seconds");
|
|
|
|
_result.putExtra("needsRefresh", true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
2019-03-25 21:32:29 +01:00
|
|
|
|
2018-05-14 16:53:27 +02:00
|
|
|
_encryptionPreference = (SwitchPreference) findPreference("pref_encryption");
|
|
|
|
_encryptionPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
2018-10-06 22:23:38 +02:00
|
|
|
if (!_db.isEncryptionEnabled()) {
|
2018-11-15 21:23:18 +01:00
|
|
|
Dialogs.showSetPasswordDialog(getActivity(), new EnableEncryptionListener());
|
2018-05-14 16:53:27 +02:00
|
|
|
} else {
|
2018-11-15 22:20:31 +01:00
|
|
|
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
2018-10-09 23:13:51 +02:00
|
|
|
.setTitle(getString(R.string.disable_encryption))
|
|
|
|
.setMessage(getString(R.string.disable_encryption_description))
|
2018-05-14 16:53:27 +02:00
|
|
|
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
2018-10-06 22:23:38 +02:00
|
|
|
try {
|
|
|
|
_db.disableEncryption();
|
|
|
|
} catch (DatabaseManagerException e) {
|
2018-10-09 23:13:51 +02:00
|
|
|
Toast.makeText(getActivity(), getString(R.string.encrypting_error), Toast.LENGTH_SHORT).show();
|
2018-10-06 22:23:38 +02:00
|
|
|
}
|
2018-11-15 21:23:18 +01:00
|
|
|
updateEncryptionPreferences();
|
2018-05-14 16:53:27 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.setNegativeButton(android.R.string.no, null)
|
2018-11-15 22:20:31 +01:00
|
|
|
.create());
|
2018-05-14 16:53:27 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
2018-10-10 20:41:37 +02:00
|
|
|
|
2018-11-15 21:23:18 +01:00
|
|
|
_fingerprintPreference = (SwitchPreference) findPreference("pref_fingerprint");
|
|
|
|
_fingerprintPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
|
|
DatabaseFileCredentials creds = _db.getCredentials();
|
|
|
|
SlotList slots = creds.getSlots();
|
|
|
|
|
|
|
|
if (!slots.has(FingerprintSlot.class)) {
|
|
|
|
Dialogs.showFingerprintDialog(getActivity(), new RegisterFingerprintListener());
|
|
|
|
} else {
|
|
|
|
// remove the fingerprint slot
|
|
|
|
FingerprintSlot slot = slots.find(FingerprintSlot.class);
|
|
|
|
slots.remove(slot);
|
|
|
|
_db.setCredentials(creds);
|
|
|
|
|
|
|
|
saveDatabase();
|
|
|
|
updateEncryptionPreferences();
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
_setPasswordPreference = findPreference("pref_password");
|
|
|
|
_setPasswordPreference.setOnPreferenceClickListener(preference -> {
|
|
|
|
Dialogs.showSetPasswordDialog(getActivity(), new SetPasswordListener());
|
2018-10-10 20:41:37 +02:00
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
2018-05-14 16:53:27 +02:00
|
|
|
_slotsPreference = findPreference("pref_slots");
|
|
|
|
_slotsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onPreferenceClick(Preference preference) {
|
|
|
|
Intent intent = new Intent(getActivity(), SlotManagerActivity.class);
|
2018-10-06 22:23:38 +02:00
|
|
|
intent.putExtra("creds", _db.getCredentials());
|
2018-05-14 16:53:27 +02:00
|
|
|
startActivityForResult(intent, CODE_SLOTS);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
2018-11-15 21:23:18 +01:00
|
|
|
|
2018-12-16 22:57:04 +01:00
|
|
|
_groupsPreference = findPreference("pref_groups");
|
|
|
|
_groupsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onPreferenceClick(Preference preference) {
|
|
|
|
Intent intent = new Intent(getActivity(), GroupManagerActivity.class);
|
2018-12-18 22:46:35 +01:00
|
|
|
intent.putExtra("groups", new ArrayList<>(_db.getGroups()));
|
|
|
|
startActivityForResult(intent, CODE_GROUPS);
|
2018-12-16 22:57:04 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
2018-11-15 21:23:18 +01:00
|
|
|
updateEncryptionPreferences();
|
2018-05-10 14:54:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
|
|
|
if (!PermissionHelper.checkResults(grantResults)) {
|
2018-10-09 23:13:51 +02:00
|
|
|
Toast.makeText(getActivity(), getString(R.string.permission_denied), Toast.LENGTH_SHORT).show();
|
2018-05-10 14:54:58 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (requestCode) {
|
|
|
|
case CODE_PERM_IMPORT:
|
|
|
|
onImport();
|
|
|
|
break;
|
2018-05-10 19:34:42 +02:00
|
|
|
case CODE_PERM_EXPORT:
|
|
|
|
onExport();
|
|
|
|
break;
|
2018-05-10 14:54:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
|
|
if (data == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (requestCode) {
|
|
|
|
case CODE_IMPORT:
|
|
|
|
onImportResult(resultCode, data);
|
|
|
|
break;
|
2018-05-10 18:42:47 +02:00
|
|
|
case CODE_IMPORT_DECRYPT:
|
|
|
|
onImportDecryptResult(resultCode, data);
|
|
|
|
break;
|
2018-05-10 21:46:27 +02:00
|
|
|
case CODE_SLOTS:
|
|
|
|
onSlotManagerResult(resultCode, data);
|
|
|
|
break;
|
2018-12-18 22:46:35 +01:00
|
|
|
case CODE_GROUPS:
|
|
|
|
onGroupManagerResult(resultCode, data);
|
|
|
|
break;
|
2018-05-10 14:54:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-11 21:30:15 +02:00
|
|
|
public Intent getResult() {
|
|
|
|
return _result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setResult(Intent result) {
|
|
|
|
_result = result;
|
|
|
|
getActivity().setResult(Activity.RESULT_OK, _result);
|
|
|
|
}
|
|
|
|
|
2018-05-10 14:54:58 +02:00
|
|
|
private void onImport() {
|
2018-06-07 12:34:20 +02:00
|
|
|
if (!PermissionHelper.request(getActivity(), CODE_PERM_IMPORT, Manifest.permission.READ_EXTERNAL_STORAGE)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-23 20:48:02 +01:00
|
|
|
Map<String, Class<? extends DatabaseFileImporter>> importers = DatabaseFileImporter.getImporters();
|
2018-05-13 21:58:41 +02:00
|
|
|
String[] names = importers.keySet().toArray(new String[importers.size()]);
|
|
|
|
|
2018-11-15 22:20:31 +01:00
|
|
|
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
2018-10-09 23:13:51 +02:00
|
|
|
.setTitle(getString(R.string.choose_application))
|
2018-05-13 21:58:41 +02:00
|
|
|
.setSingleChoiceItems(names, 0, null)
|
|
|
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
|
|
|
_importerType = importers.get(names[i]);
|
|
|
|
|
2018-06-07 12:34:20 +02:00
|
|
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
|
|
intent.setType("*/*");
|
|
|
|
startActivityForResult(intent, CODE_IMPORT);
|
2018-05-13 21:58:41 +02:00
|
|
|
}
|
|
|
|
})
|
2018-11-15 22:20:31 +01:00
|
|
|
.create());
|
2018-05-10 14:54:58 +02:00
|
|
|
}
|
|
|
|
|
2019-03-23 20:48:02 +01:00
|
|
|
private void onImportApp() {
|
|
|
|
Map<String, Class<? extends DatabaseAppImporter>> importers = DatabaseAppImporter.getImporters();
|
|
|
|
String[] names = importers.keySet().toArray(new String[importers.size()]);
|
|
|
|
|
|
|
|
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
|
|
|
|
.setTitle(getString(R.string.choose_application))
|
|
|
|
.setSingleChoiceItems(names, 0, null)
|
|
|
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
|
|
|
try {
|
|
|
|
DatabaseAppImporter 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(), getString(R.string.root_error), Toast.LENGTH_SHORT).show();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
importer = DatabaseAppImporter.create(getContext(), importers.get(names[i]));
|
|
|
|
importer.parse();
|
|
|
|
} catch (IOException e) {
|
|
|
|
Toast.makeText(getActivity(), getString(R.string.root_error), Toast.LENGTH_SHORT).show();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
importDatabase(importer);
|
|
|
|
} catch (DatabaseImporterException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
|
|
String msg = String.format("%s: %s", getString(R.string.parsing_file_error), e.getMessage());
|
|
|
|
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.create());
|
|
|
|
}
|
|
|
|
|
2018-05-10 18:42:47 +02:00
|
|
|
private void onImportDecryptResult(int resultCode, Intent data) {
|
2018-05-10 14:54:58 +02:00
|
|
|
if (resultCode != Activity.RESULT_OK) {
|
2018-05-13 21:58:41 +02:00
|
|
|
_importer = null;
|
2018-05-10 14:54:58 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-10-06 22:23:38 +02:00
|
|
|
DatabaseFileCredentials creds = (DatabaseFileCredentials) data.getSerializableExtra("creds");
|
2019-03-23 20:48:02 +01:00
|
|
|
((AegisFileImporter)_importer).setCredentials(creds);
|
2018-05-10 14:54:58 +02:00
|
|
|
|
|
|
|
try {
|
2018-05-13 21:58:41 +02:00
|
|
|
importDatabase(_importer);
|
2018-05-10 18:42:47 +02:00
|
|
|
} catch (DatabaseImporterException e) {
|
2019-03-23 13:32:04 +01:00
|
|
|
e.printStackTrace();
|
|
|
|
|
|
|
|
String msg = String.format("%s: %s", getString(R.string.parsing_file_error), e.getMessage());
|
|
|
|
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
2018-05-10 18:42:47 +02:00
|
|
|
}
|
2018-05-10 21:46:27 +02:00
|
|
|
|
2018-05-13 21:58:41 +02:00
|
|
|
_importer = null;
|
2018-05-10 18:42:47 +02:00
|
|
|
}
|
2018-05-10 14:54:58 +02:00
|
|
|
|
2018-05-10 18:42:47 +02:00
|
|
|
private void onImportResult(int resultCode, Intent data) {
|
|
|
|
Uri uri = data.getData();
|
|
|
|
if (resultCode != Activity.RESULT_OK || uri == null) {
|
|
|
|
return;
|
|
|
|
}
|
2018-05-10 14:54:58 +02:00
|
|
|
|
2018-05-10 18:42:47 +02:00
|
|
|
ByteInputStream stream;
|
|
|
|
|
2018-10-06 22:23:38 +02:00
|
|
|
try (InputStream fileStream = getActivity().getContentResolver().openInputStream(uri)) {
|
2018-05-10 18:42:47 +02:00
|
|
|
stream = ByteInputStream.create(fileStream);
|
|
|
|
} catch (FileNotFoundException e) {
|
2018-10-09 23:13:51 +02:00
|
|
|
Toast.makeText(getActivity(), getString(R.string.file_not_found), Toast.LENGTH_SHORT).show();
|
2018-05-10 18:42:47 +02:00
|
|
|
return;
|
|
|
|
} catch (IOException e) {
|
2018-10-06 22:23:38 +02:00
|
|
|
e.printStackTrace();
|
2018-10-09 23:13:51 +02:00
|
|
|
Toast.makeText(getActivity(), getString(R.string.reading_file_error), Toast.LENGTH_SHORT).show();
|
2018-05-10 18:42:47 +02:00
|
|
|
return;
|
2018-05-10 14:54:58 +02:00
|
|
|
}
|
|
|
|
|
2018-05-13 21:58:41 +02:00
|
|
|
try {
|
2019-03-23 20:48:02 +01:00
|
|
|
DatabaseFileImporter importer = DatabaseFileImporter.create(getContext(), stream, _importerType);
|
2018-05-13 21:58:41 +02:00
|
|
|
importer.parse();
|
2018-05-10 18:42:47 +02:00
|
|
|
|
2018-05-13 21:58:41 +02:00
|
|
|
// special case to decrypt encrypted aegis databases
|
2019-03-23 20:48:02 +01:00
|
|
|
if (importer.isEncrypted() && importer instanceof AegisFileImporter) {
|
2018-05-13 21:58:41 +02:00
|
|
|
_importer = importer;
|
2018-05-10 18:42:47 +02:00
|
|
|
|
2018-05-13 21:58:41 +02:00
|
|
|
Intent intent = new Intent(getActivity(), AuthActivity.class);
|
2019-03-23 20:48:02 +01:00
|
|
|
intent.putExtra("slots", ((AegisFileImporter)_importer).getFile().getHeader().getSlots());
|
2018-05-13 21:58:41 +02:00
|
|
|
startActivityForResult(intent, CODE_IMPORT_DECRYPT);
|
|
|
|
return;
|
2018-05-10 18:42:47 +02:00
|
|
|
}
|
|
|
|
|
2018-05-13 21:58:41 +02:00
|
|
|
importDatabase(importer);
|
|
|
|
} catch (DatabaseImporterException e) {
|
|
|
|
e.printStackTrace();
|
2019-03-23 13:32:04 +01:00
|
|
|
|
|
|
|
String msg = String.format("%s: %s", getString(R.string.parsing_file_error), e.getMessage());
|
|
|
|
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
|
2018-05-10 14:54:58 +02:00
|
|
|
}
|
2018-05-10 18:42:47 +02:00
|
|
|
}
|
2018-05-10 14:54:58 +02:00
|
|
|
|
2018-05-13 21:58:41 +02:00
|
|
|
private void importDatabase(DatabaseImporter importer) throws DatabaseImporterException {
|
|
|
|
List<DatabaseEntry> entries = importer.convert();
|
2018-05-10 14:54:58 +02:00
|
|
|
for (DatabaseEntry entry : entries) {
|
2018-09-22 14:12:42 +02:00
|
|
|
// temporary: randomize the UUID of duplicate entries and add them anyway
|
|
|
|
if (_db.getEntryByUUID(entry.getUUID()) != null) {
|
|
|
|
entry.resetUUID();
|
|
|
|
}
|
|
|
|
|
2018-06-06 16:15:31 +02:00
|
|
|
_db.addEntry(entry);
|
2018-05-10 14:54:58 +02:00
|
|
|
}
|
|
|
|
|
2018-05-10 21:46:27 +02:00
|
|
|
if (!saveDatabase()) {
|
2018-05-10 14:54:58 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-05-11 19:33:20 +02:00
|
|
|
_result.putExtra("needsRecreate", true);
|
2018-10-09 23:13:51 +02:00
|
|
|
Toast.makeText(getActivity(), String.format(Locale.getDefault(), getString(R.string.imported_entries_count), entries.size()), Toast.LENGTH_LONG).show();
|
2018-05-10 14:54:58 +02:00
|
|
|
}
|
2018-05-10 19:34:42 +02:00
|
|
|
|
|
|
|
private void onExport() {
|
|
|
|
if (!PermissionHelper.request(getActivity(), CODE_PERM_EXPORT, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: create a custom layout to show a message AND a checkbox
|
2018-11-15 21:36:10 +01:00
|
|
|
final AtomicReference<Boolean> checked = new AtomicReference<>(true);
|
2018-05-10 19:34:42 +02:00
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
|
|
|
|
.setTitle("Export the database")
|
|
|
|
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
|
|
|
String filename;
|
|
|
|
try {
|
2018-11-15 21:36:10 +01:00
|
|
|
filename = _db.export(checked.get());
|
2018-05-10 19:34:42 +02:00
|
|
|
} catch (DatabaseManagerException e) {
|
2018-10-09 23:13:51 +02:00
|
|
|
Toast.makeText(getActivity(), getString(R.string.exporting_database_error), Toast.LENGTH_SHORT).show();
|
2018-05-10 19:34:42 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// make sure the new file is visible
|
|
|
|
MediaScannerConnection.scanFile(getActivity(), new String[]{filename}, null, null);
|
|
|
|
|
2018-10-09 23:13:51 +02:00
|
|
|
Toast.makeText(getActivity(), getString(R.string.export_database_location) + filename, Toast.LENGTH_SHORT).show();
|
2018-05-10 19:34:42 +02:00
|
|
|
})
|
|
|
|
.setNegativeButton(android.R.string.cancel, null);
|
2018-10-06 22:23:38 +02:00
|
|
|
if (_db.isEncryptionEnabled()) {
|
2018-05-10 19:34:42 +02:00
|
|
|
final String[] items = {"Keep the database encrypted"};
|
|
|
|
final boolean[] checkedItems = {true};
|
|
|
|
builder.setMultiChoiceItems(items, checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(DialogInterface dialog, int index, boolean isChecked) {
|
2018-11-15 21:36:10 +01:00
|
|
|
checked.set(isChecked);
|
2018-05-10 19:34:42 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
2018-10-09 23:13:51 +02:00
|
|
|
builder.setMessage(getString(R.string.export_warning));
|
2018-05-10 19:34:42 +02:00
|
|
|
}
|
2018-11-15 22:20:31 +01:00
|
|
|
Dialogs.showSecureDialog(builder.create());
|
2018-05-10 19:34:42 +02:00
|
|
|
}
|
2018-05-10 21:46:27 +02:00
|
|
|
|
|
|
|
private void onSlotManagerResult(int resultCode, Intent data) {
|
|
|
|
if (resultCode != Activity.RESULT_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-10-06 22:23:38 +02:00
|
|
|
DatabaseFileCredentials creds = (DatabaseFileCredentials) data.getSerializableExtra("creds");
|
|
|
|
_db.setCredentials(creds);
|
2018-05-10 21:46:27 +02:00
|
|
|
saveDatabase();
|
2018-11-15 21:23:18 +01:00
|
|
|
updateEncryptionPreferences();
|
2018-05-10 21:46:27 +02:00
|
|
|
}
|
|
|
|
|
2018-12-18 22:46:35 +01:00
|
|
|
private void onGroupManagerResult(int resultCode, Intent data) {
|
2018-12-18 23:18:11 +01:00
|
|
|
if (resultCode != Activity.RESULT_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-18 22:46:35 +01:00
|
|
|
HashSet<String> groups = new HashSet<>(data.getStringArrayListExtra("groups"));
|
|
|
|
|
|
|
|
for (DatabaseEntry entry : _db.getEntries()) {
|
|
|
|
if (!groups.contains(entry.getGroup())) {
|
|
|
|
entry.setGroup(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-10 21:46:27 +02:00
|
|
|
private boolean saveDatabase() {
|
|
|
|
try {
|
|
|
|
_db.save();
|
|
|
|
} catch (DatabaseManagerException e) {
|
2018-10-09 23:13:51 +02:00
|
|
|
Toast.makeText(getActivity(), getString(R.string.saving_error), Toast.LENGTH_LONG).show();
|
2018-05-10 21:46:27 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2018-05-14 16:53:27 +02:00
|
|
|
|
2018-11-15 21:23:18 +01:00
|
|
|
private void updateEncryptionPreferences() {
|
|
|
|
boolean encrypted = _db.isEncryptionEnabled();
|
|
|
|
_encryptionPreference.setChecked(encrypted, true);
|
|
|
|
_setPasswordPreference.setVisible(encrypted);
|
|
|
|
_fingerprintPreference.setVisible(encrypted);
|
|
|
|
_slotsPreference.setEnabled(encrypted);
|
2018-05-14 16:53:27 +02:00
|
|
|
|
2018-11-15 21:23:18 +01:00
|
|
|
if (encrypted) {
|
|
|
|
SlotList slots = _db.getCredentials().getSlots();
|
|
|
|
boolean multiPassword = slots.findAll(PasswordSlot.class).size() > 1;
|
|
|
|
boolean multiFinger = slots.findAll(FingerprintSlot.class).size() > 1;
|
|
|
|
boolean showSlots = BuildConfig.DEBUG || multiPassword || multiFinger;
|
|
|
|
_setPasswordPreference.setEnabled(!multiPassword);
|
|
|
|
_fingerprintPreference.setEnabled(FingerprintHelper.getManager(getContext()) != null && !multiFinger);
|
|
|
|
_fingerprintPreference.setChecked(slots.has(FingerprintSlot.class), true);
|
|
|
|
_slotsPreference.setVisible(showSlots);
|
|
|
|
} else {
|
|
|
|
_setPasswordPreference.setEnabled(false);
|
|
|
|
_fingerprintPreference.setEnabled(false);
|
|
|
|
_fingerprintPreference.setChecked(false, true);
|
|
|
|
_slotsPreference.setVisible(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class SetPasswordListener implements Dialogs.SlotListener {
|
|
|
|
@Override
|
|
|
|
public void onSlotResult(Slot slot, Cipher cipher) {
|
2018-11-15 23:34:46 +01:00
|
|
|
DatabaseFileCredentials creds = _db.getCredentials();
|
|
|
|
SlotList slots = creds.getSlots();
|
2018-11-15 21:23:18 +01:00
|
|
|
|
|
|
|
try {
|
2018-11-15 23:34:46 +01:00
|
|
|
// encrypt the master key for this slot
|
2018-11-15 21:23:18 +01:00
|
|
|
slot.setKey(creds.getKey(), cipher);
|
2018-11-15 23:34:46 +01:00
|
|
|
|
|
|
|
// remove the old master password slot
|
|
|
|
PasswordSlot oldSlot = creds.getSlots().find(PasswordSlot.class);
|
|
|
|
slots.remove(oldSlot);
|
|
|
|
|
|
|
|
// add the new master password slot
|
|
|
|
slots.add(slot);
|
|
|
|
} catch (SlotException e) {
|
2018-11-15 21:23:18 +01:00
|
|
|
onException(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-15 23:34:46 +01:00
|
|
|
_db.setCredentials(creds);
|
|
|
|
saveDatabase();
|
2018-05-14 16:53:27 +02:00
|
|
|
}
|
|
|
|
|
2018-11-15 21:23:18 +01:00
|
|
|
@Override
|
|
|
|
public void onException(Exception e) {
|
|
|
|
updateEncryptionPreferences();
|
|
|
|
Toast.makeText(getActivity(), getString(R.string.encryption_set_password_error) + e.getMessage(), Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
2018-05-14 16:53:27 +02:00
|
|
|
}
|
|
|
|
|
2018-11-15 21:23:18 +01:00
|
|
|
private class RegisterFingerprintListener implements Dialogs.SlotListener {
|
|
|
|
@Override
|
|
|
|
public void onSlotResult(Slot slot, Cipher cipher) {
|
|
|
|
DatabaseFileCredentials creds = _db.getCredentials();
|
|
|
|
SlotList slots = creds.getSlots();
|
|
|
|
|
|
|
|
try {
|
|
|
|
slot.setKey(creds.getKey(), cipher);
|
|
|
|
} catch (SlotException e) {
|
|
|
|
onException(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
slots.add(slot);
|
|
|
|
_db.setCredentials(creds);
|
|
|
|
|
|
|
|
saveDatabase();
|
|
|
|
updateEncryptionPreferences();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onException(Exception e) {
|
|
|
|
Toast.makeText(getActivity(), getString(R.string.encryption_enable_fingerprint_error) + e.getMessage(), Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
2018-05-14 16:53:27 +02:00
|
|
|
}
|
|
|
|
|
2018-11-15 21:23:18 +01:00
|
|
|
private class EnableEncryptionListener implements Dialogs.SlotListener {
|
|
|
|
@Override
|
|
|
|
public void onSlotResult(Slot slot, Cipher cipher) {
|
2018-11-15 23:34:46 +01:00
|
|
|
DatabaseFileCredentials creds = new DatabaseFileCredentials();
|
2018-11-15 21:23:18 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
slot.setKey(creds.getKey(), cipher);
|
2018-11-15 23:34:46 +01:00
|
|
|
creds.getSlots().add(slot);
|
|
|
|
_db.enableEncryption(creds);
|
|
|
|
} catch (DatabaseManagerException | SlotException e) {
|
2018-11-15 21:23:18 +01:00
|
|
|
onException(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-15 23:34:46 +01:00
|
|
|
updateEncryptionPreferences();
|
2018-11-15 21:23:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onException(Exception e) {
|
|
|
|
Toast.makeText(getActivity(), getString(R.string.encryption_set_password_error) + e.getMessage(), Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
2018-05-14 16:53:27 +02:00
|
|
|
}
|
2018-05-10 18:42:47 +02:00
|
|
|
}
|