Merge pull request #510 from michaelschattgen/feature/pin-keyboard

Add ability to enable PIN keyboard
This commit is contained in:
Alexander Bakker 2020-08-01 13:04:12 +02:00 committed by GitHub
commit 7059f3be74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 107 additions and 9 deletions

View file

@ -168,6 +168,10 @@ public class Preferences {
return _prefs.getString("pref_backups_error", null);
}
public boolean isPinKeyboardEnabled() {
return _prefs.getBoolean("pref_pin_keyboard", false);
}
public boolean isTimeSyncWarningEnabled() {
return _prefs.getBoolean("pref_warn_time_sync", true);
}

View file

@ -5,6 +5,7 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
@ -59,6 +60,8 @@ public class AuthActivity extends AegisActivity {
private BiometricSlot _bioSlot;
private BiometricPrompt _bioPrompt;
private int _failedUnlockAttempts;
// the first time this activity is resumed after creation, it's possible to inhibit showing the
// biometric prompt by setting 'inhibitBioPrompt' to false through the intent
private boolean _inhibitBioPrompt;
@ -83,6 +86,10 @@ public class AuthActivity extends AegisActivity {
return false;
});
if (_prefs.isPinKeyboardEnabled()) {
_textPassword.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
}
Intent intent = getIntent();
_inhibitBioPrompt = savedInstanceState == null ? !intent.getBooleanExtra("_inhibitBioPrompt", true) : savedInstanceState.getBoolean("_inhibitBioPrompt");
_cancelAction = (CancelAction) intent.getSerializableExtra("cancelAction");
@ -253,6 +260,9 @@ public class AuthActivity extends AegisActivity {
}
public BiometricPrompt showBiometricPrompt() {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(_textPassword.getWindowToken(), 0);
Cipher cipher;
try {
cipher = _bioSlot.createDecryptCipher(_bioKey);
@ -301,6 +311,21 @@ public class AuthActivity extends AegisActivity {
finish();
}
private void onInvalidPassword() {
Dialogs.showSecureDialog(new AlertDialog.Builder(AuthActivity.this)
.setTitle(getString(R.string.unlock_vault_error))
.setMessage(getString(R.string.unlock_vault_error_description))
.setCancelable(false)
.setPositiveButton(android.R.string.ok, (dialog, which) -> selectPassword())
.create());
_failedUnlockAttempts ++;
if (_failedUnlockAttempts >= 3) {
_textPassword.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
}
private class PasswordDerivationListener implements PasswordSlotDecryptTask.Callback {
@Override
public void onTaskFinished(PasswordSlotDecryptTask.Result result) {
@ -316,12 +341,7 @@ public class AuthActivity extends AegisActivity {
finish(result.getKey(), result.isSlotRepaired());
} else {
Dialogs.showSecureDialog(new AlertDialog.Builder(AuthActivity.this)
.setTitle(getString(R.string.unlock_vault_error))
.setMessage(getString(R.string.unlock_vault_error_description))
.setCancelable(false)
.setPositiveButton(android.R.string.ok, (dialog, which) -> selectPassword())
.create());
onInvalidPassword();
}
}
}

View file

@ -183,7 +183,7 @@ public class Dialogs {
showSecureDialog(dialog);
}
private static void showTextInputDialog(Context context, @StringRes int titleId, @StringRes int messageId, @StringRes int hintId, TextInputListener listener, boolean isSecret) {
private static void showTextInputDialog(Context context, @StringRes int titleId, @StringRes int messageId, @StringRes int hintId, TextInputListener listener, DialogInterface.OnDismissListener dismissListener, boolean isSecret) {
View view = LayoutInflater.from(context).inflate(R.layout.dialog_text_input, null);
EditText input = view.findViewById(R.id.text_input);
if (isSecret) {
@ -198,6 +198,11 @@ public class Dialogs {
char[] text = EditTextHelper.getEditTextChars(input);
listener.onTextInputResult(text);
});
if (dismissListener != null) {
builder.setOnDismissListener(dismissListener);
}
if (messageId != 0) {
builder.setMessage(messageId);
}
@ -207,7 +212,7 @@ public class Dialogs {
}
private static void showTextInputDialog(Context context, @StringRes int titleId, @StringRes int hintId, TextInputListener listener, boolean isSecret) {
showTextInputDialog(context, titleId, 0, hintId, listener, isSecret);
showTextInputDialog(context, titleId, 0, hintId, listener, null, isSecret);
}
public static void showTextInputDialog(Context context, @StringRes int titleId, @StringRes int hintId, TextInputListener listener) {
@ -219,7 +224,11 @@ public class Dialogs {
}
public static void showPasswordInputDialog(Context context, @StringRes int messageId, TextInputListener listener) {
showTextInputDialog(context, R.string.set_password, messageId, R.string.password, listener, true);
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 showNumberPickerDialog(Activity activity, NumberInputListener listener) {

View file

@ -36,6 +36,7 @@ import com.beemdevelopment.aegis.importers.DatabaseImporterException;
import com.beemdevelopment.aegis.services.NotificationService;
import com.beemdevelopment.aegis.ui.models.ImportEntry;
import com.beemdevelopment.aegis.ui.preferences.SwitchPreference;
import com.beemdevelopment.aegis.ui.tasks.PasswordSlotDecryptTask;
import com.beemdevelopment.aegis.util.UUIDMap;
import com.beemdevelopment.aegis.vault.VaultBackupManager;
import com.beemdevelopment.aegis.vault.VaultEntry;
@ -63,6 +64,8 @@ import java.util.concurrent.atomic.AtomicReference;
import javax.crypto.Cipher;
import static android.text.TextUtils.isDigitsOnly;
public class PreferencesFragment extends PreferenceFragmentCompat {
// activity request codes
private static final int CODE_IMPORT = 0;
@ -90,6 +93,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
private Preference _slotsPreference;
private Preference _groupsPreference;
private Preference _passwordReminderPreference;
private SwitchPreferenceCompat _pinKeyboardPreference;
private SwitchPreferenceCompat _backupsPreference;
private Preference _backupsLocationPreference;
private Preference _backupsTriggerPreference;
@ -370,6 +374,32 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
}
});
_pinKeyboardPreference = findPreference("pref_pin_keyboard");
_pinKeyboardPreference.setOnPreferenceChangeListener((preference, o) -> {
if ((boolean)o) {
return true;
}
Dialogs.showPasswordInputDialog(getActivity(), R.string.set_password_confirm, R.string.pin_keyboard_description, password -> {
if (isDigitsOnly(new String(password))) {
List<PasswordSlot> slots = _vault.getCredentials().getSlots().findAll(PasswordSlot.class);
PasswordSlotDecryptTask.Params params = new PasswordSlotDecryptTask.Params(slots, password);
new PasswordSlotDecryptTask(getActivity(), new PasswordConfirmationListener()).execute(params);
} else {
setPinKeyboardPreference(false);
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
.setTitle(R.string.pin_keyboard_error)
.setMessage(R.string.pin_keyboard_error_description)
.setCancelable(false)
.setPositiveButton(android.R.string.ok, null)
.create());
}
}, dialog -> {
setPinKeyboardPreference(false);
});
return false;
});
_groupsPreference = findPreference("pref_groups");
_groupsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
@ -767,6 +797,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
_biometricsPreference.setVisible(encrypted);
_slotsPreference.setEnabled(encrypted);
_autoLockPreference.setVisible(encrypted);
_pinKeyboardPreference.setVisible(encrypted);
if (encrypted) {
SlotList slots = _vault.getCredentials().getSlots();
@ -810,6 +841,10 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
startActivityForResult(intent, CODE_BACKUPS);
}
private void setPinKeyboardPreference(boolean enable) {
_pinKeyboardPreference.setChecked(enable);
}
private class SetPasswordListener implements Dialogs.SlotListener {
@Override
public void onSlotResult(Slot slot, Cipher cipher) {
@ -895,4 +930,21 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
Dialogs.showErrorDialog(getContext(), R.string.encryption_set_password_error, e);
}
}
private class PasswordConfirmationListener implements PasswordSlotDecryptTask.Callback {
@Override
public void onTaskFinished(PasswordSlotDecryptTask.Result result) {
if (result != null) {
setPinKeyboardPreference(true);
} else {
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
.setTitle(R.string.pin_keyboard_error)
.setMessage(R.string.invalid_password)
.setCancelable(false)
.setPositiveButton(android.R.string.ok, null)
.create());
setPinKeyboardPreference(false);
}
}
}
}