mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-26 16:56:09 +00:00
Merge pull request #510 from michaelschattgen/feature/pin-keyboard
Add ability to enable PIN keyboard
This commit is contained in:
commit
7059f3be74
6 changed files with 107 additions and 9 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
<string name="set_group">Please enter a group name</string>
|
||||
<string name="set_number">Please enter a number</string>
|
||||
<string name="set_password_confirm">Please confirm the password</string>
|
||||
<string name="invalid_password">The password is incorrect</string>
|
||||
<string name="invalidated_biometrics">A change in your device\'s security settings has been detected. Please go to \"Aegis -> Settings -> Biometrics\" and re-enable biometric unlock.</string>
|
||||
<string name="password_reminder">It\'s been a while since you\'ve entered your password. Do you still remember it?</string>
|
||||
<string name="enter_password_authy_message">It looks like your Authy tokens are encrypted. Please close Aegis, open Authy and unlock the tokens with your password. Instead, Aegis can also attempt to decrypt your Authy tokens for you, if you enter your password below.</string>
|
||||
|
@ -203,6 +204,9 @@
|
|||
<string name="pref_highlight_entry_summary">Make tokens easier to distinguish from each other by temporarily highlighting them when tapped</string>
|
||||
<string name="pref_copy_on_tap_title">Copy tokens when tapped</string>
|
||||
<string name="pref_copy_on_tap_summary">Copy tokens to the clipboard by tapping them</string>
|
||||
<string name="pin_keyboard_description">Enter your password to enable the PIN keyboard. Note that this only works if your password only consists of numbers</string>
|
||||
<string name="pin_keyboard_error">Error enabling PIN keyboard</string>
|
||||
<string name="pin_keyboard_error_description">It\'s not possible to set PIN keyboard. Your password must only consists of numbers.</string>
|
||||
<string name="selected">Selected</string>
|
||||
<string name="dark_theme_title">Dark theme</string>
|
||||
<string name="light_theme_title">Light theme</string>
|
||||
|
@ -281,4 +285,6 @@
|
|||
<string name="password_strength_fair">Fair</string>
|
||||
<string name="password_strength_good">Good</string>
|
||||
<string name="password_strength_strong">Strong</string>
|
||||
<string name="pref_pin_keyboard_title">Use PIN keyboard on lockscreen</string>
|
||||
<string name="pref_pin_keyboard_summary">Enable this if you want to enable the PIN keyboard on the lockscreen. This only works for numeric passwords</string>
|
||||
</resources>
|
||||
|
|
|
@ -130,6 +130,13 @@
|
|||
android:dependency="pref_biometrics"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:key="pref_pin_keyboard"
|
||||
android:dependency="pref_encryption"
|
||||
android:title="@string/pref_pin_keyboard_title"
|
||||
android:summary="@string/pref_pin_keyboard_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<androidx.preference.SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="pref_auto_lock"
|
||||
|
|
Loading…
Add table
Reference in a new issue