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)
This commit is contained in:
Alexander Bakker 2020-01-19 13:53:08 +01:00
parent 7f1ce1e645
commit fa799e9542
6 changed files with 90 additions and 10 deletions

View file

@ -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);
}

View file

@ -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));

View file

@ -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);
}
}

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/cardBackground">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="@string/password_reminder"/>
</LinearLayout>

View file

@ -32,6 +32,8 @@
<string name="pref_import_app_summary">Import tokens from an app (requires root access)</string>
<string name="pref_export_title">Export</string>
<string name="pref_export_summary">Export the vault</string>
<string name="pref_password_reminder_title">Password reminder</string>
<string name="pref_password_reminder_summary">Show a reminder to enter the password every once in a while, so that you don\'t forget it.</string>
<string name="pref_secure_screen_title">Screen security</string>
<string name="pref_secure_screen_summary">Block screenshots and other attempts to capture the screen within the app</string>
<string name="pref_tap_to_reveal_title">Tap to reveal</string>
@ -63,6 +65,7 @@
<string name="set_number">Please enter a number</string>
<string name="set_password_confirm">Please confirm the password</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="unlock">Unlock</string>
<string name="biometrics">Biometrics</string>

View file

@ -109,6 +109,14 @@
android:persistent="false"
app:iconSpaceReserved="false"/>
<androidx.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="pref_password_reminder"
android:title="@string/pref_password_reminder_title"
android:summary="@string/pref_password_reminder_summary"
android:dependency="pref_biometrics"
app:iconSpaceReserved="false"/>
<androidx.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="pref_auto_lock"