allow to disable fingerprint unlock (in QuickUnlock mode) after three failed attempts, closes #16
This commit is contained in:
@@ -119,6 +119,7 @@ namespace keepass2android
|
||||
{
|
||||
void OnBiometricAuthSucceeded();
|
||||
void OnBiometricError(string toString);
|
||||
void OnBiometricAttemptFailed(string message);
|
||||
}
|
||||
|
||||
public class BiometricModule
|
||||
@@ -234,6 +235,7 @@ namespace keepass2android
|
||||
|
||||
private BiometricPrompt _biometricPrompt;
|
||||
private FragmentActivity _activity;
|
||||
private BiometricAuthCallbackAdapter _biometricAuthCallbackAdapter;
|
||||
|
||||
public BiometricCrypt(BiometricModule biometric, string keyId)
|
||||
{
|
||||
@@ -261,13 +263,14 @@ namespace keepass2android
|
||||
|
||||
public void StartListening(IBiometricAuthCallback callback)
|
||||
{
|
||||
|
||||
StartListening(new BiometricAuthCallbackAdapter(callback, _activity));
|
||||
_biometricAuthCallbackAdapter = new BiometricAuthCallbackAdapter(callback, _activity);
|
||||
StartListening(_biometricAuthCallbackAdapter);
|
||||
}
|
||||
|
||||
public void StopListening()
|
||||
{
|
||||
|
||||
_biometricAuthCallbackAdapter.IgnoreNextError();
|
||||
_biometricPrompt.CancelAuthentication();
|
||||
}
|
||||
|
||||
public bool HasUserInterface
|
||||
@@ -520,6 +523,7 @@ namespace keepass2android
|
||||
{
|
||||
private readonly IBiometricAuthCallback _callback;
|
||||
private readonly Context _context;
|
||||
private bool _ignoreNextError;
|
||||
|
||||
public BiometricAuthCallbackAdapter(IBiometricAuthCallback callback, Context context)
|
||||
{
|
||||
@@ -535,16 +539,24 @@ namespace keepass2android
|
||||
|
||||
public override void OnAuthenticationError(int errorCode, ICharSequence errString)
|
||||
{
|
||||
|
||||
if (_ignoreNextError)
|
||||
{
|
||||
_ignoreNextError = false;
|
||||
return;
|
||||
}
|
||||
new Handler(Looper.MainLooper).Post(() => _callback.OnBiometricError(errString.ToString()));
|
||||
}
|
||||
|
||||
|
||||
public override void OnAuthenticationFailed()
|
||||
{
|
||||
new Handler(Looper.MainLooper).Post(() => _callback.OnBiometricError(_context.Resources.GetString(Resource.String.fingerprint_not_recognized)));
|
||||
new Handler(Looper.MainLooper).Post(() => _callback.OnBiometricAttemptFailed(_context.Resources.GetString(Resource.String.fingerprint_not_recognized)));
|
||||
}
|
||||
|
||||
public void IgnoreNextError()
|
||||
{
|
||||
_ignoreNextError = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -120,26 +120,27 @@ namespace keepass2android
|
||||
|
||||
FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
|
||||
FindViewById<CheckBox>(Resource.Id.show_keyboard_while_fingerprint).Checked =
|
||||
Util.GetShowKeyboardDuringFingerprintUnlock(this);
|
||||
|
||||
FindViewById<CheckBox>(Resource.Id.show_keyboard_while_fingerprint).CheckedChange += (sender, args) =>
|
||||
{
|
||||
PreferenceManager.GetDefaultSharedPreferences(this)
|
||||
.Edit()
|
||||
.PutBoolean(GetString(Resource.String.ShowKeyboardWhileFingerprint_key), args.IsChecked)
|
||||
.Commit();
|
||||
};
|
||||
|
||||
|
||||
UpdateKeyboardCheckboxVisibility();
|
||||
|
||||
|
||||
}
|
||||
FindViewById<CheckBox>(Resource.Id.close_database_after_failed).Checked =
|
||||
Util.GetCloseDatabaseAfterFailedBiometricQuickUnlock(this);
|
||||
|
||||
private void UpdateKeyboardCheckboxVisibility()
|
||||
FindViewById<CheckBox>(Resource.Id.close_database_after_failed).CheckedChange += (sender, args) =>
|
||||
{
|
||||
PreferenceManager.GetDefaultSharedPreferences(this)
|
||||
.Edit()
|
||||
.PutBoolean(GetString(Resource.String.CloseDatabaseAfterFailedBiometricQuickUnlock_key), args.IsChecked)
|
||||
.Commit();
|
||||
};
|
||||
|
||||
|
||||
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility()
|
||||
{
|
||||
FindViewById(Resource.Id.show_keyboard_while_fingerprint).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.close_database_after_failed).Visibility = _unlockMode == FingerprintUnlockMode.QuickUnlock ? ViewStates.Visible : ViewStates.Gone;
|
||||
}
|
||||
|
||||
private bool TrySetupSamsung()
|
||||
@@ -249,7 +250,7 @@ namespace keepass2android
|
||||
if (_samsungBiometry != null)
|
||||
{
|
||||
_unlockMode = newMode;
|
||||
UpdateKeyboardCheckboxVisibility();
|
||||
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
|
||||
|
||||
ISharedPreferencesEditor edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit();
|
||||
edit.PutString(App.Kp2a.CurrentDb.CurrentFingerprintModePrefKey, _unlockMode.ToString());
|
||||
@@ -260,17 +261,17 @@ namespace keepass2android
|
||||
if (newMode == FingerprintUnlockMode.Disabled)
|
||||
{
|
||||
_unlockMode = newMode;
|
||||
UpdateKeyboardCheckboxVisibility();
|
||||
|
||||
StoreUnlockMode();
|
||||
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
|
||||
|
||||
StoreUnlockMode();
|
||||
return;
|
||||
}
|
||||
|
||||
_desiredUnlockMode = newMode;
|
||||
FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.show_keyboard_while_fingerprint).Visibility = ViewStates.Gone;
|
||||
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
|
||||
|
||||
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Visible;
|
||||
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Visible;
|
||||
try
|
||||
{
|
||||
_enc = new BiometricEncryption(new BiometricModule(this), CurrentPreferenceKey);
|
||||
@@ -312,7 +313,7 @@ namespace keepass2android
|
||||
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
|
||||
|
||||
StoreUnlockMode();
|
||||
UpdateKeyboardCheckboxVisibility();
|
||||
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
|
||||
|
||||
|
||||
}, SUCCESS_DELAY_MILLIS);
|
||||
@@ -332,7 +333,12 @@ namespace keepass2android
|
||||
_fpTextView.PostDelayed(ResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS);
|
||||
}
|
||||
|
||||
void ResetErrorTextRunnable()
|
||||
public void OnBiometricAttemptFailed(string message)
|
||||
{
|
||||
//ignore
|
||||
}
|
||||
|
||||
void ResetErrorTextRunnable()
|
||||
{
|
||||
_fpTextView.SetTextColor(
|
||||
_fpTextView.Resources.GetColor(Resource.Color.hint_color, null));
|
||||
@@ -351,7 +357,7 @@ namespace keepass2android
|
||||
//seems like not all Samsung Devices (e.g. Note 4) don't support the Android 6 fingerprint API
|
||||
if (!TrySetupSamsung())
|
||||
SetError(Resource.String.fingerprint_hardware_error);
|
||||
UpdateKeyboardCheckboxVisibility();
|
||||
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
|
||||
return;
|
||||
}
|
||||
if (!fpModule.IsAvailable)
|
||||
@@ -360,8 +366,8 @@ namespace keepass2android
|
||||
return;
|
||||
}
|
||||
ShowRadioButtons();
|
||||
UpdateKeyboardCheckboxVisibility();
|
||||
|
||||
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
|
||||
|
||||
}
|
||||
|
||||
protected override void OnPause()
|
||||
|
||||
@@ -928,7 +928,12 @@ namespace keepass2android
|
||||
Toast.MakeText(this, message, ToastLength.Long).Show();
|
||||
}
|
||||
|
||||
public void OnBiometricAuthSucceeded()
|
||||
public void OnBiometricAttemptFailed(string message)
|
||||
{
|
||||
//ignore
|
||||
}
|
||||
|
||||
public void OnBiometricAuthSucceeded()
|
||||
{
|
||||
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
|
||||
|
||||
@@ -1761,17 +1766,9 @@ namespace keepass2android
|
||||
}
|
||||
else
|
||||
{
|
||||
bool showKeyboard = true;
|
||||
|
||||
|
||||
bool showKeyboard = (Util.GetShowKeyboardDuringFingerprintUnlock(this));
|
||||
|
||||
|
||||
if (!fingerprintInitialized)
|
||||
showKeyboard = true;
|
||||
|
||||
|
||||
|
||||
|
||||
EditText pwd = (EditText) FindViewById(Resource.Id.password_edit);
|
||||
pwd.PostDelayed(() =>
|
||||
{
|
||||
|
||||
@@ -48,7 +48,10 @@ namespace keepass2android
|
||||
private int _quickUnlockLength;
|
||||
private const int FingerprintPermissionRequestCode = 0;
|
||||
|
||||
public QuickUnlock()
|
||||
private int numFailedAttempts = 0;
|
||||
private int maxNumFailedAttempts = int.MaxValue;
|
||||
|
||||
public QuickUnlock()
|
||||
{
|
||||
_design = new ActivityDesign(this);
|
||||
}
|
||||
@@ -63,7 +66,9 @@ namespace keepass2android
|
||||
|
||||
_ioc = App.Kp2a.GetDbForQuickUnlock()?.Ioc;
|
||||
|
||||
if (_ioc == null)
|
||||
|
||||
|
||||
if (_ioc == null)
|
||||
{
|
||||
Finish();
|
||||
return;
|
||||
@@ -147,11 +152,23 @@ namespace keepass2android
|
||||
|
||||
Util.SetNoPersonalizedLearning(FindViewById<EditText>(Resource.Id.QuickUnlock_password));
|
||||
|
||||
if (bundle != null)
|
||||
numFailedAttempts = bundle.GetInt(NumFailedAttemptsKey, 0);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
private const string NumFailedAttemptsKey = "FailedAttempts";
|
||||
|
||||
protected override void OnSaveInstanceState(Bundle outState)
|
||||
{
|
||||
base.OnSaveInstanceState(outState);
|
||||
outState.PutInt(NumFailedAttemptsKey, numFailedAttempts);
|
||||
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
DonateReminder.ShowDonateReminderIfAppropriate(this);
|
||||
@@ -174,7 +191,18 @@ namespace keepass2android
|
||||
Toast.MakeText(this, message, ToastLength.Long).Show();
|
||||
}
|
||||
|
||||
public void OnBiometricAuthSucceeded()
|
||||
|
||||
public void OnBiometricAttemptFailed(string message)
|
||||
{
|
||||
numFailedAttempts++;
|
||||
if (numFailedAttempts >= maxNumFailedAttempts)
|
||||
{
|
||||
FindViewById<ImageButton>(Resource.Id.fingerprintbtn).Visibility = ViewStates.Gone;
|
||||
_biometryIdentifier.StopListening();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBiometricAuthSucceeded()
|
||||
{
|
||||
Kp2aLog.Log("OnFingerprintAuthSucceeded");
|
||||
_biometryIdentifier.StopListening();
|
||||
@@ -218,7 +246,14 @@ namespace keepass2android
|
||||
return false;
|
||||
}
|
||||
|
||||
BiometricModule fpModule = new BiometricModule(this);
|
||||
|
||||
|
||||
if (um == FingerprintUnlockMode.QuickUnlock && Util.GetCloseDatabaseAfterFailedBiometricQuickUnlock(this))
|
||||
{
|
||||
maxNumFailedAttempts = 3;
|
||||
}
|
||||
|
||||
BiometricModule fpModule = new BiometricModule(this);
|
||||
Kp2aLog.Log("fpModule.IsHardwareAvailable=" + fpModule.IsHardwareAvailable);
|
||||
if (fpModule.IsHardwareAvailable) //see FingerprintSetupActivity
|
||||
_biometryIdentifier = new BiometricDecryption(fpModule, App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintPrefKey, this,
|
||||
@@ -232,9 +267,15 @@ namespace keepass2android
|
||||
_biometryIdentifier = new BiometrySamsungIdentifier(this);
|
||||
btn.Click += (sender, args) =>
|
||||
{
|
||||
if (_biometryIdentifier.Init())
|
||||
_biometryIdentifier.StartListening(this);
|
||||
};
|
||||
if (_biometryIdentifier.Init())
|
||||
{
|
||||
if (numFailedAttempts < maxNumFailedAttempts)
|
||||
{
|
||||
_biometryIdentifier.StartListening(this);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
Kp2aLog.Log("trying Samsung Fingerprint API...Seems to work!");
|
||||
}
|
||||
catch (Exception)
|
||||
@@ -330,8 +371,9 @@ namespace keepass2android
|
||||
|
||||
CheckIfUnloaded();
|
||||
|
||||
InitFingerprintUnlock();
|
||||
|
||||
bool showKeyboard = ((!InitFingerprintUnlock()) || (Util.GetShowKeyboardDuringFingerprintUnlock(this)));
|
||||
bool showKeyboard = true;
|
||||
|
||||
EditText pwd = (EditText)FindViewById(Resource.Id.QuickUnlock_password);
|
||||
pwd.PostDelayed(() =>
|
||||
@@ -347,7 +389,7 @@ namespace keepass2android
|
||||
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
|
||||
btn.Click += (sender, args) =>
|
||||
{
|
||||
if (_biometryIdentifier.HasUserInterface|| string.IsNullOrEmpty((string)btn.Tag) )
|
||||
if ((_biometryIdentifier != null) && ((_biometryIdentifier.HasUserInterface)|| string.IsNullOrEmpty((string)btn.Tag) ))
|
||||
{
|
||||
_biometryIdentifier.StartListening(this);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="fill_parent"
|
||||
@@ -68,11 +68,11 @@
|
||||
android:background="?android:attr/selectableItemBackground" />
|
||||
</LinearLayout>
|
||||
<CheckBox
|
||||
android:id="@+id/show_keyboard_while_fingerprint"
|
||||
android:id="@+id/close_database_after_failed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:text="@string/ShowKeyboardDuringFingerprintAuth" />
|
||||
android:text="@string/CloseDbAfterFailedAttempts" />
|
||||
</LinearLayout>
|
||||
<RelativeLayout
|
||||
android:id="@+id/fingerprint_auth_container"
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
<bool name="ShowUsernameInList_default">true</bool>
|
||||
<bool name="ShowGroupnameInSearchResult_default">true</bool>
|
||||
<string name="ViewDatabaseSecure_key">ViewDatabaseSecure</string>
|
||||
<string name="ShowKeyboardWhileFingerprint_key">ShowKeyboardWhileFingerprint_key</string>
|
||||
<string name="CloseDatabaseAfterFailedBiometricQuickUnlock_key">CloseDatabaseAfterFailedBiometricQuickUnlock_key</string>
|
||||
<bool name="RememberRecentFiles_default">true</bool>
|
||||
<string name="TrayTotp_SettingsField_key">TrayTotp_SettingsField_key</string>
|
||||
<string name="TrayTotp_SeedField_key">TrayTotp_SeedField_key</string>
|
||||
|
||||
@@ -779,7 +779,7 @@
|
||||
<string name="EntryChannel_name">Entry notifications</string>
|
||||
<string name="EntryChannel_desc">Notification to simplify access to the currently selected entry.</string>
|
||||
|
||||
<string name="ShowKeyboardDuringFingerprintAuth">Show soft keyboard for password input when biometric authentication is active.</string>
|
||||
<string name="CloseDbAfterFailedAttempts">Close database after three failed biometric unlock attempts.</string>
|
||||
|
||||
<string-array name="ChangeLog_1_08">
|
||||
<item>Add notification button for copying TOTP to clipboard</item>
|
||||
|
||||
@@ -541,15 +541,19 @@ namespace keepass2android
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetShowKeyboardDuringFingerprintUnlock(Context ctx)
|
||||
{
|
||||
return (PreferenceManager.GetDefaultSharedPreferences(ctx).GetBoolean(
|
||||
ctx.GetString(Resource.String.ShowKeyboardWhileFingerprint_key), true));
|
||||
|
||||
}
|
||||
|
||||
public static bool GetCloseDatabaseAfterFailedBiometricQuickUnlock(Context ctx)
|
||||
{
|
||||
return (PreferenceManager.GetDefaultSharedPreferences(ctx).GetBoolean(
|
||||
ctx.GetString(Resource.String.CloseDatabaseAfterFailedBiometricQuickUnlock_key), true));
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void MoveBottomBarButtons(int btn1Id, int btn2Id, int bottomBarId, Activity context)
|
||||
|
||||
|
||||
|
||||
public static void MoveBottomBarButtons(int btn1Id, int btn2Id, int bottomBarId, Activity context)
|
||||
{
|
||||
var btn1 = context.FindViewById<Button>(btn1Id);
|
||||
var btn2 = context.FindViewById<Button>(btn2Id);
|
||||
|
||||
@@ -44,8 +44,6 @@ namespace keepass2android.view
|
||||
get { return base.Activated; }
|
||||
set
|
||||
{
|
||||
Log.Debug("KP2A", "Setting activated = " + value);
|
||||
|
||||
if (value)
|
||||
{
|
||||
FindViewById(Resource.Id.icon).Visibility = ViewStates.Invisible;
|
||||
|
||||
Reference in New Issue
Block a user