mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-04 12:24:49 +00:00
Make fingerprint unlock toggleable
Also: - Fix a bug where setting the password would clear the key slot list - Show the "Key slots" preference if multiple slots of one kind exist
This commit is contained in:
parent
926b5139da
commit
80b1967693
10 changed files with 317 additions and 261 deletions
|
@ -2,12 +2,7 @@ package me.impy.aegis.ui;
|
|||
|
||||
import android.os.Bundle;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import me.impy.aegis.db.slots.Slot;
|
||||
import me.impy.aegis.ui.dialogs.PasswordDialogFragment;
|
||||
|
||||
public class PreferencesActivity extends AegisActivity implements PasswordDialogFragment.Listener {
|
||||
public class PreferencesActivity extends AegisActivity {
|
||||
private PreferencesFragment _fragment;
|
||||
|
||||
@Override
|
||||
|
@ -41,14 +36,4 @@ public class PreferencesActivity extends AegisActivity implements PasswordDialog
|
|||
outState.putParcelable("result", _fragment.getResult());
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlotResult(Slot slot, Cipher cipher) {
|
||||
_fragment.onSlotResult(slot, cipher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Exception e) {
|
||||
_fragment.onException(e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,20 +29,23 @@ import me.impy.aegis.BuildConfig;
|
|||
import me.impy.aegis.R;
|
||||
import me.impy.aegis.db.DatabaseEntry;
|
||||
import me.impy.aegis.db.DatabaseFileCredentials;
|
||||
import me.impy.aegis.db.DatabaseFileException;
|
||||
import me.impy.aegis.db.DatabaseManager;
|
||||
import me.impy.aegis.db.DatabaseManagerException;
|
||||
import me.impy.aegis.db.slots.FingerprintSlot;
|
||||
import me.impy.aegis.db.slots.PasswordSlot;
|
||||
import me.impy.aegis.db.slots.Slot;
|
||||
import me.impy.aegis.db.slots.SlotException;
|
||||
import me.impy.aegis.db.slots.SlotList;
|
||||
import me.impy.aegis.helpers.FingerprintHelper;
|
||||
import me.impy.aegis.helpers.PermissionHelper;
|
||||
import me.impy.aegis.importers.AegisImporter;
|
||||
import me.impy.aegis.importers.DatabaseImporter;
|
||||
import me.impy.aegis.importers.DatabaseImporterException;
|
||||
import me.impy.aegis.ui.dialogs.PasswordDialogFragment;
|
||||
import me.impy.aegis.ui.dialogs.Dialogs;
|
||||
import me.impy.aegis.ui.preferences.SwitchPreference;
|
||||
import me.impy.aegis.util.ByteInputStream;
|
||||
|
||||
public class PreferencesFragment extends PreferenceFragmentCompat implements PasswordDialogFragment.Listener {
|
||||
public class PreferencesFragment extends PreferenceFragmentCompat {
|
||||
// activity request codes
|
||||
private static final int CODE_IMPORT = 0;
|
||||
private static final int CODE_IMPORT_DECRYPT = 1;
|
||||
|
@ -61,6 +64,8 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
private Class<? extends DatabaseImporter> _importerType;
|
||||
|
||||
private SwitchPreference _encryptionPreference;
|
||||
private SwitchPreference _fingerprintPreference;
|
||||
private Preference _setPasswordPreference;
|
||||
private Preference _slotsPreference;
|
||||
|
||||
@Override
|
||||
|
@ -140,9 +145,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
if (!_db.isEncryptionEnabled()) {
|
||||
PasswordDialogFragment dialog = new PasswordDialogFragment();
|
||||
// TODO: find a less ugly way to obtain the fragment manager
|
||||
dialog.show(getActivity().getSupportFragmentManager(), null);
|
||||
Dialogs.showSetPasswordDialog(getActivity(), new EnableEncryptionListener());
|
||||
} else {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(getString(R.string.disable_encryption))
|
||||
|
@ -154,7 +157,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
} catch (DatabaseManagerException e) {
|
||||
Toast.makeText(getActivity(), getString(R.string.encrypting_error), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
updateEncryptionPreference();
|
||||
updateEncryptionPreferences();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
|
@ -164,20 +167,37 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
}
|
||||
});
|
||||
|
||||
Preference setPasswordPreference = findPreference("pref_password");
|
||||
setPasswordPreference.setOnPreferenceClickListener(preference -> {
|
||||
PasswordDialogFragment dialog = new PasswordDialogFragment();
|
||||
// TODO: find a less ugly way to obtain the fragment manager
|
||||
dialog.show(getActivity().getSupportFragmentManager(), null);
|
||||
_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());
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
_slotsPreference = findPreference("pref_slots");
|
||||
if (BuildConfig.DEBUG) {
|
||||
_slotsPreference.setVisible(true);
|
||||
} else {
|
||||
_slotsPreference.setVisible(false);
|
||||
}
|
||||
_slotsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
|
@ -187,7 +207,8 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
return true;
|
||||
}
|
||||
});
|
||||
updateEncryptionPreference();
|
||||
|
||||
updateEncryptionPreferences();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -383,6 +404,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
DatabaseFileCredentials creds = (DatabaseFileCredentials) data.getSerializableExtra("creds");
|
||||
_db.setCredentials(creds);
|
||||
saveDatabase();
|
||||
updateEncryptionPreferences();
|
||||
}
|
||||
|
||||
private boolean saveDatabase() {
|
||||
|
@ -396,31 +418,108 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlotResult(Slot slot, Cipher cipher) {
|
||||
DatabaseFileCredentials creds = new DatabaseFileCredentials();
|
||||
|
||||
try {
|
||||
slot.setKey(creds.getKey(), cipher);
|
||||
creds.getSlots().add(slot);
|
||||
_db.enableEncryption(creds);
|
||||
} catch (DatabaseManagerException | SlotException e) {
|
||||
onException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
updateEncryptionPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Exception e) {
|
||||
updateEncryptionPreference();
|
||||
Toast.makeText(getActivity(), getString(R.string.encryption_set_password_error) + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private void updateEncryptionPreference() {
|
||||
private void updateEncryptionPreferences() {
|
||||
boolean encrypted = _db.isEncryptionEnabled();
|
||||
_encryptionPreference.setChecked(encrypted, true);
|
||||
_setPasswordPreference.setVisible(encrypted);
|
||||
_fingerprintPreference.setVisible(encrypted);
|
||||
_slotsPreference.setEnabled(encrypted);
|
||||
|
||||
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) {
|
||||
DatabaseFileCredentials creds = new DatabaseFileCredentials();
|
||||
|
||||
try {
|
||||
slot.setKey(creds.getKey(), cipher);
|
||||
creds.getSlots().add(slot);
|
||||
_db.enableEncryption(creds);
|
||||
} catch (DatabaseManagerException | SlotException e) {
|
||||
onException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
updateEncryptionPreferences();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Exception e) {
|
||||
updateEncryptionPreferences();
|
||||
Toast.makeText(getActivity(), getString(R.string.encryption_set_password_error) + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private class EnableEncryptionListener implements Dialogs.SlotListener {
|
||||
@Override
|
||||
public void onSlotResult(Slot slot, Cipher cipher) {
|
||||
DatabaseFileCredentials creds = _db.getCredentials();
|
||||
SlotList slots = creds.getSlots();
|
||||
|
||||
try {
|
||||
// encrypt the master key for this slot
|
||||
slot.setKey(creds.getKey(), cipher);
|
||||
|
||||
// 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) {
|
||||
onException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
_db.setCredentials(creds);
|
||||
saveDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Exception e) {
|
||||
Toast.makeText(getActivity(), getString(R.string.encryption_set_password_error) + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,11 +24,9 @@ import me.impy.aegis.db.slots.SlotList;
|
|||
import me.impy.aegis.db.slots.SlotException;
|
||||
import me.impy.aegis.helpers.FingerprintHelper;
|
||||
import me.impy.aegis.ui.dialogs.Dialogs;
|
||||
import me.impy.aegis.ui.dialogs.FingerprintDialogFragment;
|
||||
import me.impy.aegis.ui.views.SlotAdapter;
|
||||
import me.impy.aegis.ui.dialogs.SlotDialogFragment;
|
||||
|
||||
public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Listener, SlotDialogFragment.Listener {
|
||||
public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Listener, Dialogs.SlotListener {
|
||||
private DatabaseFileCredentials _creds;
|
||||
private SlotAdapter _adapter;
|
||||
|
||||
|
@ -45,8 +43,11 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li
|
|||
bar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
findViewById(R.id.button_add_fingerprint).setOnClickListener(view -> {
|
||||
FingerprintDialogFragment dialog = new FingerprintDialogFragment();
|
||||
dialog.show(getSupportFragmentManager(), null);
|
||||
Dialogs.showFingerprintDialog(this ,this);
|
||||
});
|
||||
|
||||
findViewById(R.id.button_add_password).setOnClickListener(view -> {
|
||||
Dialogs.showSetPasswordDialog(this, this);
|
||||
});
|
||||
|
||||
// set up the recycler view
|
||||
|
|
|
@ -2,17 +2,40 @@ package me.impy.aegis.ui.dialogs;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.content.DialogInterface;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.mattprecious.swirl.SwirlView;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import me.impy.aegis.R;
|
||||
import me.impy.aegis.crypto.KeyStoreHandle;
|
||||
import me.impy.aegis.crypto.KeyStoreHandleException;
|
||||
import me.impy.aegis.db.slots.FingerprintSlot;
|
||||
import me.impy.aegis.db.slots.PasswordSlot;
|
||||
import me.impy.aegis.db.slots.Slot;
|
||||
import me.impy.aegis.db.slots.SlotException;
|
||||
import me.impy.aegis.helpers.EditTextHelper;
|
||||
import me.impy.aegis.helpers.FingerprintHelper;
|
||||
import me.impy.aegis.helpers.FingerprintUiHelper;
|
||||
import me.impy.aegis.ui.tasks.DerivationTask;
|
||||
|
||||
public class Dialogs {
|
||||
private Dialogs() {
|
||||
|
||||
}
|
||||
|
||||
public static AlertDialog showDeleteEntryDialog(Activity activity, DialogInterface.OnClickListener onDelete) {
|
||||
return new AlertDialog.Builder(activity)
|
||||
public static void showDeleteEntryDialog(Activity activity, DialogInterface.OnClickListener onDelete) {
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(activity.getString(R.string.delete_entry))
|
||||
.setMessage(activity.getString(R.string.delete_entry_description))
|
||||
.setPositiveButton(android.R.string.yes, onDelete)
|
||||
|
@ -20,12 +43,115 @@ public class Dialogs {
|
|||
.show();
|
||||
}
|
||||
|
||||
public static AlertDialog showDiscardDialog(Activity activity, DialogInterface.OnClickListener onSave, DialogInterface.OnClickListener onDiscard) {
|
||||
return new AlertDialog.Builder(activity)
|
||||
public static void showDiscardDialog(Activity activity, DialogInterface.OnClickListener onSave, DialogInterface.OnClickListener onDiscard) {
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(activity.getString(R.string.discard_changes))
|
||||
.setMessage(activity.getString(R.string.discard_changes_description))
|
||||
.setPositiveButton(R.string.save, onSave)
|
||||
.setNegativeButton(R.string.discard, onDiscard)
|
||||
.show();
|
||||
}
|
||||
|
||||
public static void showSetPasswordDialog(Activity activity, Dialogs.SlotListener listener) {
|
||||
View view = activity.getLayoutInflater().inflate(R.layout.dialog_password, null);
|
||||
EditText textPassword = view.findViewById(R.id.text_password);
|
||||
EditText textPasswordConfirm = view.findViewById(R.id.text_password_confirm);
|
||||
|
||||
AlertDialog alert = new AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.set_password)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create();
|
||||
|
||||
final Button[] buttonOK = new Button[1];
|
||||
alert.setOnShowListener(dialog -> {
|
||||
buttonOK[0] = alert.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
buttonOK[0].setEnabled(false);
|
||||
|
||||
// replace the default listener
|
||||
buttonOK[0].setOnClickListener(v -> {
|
||||
if (!EditTextHelper.areEditTextsEqual(textPassword, textPasswordConfirm)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char[] password = EditTextHelper.getEditTextChars(textPassword);
|
||||
PasswordSlot slot = new PasswordSlot();
|
||||
DerivationTask task = new DerivationTask(activity, key -> {
|
||||
Cipher cipher;
|
||||
try {
|
||||
cipher = Slot.createEncryptCipher(key);
|
||||
} catch (SlotException e) {
|
||||
listener.onException(e);
|
||||
dialog.cancel();
|
||||
return;
|
||||
}
|
||||
listener.onSlotResult(slot, cipher);
|
||||
dialog.dismiss();
|
||||
});
|
||||
task.execute(new DerivationTask.Params(slot, password));
|
||||
});
|
||||
});
|
||||
|
||||
TextWatcher watcher = new TextWatcher() {
|
||||
public void onTextChanged(CharSequence c, int start, int before, int count) {
|
||||
boolean equal = EditTextHelper.areEditTextsEqual(textPassword, textPasswordConfirm);
|
||||
buttonOK[0].setEnabled(equal);
|
||||
}
|
||||
public void beforeTextChanged(CharSequence c, int start, int count, int after) { }
|
||||
public void afterTextChanged(Editable c) { }
|
||||
};
|
||||
textPassword.addTextChangedListener(watcher);
|
||||
textPasswordConfirm.addTextChangedListener(watcher);
|
||||
|
||||
alert.show();
|
||||
}
|
||||
|
||||
public static void showFingerprintDialog(Activity activity, Dialogs.SlotListener listener) {
|
||||
View view = activity.getLayoutInflater().inflate(R.layout.dialog_fingerprint, null);
|
||||
TextView textFingerprint = view.findViewById(R.id.text_fingerprint);
|
||||
SwirlView imgFingerprint = view.findViewById(R.id.img_fingerprint);
|
||||
|
||||
Cipher cipher;
|
||||
FingerprintSlot slot;
|
||||
final FingerprintUiHelper[] helper = new FingerprintUiHelper[1];
|
||||
FingerprintManager manager = FingerprintHelper.getManager(activity);
|
||||
|
||||
try {
|
||||
slot = new FingerprintSlot();
|
||||
SecretKey key = new KeyStoreHandle().generateKey(slot.getUUID().toString());
|
||||
cipher = Slot.createEncryptCipher(key);
|
||||
} catch (KeyStoreHandleException | SlotException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.register_fingerprint)
|
||||
.setView(view)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setOnDismissListener(d -> {
|
||||
helper[0].stopListening();
|
||||
})
|
||||
.show();
|
||||
|
||||
helper[0] = new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, new FingerprintUiHelper.Callback() {
|
||||
@Override
|
||||
public void onAuthenticated() {
|
||||
listener.onSlotResult(slot, cipher);
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
helper[0].startListening(new FingerprintManager.CryptoObject(cipher));
|
||||
}
|
||||
|
||||
public interface SlotListener {
|
||||
void onSlotResult(Slot slot, Cipher cipher);
|
||||
void onException(Exception e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
package me.impy.aegis.ui.dialogs;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.mattprecious.swirl.SwirlView;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import me.impy.aegis.R;
|
||||
import me.impy.aegis.crypto.KeyStoreHandle;
|
||||
import me.impy.aegis.crypto.KeyStoreHandleException;
|
||||
import me.impy.aegis.db.slots.FingerprintSlot;
|
||||
import me.impy.aegis.db.slots.Slot;
|
||||
import me.impy.aegis.db.slots.SlotException;
|
||||
import me.impy.aegis.helpers.FingerprintHelper;
|
||||
import me.impy.aegis.helpers.FingerprintUiHelper;
|
||||
|
||||
public class FingerprintDialogFragment extends SlotDialogFragment implements FingerprintUiHelper.Callback {
|
||||
private Cipher _cipher;
|
||||
private FingerprintUiHelper _helper;
|
||||
private FingerprintSlot _slot;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fingerprint, null);
|
||||
TextView textFingerprint = view.findViewById(R.id.text_fingerprint);
|
||||
SwirlView imgFingerprint = view.findViewById(R.id.img_fingerprint);
|
||||
|
||||
FingerprintManager manager = FingerprintHelper.getManager(getContext());
|
||||
try {
|
||||
_slot = new FingerprintSlot();
|
||||
SecretKey key = new KeyStoreHandle().generateKey(_slot.getUUID().toString());
|
||||
_cipher = Slot.createEncryptCipher(key);
|
||||
_helper = new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, this);
|
||||
} catch (KeyStoreHandleException | SlotException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.register_fingerprint)
|
||||
.setView(view)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (_helper != null) {
|
||||
_helper.startListening(new FingerprintManager.CryptoObject(_cipher));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (_helper != null) {
|
||||
_helper.stopListening();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticated() {
|
||||
getListener().onSlotResult(_slot, _cipher);
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
package me.impy.aegis.ui.dialogs;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import me.impy.aegis.R;
|
||||
import me.impy.aegis.db.slots.PasswordSlot;
|
||||
import me.impy.aegis.db.slots.Slot;
|
||||
import me.impy.aegis.db.slots.SlotException;
|
||||
import me.impy.aegis.helpers.EditTextHelper;
|
||||
import me.impy.aegis.ui.tasks.DerivationTask;
|
||||
|
||||
public class PasswordDialogFragment extends SlotDialogFragment {
|
||||
private Button _buttonOK;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_password, null);
|
||||
EditText textPassword = view.findViewById(R.id.text_password);
|
||||
EditText textPasswordConfirm = view.findViewById(R.id.text_password_confirm);
|
||||
|
||||
AlertDialog alert = new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.set_password)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create();
|
||||
|
||||
alert.setOnShowListener(dialog -> {
|
||||
_buttonOK = alert.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
_buttonOK.setEnabled(false);
|
||||
|
||||
// replace the default listener
|
||||
_buttonOK.setOnClickListener(v -> {
|
||||
if (!EditTextHelper.areEditTextsEqual(textPassword, textPasswordConfirm)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char[] password = EditTextHelper.getEditTextChars(textPassword);
|
||||
PasswordSlot slot = new PasswordSlot();
|
||||
DerivationTask task = new DerivationTask(getActivity(), key -> {
|
||||
Cipher cipher;
|
||||
try {
|
||||
cipher = Slot.createEncryptCipher(key);
|
||||
} catch (SlotException e) {
|
||||
getListener().onException(e);
|
||||
dialog.cancel();
|
||||
return;
|
||||
}
|
||||
getListener().onSlotResult(slot, cipher);
|
||||
dialog.dismiss();
|
||||
});
|
||||
task.execute(new DerivationTask.Params(slot, password));
|
||||
});
|
||||
});
|
||||
|
||||
TextWatcher watcher = new TextWatcher() {
|
||||
public void onTextChanged(CharSequence c, int start, int before, int count) {
|
||||
boolean equal = EditTextHelper.areEditTextsEqual(textPassword, textPasswordConfirm);
|
||||
_buttonOK.setEnabled(equal);
|
||||
}
|
||||
public void beforeTextChanged(CharSequence c, int start, int count, int after) { }
|
||||
public void afterTextChanged(Editable c) { }
|
||||
};
|
||||
textPassword.addTextChangedListener(watcher);
|
||||
textPasswordConfirm.addTextChangedListener(watcher);
|
||||
|
||||
return alert;
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package me.impy.aegis.ui.dialogs;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import me.impy.aegis.db.slots.Slot;
|
||||
|
||||
public class SlotDialogFragment extends DialogFragment {
|
||||
private Listener _listener;
|
||||
|
||||
protected Listener getListener() {
|
||||
return _listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
|
||||
try {
|
||||
_listener = (Listener) context;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(context.toString() + " must implement SlotDialogFragment.Listener");
|
||||
}
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
void onSlotResult(Slot slot, Cipher cipher);
|
||||
void onException(Exception e);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue