From fa799e95424b101268719da064b47fd124f1b334 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Sun, 19 Jan 2020 13:53:08 +0100 Subject: [PATCH] Remind users who use biometrics to enter their password periodically Instead of showing the reminder after x unlocks, I decided to show the reminder 2 weeks after the vault was last unlocked with the password. Let me know if you agree with that. ![](https://alexbakker.me/u/115z6be7go.png) --- .../beemdevelopment/aegis/Preferences.java | 24 +++++++++ .../aegis/ui/AuthActivity.java | 49 +++++++++++++++---- .../aegis/ui/PreferencesFragment.java | 4 ++ app/src/main/res/layout/popup_password.xml | 12 +++++ app/src/main/res/values/strings.xml | 3 ++ app/src/main/res/xml/preferences.xml | 8 +++ 6 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 app/src/main/res/layout/popup_password.xml diff --git a/app/src/main/java/com/beemdevelopment/aegis/Preferences.java b/app/src/main/java/com/beemdevelopment/aegis/Preferences.java index 304474f2..5ff095d1 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/Preferences.java +++ b/app/src/main/java/com/beemdevelopment/aegis/Preferences.java @@ -6,13 +6,19 @@ import android.content.res.Resources; import android.os.Build; import android.preference.PreferenceManager; +import java.util.Date; import java.util.Locale; +import java.util.concurrent.TimeUnit; public class Preferences { private SharedPreferences _prefs; public Preferences(Context context) { _prefs = PreferenceManager.getDefaultSharedPreferences(context); + + if (getPasswordReminderTimestamp().getTime() == 0) { + resetPasswordReminderTimestamp(); + } } public boolean isTapToRevealEnabled() { @@ -32,6 +38,24 @@ public class Preferences { return _prefs.getBoolean("pref_secure_screen", !BuildConfig.DEBUG); } + public boolean isPasswordReminderEnabled() { + return _prefs.getBoolean("pref_password_reminder", true); + } + + public boolean isPasswordReminderNeeded() { + long diff = new Date().getTime() - getPasswordReminderTimestamp().getTime(); + long days = TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS); + return isPasswordReminderEnabled() && days >= 7; + } + + public Date getPasswordReminderTimestamp() { + return new Date(_prefs.getLong("pref_password_reminder_counter", 0)); + } + + public void resetPasswordReminderTimestamp() { + _prefs.edit().putLong("pref_password_reminder_counter", new Date().getTime()).apply(); + } + public boolean isAccountNameVisible() { return _prefs.getBoolean("pref_account_name", true); } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/AuthActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/AuthActivity.java index 8d9af242..aab23e70 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/AuthActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/AuthActivity.java @@ -2,15 +2,18 @@ package com.beemdevelopment.aegis.ui; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.view.KeyEvent; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.Toast; import androidx.annotation.NonNull; @@ -18,19 +21,20 @@ import androidx.appcompat.app.AlertDialog; import androidx.biometric.BiometricPrompt; import com.beemdevelopment.aegis.CancelAction; +import com.beemdevelopment.aegis.Preferences; import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.crypto.KeyStoreHandle; import com.beemdevelopment.aegis.crypto.KeyStoreHandleException; +import com.beemdevelopment.aegis.helpers.BiometricsHelper; +import com.beemdevelopment.aegis.helpers.EditTextHelper; +import com.beemdevelopment.aegis.helpers.UiThreadExecutor; +import com.beemdevelopment.aegis.ui.tasks.SlotListTask; import com.beemdevelopment.aegis.vault.VaultFileCredentials; import com.beemdevelopment.aegis.vault.slots.BiometricSlot; import com.beemdevelopment.aegis.vault.slots.PasswordSlot; import com.beemdevelopment.aegis.vault.slots.Slot; import com.beemdevelopment.aegis.vault.slots.SlotException; import com.beemdevelopment.aegis.vault.slots.SlotList; -import com.beemdevelopment.aegis.helpers.BiometricsHelper; -import com.beemdevelopment.aegis.helpers.EditTextHelper; -import com.beemdevelopment.aegis.helpers.UiThreadExecutor; -import com.beemdevelopment.aegis.ui.tasks.SlotListTask; import javax.crypto.Cipher; import javax.crypto.SecretKey; @@ -43,9 +47,12 @@ public class AuthActivity extends AegisActivity implements SlotListTask.Callback private BiometricPrompt.CryptoObject _bioCryptoObj; private BiometricPrompt _bioPrompt; + private Preferences _prefs; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + _prefs = new Preferences(this); setContentView(R.layout.activity_auth); _textPassword = findViewById(R.id.text_password); LinearLayout boxBiometricInfo = findViewById(R.id.box_biometric_info); @@ -112,11 +119,6 @@ public class AuthActivity extends AegisActivity implements SlotListTask.Callback biometricsButton.setOnClickListener(v -> { showBiometricPrompt(); }); - - if (_bioCryptoObj == null) { - _textPassword.requestFocus(); - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - } } private void showError() { @@ -158,10 +160,33 @@ public class AuthActivity extends AegisActivity implements SlotListTask.Callback super.onResume(); if (_bioPrompt != null) { - showBiometricPrompt(); + if (_prefs.isPasswordReminderNeeded()) { + focusPasswordField(); + showPasswordReminder(); + } else { + showBiometricPrompt(); + } + } else { + focusPasswordField(); } } + private void focusPasswordField() { + _textPassword.requestFocus(); + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + } + + private void showPasswordReminder() { + View popupLayout = getLayoutInflater().inflate(R.layout.popup_password, null); + PopupWindow popup = new PopupWindow(popupLayout, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + popup.setFocusable(true); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ + popup.setElevation(5.0f); + } + _textPassword.post(() -> popup.showAsDropDown(_textPassword)); + _textPassword.postDelayed(popup::dismiss, 5000); + } + public void showBiometricPrompt() { BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder() .setTitle(getString(R.string.authentication)) @@ -187,6 +212,10 @@ public class AuthActivity extends AegisActivity implements SlotListTask.Callback _slots.replace(result.getSlot()); } + if (result.getSlot().getType() == Slot.TYPE_DERIVED) { + _prefs.resetPasswordReminderTimestamp(); + } + // send the master key back to the main activity Intent intent = new Intent(); intent.putExtra("creds", new VaultFileCredentials(result.getKey(), _slots)); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/PreferencesFragment.java b/app/src/main/java/com/beemdevelopment/aegis/ui/PreferencesFragment.java index cbe23e91..9658204a 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/PreferencesFragment.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/PreferencesFragment.java @@ -85,6 +85,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat { private Preference _setPasswordPreference; private Preference _slotsPreference; private Preference _groupsPreference; + private Preference _passwordReminderPreference; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @@ -354,6 +355,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat { }); _autoLockPreference = findPreference("pref_auto_lock"); + _passwordReminderPreference = findPreference("pref_password_reminder"); } @Override @@ -683,11 +685,13 @@ public class PreferencesFragment extends PreferenceFragmentCompat { _biometricsPreference.setEnabled(canUseBio && !multiBio); _biometricsPreference.setChecked(slots.has(BiometricSlot.class), true); _slotsPreference.setVisible(showSlots); + _passwordReminderPreference.setVisible(slots.has(BiometricSlot.class)); } else { _setPasswordPreference.setEnabled(false); _biometricsPreference.setEnabled(false); _biometricsPreference.setChecked(false, true); _slotsPreference.setVisible(false); + _passwordReminderPreference.setVisible(false); } } diff --git a/app/src/main/res/layout/popup_password.xml b/app/src/main/res/layout/popup_password.xml new file mode 100644 index 00000000..6e05cb7f --- /dev/null +++ b/app/src/main/res/layout/popup_password.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6ebd9cce..ada120c2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,6 +32,8 @@ Import tokens from an app (requires root access) Export Export the vault + Password reminder + Show a reminder to enter the password every once in a while, so that you don\'t forget it. Screen security Block screenshots and other attempts to capture the screen within the app Tap to reveal @@ -63,6 +65,7 @@ Please enter a number Please confirm the password A change in your device\'s security settings has been detected. Please go to \"Aegis -> Settings -> Biometrics\" and re-enable biometric unlock. + It\'s been a while since you\'ve entered your password. Do you still remember it? Unlock Biometrics diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 5d87a1ac..cc6d0f02 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -109,6 +109,14 @@ android:persistent="false" app:iconSpaceReserved="false"/> + +