mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-04 20:30:36 +00:00
Improve auto lock and make it more customizable
This patch makes the auto lock option more customizable. Users can now choose a combination of the following: Locking Aegis when - The back button is pressed - The app is minimized - The device is locked <img src="https://alexbakker.me/u/rlj4y2u8pk.png" width="300">
This commit is contained in:
parent
7d38bc9b71
commit
d875cb6baa
28 changed files with 146 additions and 88 deletions
|
@ -12,7 +12,12 @@ import android.content.pm.ShortcutManager;
|
|||
import android.graphics.drawable.Icon;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.LifecycleEventObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
|
||||
import com.beemdevelopment.aegis.services.NotificationService;
|
||||
import com.beemdevelopment.aegis.ui.MainActivity;
|
||||
|
@ -53,6 +58,9 @@ public class AegisApplication extends Application {
|
|||
intentFilter.addAction(CODE_LOCK_VAULT_ACTION);
|
||||
registerReceiver(receiver, intentFilter);
|
||||
|
||||
// lock the app if the user moves the application to the background
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(new AppLifecycleObserver());
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||
initAppShortcuts();
|
||||
}
|
||||
|
@ -111,8 +119,8 @@ public class AegisApplication extends Application {
|
|||
return _prefs;
|
||||
}
|
||||
|
||||
public boolean isAutoLockEnabled() {
|
||||
return _prefs.isAutoLockEnabled() && !isVaultLocked() && _manager.isEncryptionEnabled() ;
|
||||
public boolean isAutoLockEnabled(int autoLockType) {
|
||||
return _prefs.isAutoLockTypeEnabled(autoLockType) && !isVaultLocked() && _manager.isEncryptionEnabled();
|
||||
}
|
||||
|
||||
public void registerLockListener(LockListener listener) {
|
||||
|
@ -123,10 +131,14 @@ public class AegisApplication extends Application {
|
|||
_lockListeners.remove(listener);
|
||||
}
|
||||
|
||||
public void lock() {
|
||||
/**
|
||||
* Locks the vault and the app.
|
||||
* @param userInitiated whether or not the user initiated the lock in MainActivity.
|
||||
*/
|
||||
public void lock(boolean userInitiated) {
|
||||
_manager = null;
|
||||
for (LockListener listener : _lockListeners) {
|
||||
listener.onLocked();
|
||||
listener.onLocked(userInitiated);
|
||||
}
|
||||
|
||||
stopService(new Intent(AegisApplication.this, NotificationService.class));
|
||||
|
@ -168,16 +180,29 @@ public class AegisApplication extends Application {
|
|||
}
|
||||
}
|
||||
|
||||
public class ScreenOffReceiver extends BroadcastReceiver {
|
||||
private class AppLifecycleObserver implements LifecycleEventObserver {
|
||||
@Override
|
||||
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
|
||||
if (event == Lifecycle.Event.ON_STOP && isAutoLockEnabled(Preferences.AUTO_LOCK_ON_MINIMIZE)) {
|
||||
lock(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ScreenOffReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (isAutoLockEnabled()) {
|
||||
lock();
|
||||
if (isAutoLockEnabled(Preferences.AUTO_LOCK_ON_DEVICE_LOCK)) {
|
||||
lock(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface LockListener {
|
||||
void onLocked();
|
||||
/**
|
||||
* When called, the app/vault has been locked and the listener should perform its cleanup operations.
|
||||
* @param userInitiated whether or not the user initiated the lock in MainActivity.
|
||||
*/
|
||||
void onLocked(boolean userInitiated);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package com.beemdevelopment.aegis;
|
||||
|
||||
public enum CancelAction {
|
||||
KILL,
|
||||
CLOSE
|
||||
}
|
||||
|
|
@ -12,6 +12,17 @@ import java.util.Locale;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Preferences {
|
||||
public static final int AUTO_LOCK_OFF = 1 << 0;
|
||||
public static final int AUTO_LOCK_ON_BACK_BUTTON = 1 << 1;
|
||||
public static final int AUTO_LOCK_ON_MINIMIZE = 1 << 2;
|
||||
public static final int AUTO_LOCK_ON_DEVICE_LOCK = 1 << 3;
|
||||
|
||||
public static final int[] AUTO_LOCK_SETTINGS = {
|
||||
AUTO_LOCK_ON_BACK_BUTTON,
|
||||
AUTO_LOCK_ON_MINIMIZE,
|
||||
AUTO_LOCK_ON_DEVICE_LOCK
|
||||
};
|
||||
|
||||
private SharedPreferences _prefs;
|
||||
|
||||
public Preferences(Context context) {
|
||||
|
@ -73,8 +84,25 @@ public class Preferences {
|
|||
return _prefs.getBoolean("pref_intro", false);
|
||||
}
|
||||
|
||||
private int getAutoLockMask() {
|
||||
final int def = AUTO_LOCK_ON_BACK_BUTTON | AUTO_LOCK_ON_DEVICE_LOCK;
|
||||
if (!_prefs.contains("pref_auto_lock_mask")) {
|
||||
return _prefs.getBoolean("pref_auto_lock", true) ? def : AUTO_LOCK_OFF;
|
||||
}
|
||||
|
||||
return _prefs.getInt("pref_auto_lock_mask", def);
|
||||
}
|
||||
|
||||
public boolean isAutoLockEnabled() {
|
||||
return _prefs.getBoolean("pref_auto_lock", true);
|
||||
return getAutoLockMask() != AUTO_LOCK_OFF;
|
||||
}
|
||||
|
||||
public boolean isAutoLockTypeEnabled(int autoLockType) {
|
||||
return (getAutoLockMask() & autoLockType) == autoLockType;
|
||||
}
|
||||
|
||||
public void setAutoLockMask(int autoLock) {
|
||||
_prefs.edit().putInt("pref_auto_lock_mask", autoLock).apply();
|
||||
}
|
||||
|
||||
public void setIntroDone(boolean done) {
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.os.Bundle;
|
|||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.beemdevelopment.aegis.AegisApplication;
|
||||
|
@ -20,9 +19,7 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
|
||||
public abstract class AegisActivity extends AppCompatActivity implements AegisApplication.LockListener {
|
||||
private boolean _resumed;
|
||||
private AegisApplication _app;
|
||||
private Theme _configuredTheme;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -39,6 +36,7 @@ public abstract class AegisActivity extends AppCompatActivity implements AegisAp
|
|||
Intent intent = new Intent(this, MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -58,24 +56,9 @@ public abstract class AegisActivity extends AppCompatActivity implements AegisAp
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
_resumed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
_resumed = false;
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void onLocked() {
|
||||
if (isOrphan()) {
|
||||
setResult(RESULT_CANCELED, null);
|
||||
finish();
|
||||
}
|
||||
public void onLocked(boolean userInitiated) {
|
||||
setResult(RESULT_CANCELED, null);
|
||||
finishAndRemoveTask();
|
||||
}
|
||||
|
||||
protected AegisApplication getApp() {
|
||||
|
@ -139,13 +122,6 @@ public abstract class AegisActivity extends AppCompatActivity implements AegisAp
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports whether this Activity has been resumed. (i.e. onResume was called)
|
||||
*/
|
||||
protected boolean isOpen() {
|
||||
return _resumed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports whether this Activity instance has become an orphan. This can happen if
|
||||
* the vault was locked by an external trigger while the Activity was still open.
|
||||
|
|
|
@ -23,7 +23,6 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import androidx.biometric.BiometricPrompt;
|
||||
|
||||
import com.beemdevelopment.aegis.AegisApplication;
|
||||
import com.beemdevelopment.aegis.CancelAction;
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.ThemeMap;
|
||||
|
@ -52,9 +51,7 @@ import javax.crypto.SecretKey;
|
|||
public class AuthActivity extends AegisActivity {
|
||||
private EditText _textPassword;
|
||||
|
||||
private CancelAction _cancelAction;
|
||||
private SlotList _slots;
|
||||
|
||||
private SecretKey _bioKey;
|
||||
private BiometricSlot _bioSlot;
|
||||
private BiometricPrompt _bioPrompt;
|
||||
|
@ -62,7 +59,7 @@ public class AuthActivity extends AegisActivity {
|
|||
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
|
||||
// biometric prompt by setting 'inhibitBioPrompt' to true through the intent
|
||||
private boolean _inhibitBioPrompt;
|
||||
|
||||
private Preferences _prefs;
|
||||
|
@ -95,7 +92,6 @@ public class AuthActivity extends AegisActivity {
|
|||
} else {
|
||||
_inhibitBioPrompt = savedInstanceState.getBoolean("inhibitBioPrompt", false);
|
||||
}
|
||||
_cancelAction = (CancelAction) intent.getSerializableExtra("cancelAction");
|
||||
_slots = (SlotList) intent.getSerializableExtra("slots");
|
||||
_stateless = _slots != null;
|
||||
if (!_stateless) {
|
||||
|
@ -182,11 +178,10 @@ public class AuthActivity extends AegisActivity {
|
|||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
switch (_cancelAction) {
|
||||
case KILL:
|
||||
finishAffinity();
|
||||
case CLOSE:
|
||||
finish();
|
||||
if (_stateless) {
|
||||
super.onBackPressed();
|
||||
} else {
|
||||
finishAffinity();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ import androidx.appcompat.view.ActionMode;
|
|||
import androidx.appcompat.widget.SearchView;
|
||||
|
||||
import com.beemdevelopment.aegis.AegisApplication;
|
||||
import com.beemdevelopment.aegis.CancelAction;
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.SortCategory;
|
||||
import com.beemdevelopment.aegis.ViewMode;
|
||||
|
@ -523,8 +523,8 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
return;
|
||||
}
|
||||
|
||||
if (_app.isAutoLockEnabled()) {
|
||||
_app.lock();
|
||||
if (_app.isAutoLockEnabled(Preferences.AUTO_LOCK_ON_BACK_BUTTON)) {
|
||||
_app.lock(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -596,7 +596,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
return true;
|
||||
}
|
||||
case R.id.action_lock:
|
||||
_app.lock();
|
||||
_app.lock(true);
|
||||
return true;
|
||||
default:
|
||||
if (item.getGroupId() == R.id.action_filter_group) {
|
||||
|
@ -655,7 +655,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
private void startAuthActivity(boolean inhibitBioPrompt) {
|
||||
if (!_isAuthenticating) {
|
||||
Intent intent = new Intent(this, AuthActivity.class);
|
||||
intent.putExtra("cancelAction", CancelAction.KILL);
|
||||
intent.putExtra("inhibitBioPrompt", inhibitBioPrompt);
|
||||
startActivityForResult(intent, CODE_DECRYPT);
|
||||
_isAuthenticating = true;
|
||||
|
@ -747,7 +746,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
public void onListChange() { _fabScrollHelper.setVisible(true); }
|
||||
|
||||
@Override
|
||||
public void onLocked() {
|
||||
public void onLocked(boolean userInitiated) {
|
||||
if (_actionMode != null) {
|
||||
_actionMode.finish();
|
||||
}
|
||||
|
@ -755,11 +754,11 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
_entryListView.clearEntries();
|
||||
_loaded = false;
|
||||
|
||||
if (isOpen()) {
|
||||
if (userInitiated) {
|
||||
startAuthActivity(true);
|
||||
} else {
|
||||
super.onLocked(userInitiated);
|
||||
}
|
||||
|
||||
super.onLocked();
|
||||
}
|
||||
|
||||
private void copyEntryCode(VaultEntry entry) {
|
||||
|
|
|
@ -20,7 +20,6 @@ import androidx.preference.SwitchPreferenceCompat;
|
|||
|
||||
import com.beemdevelopment.aegis.AegisApplication;
|
||||
import com.beemdevelopment.aegis.BuildConfig;
|
||||
import com.beemdevelopment.aegis.CancelAction;
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.Theme;
|
||||
|
@ -235,7 +234,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
_result.putExtra("needsRefresh", true);
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
Preference entryHighlightPreference = findPreference("pref_highlight_entry");
|
||||
entryHighlightPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
_result.putExtra("needsRefresh", true);
|
||||
|
@ -254,7 +253,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
_result.putExtra("needsRecreate", true);
|
||||
Window window = getActivity().getWindow();
|
||||
if ((boolean)newValue) {
|
||||
if ((boolean) newValue) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
|
@ -458,6 +457,35 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
});
|
||||
|
||||
_autoLockPreference = findPreference("pref_auto_lock");
|
||||
_autoLockPreference.setSummary(getAutoLockSummary());
|
||||
_autoLockPreference.setOnPreferenceClickListener((preference) -> {
|
||||
final int[] items = Preferences.AUTO_LOCK_SETTINGS;
|
||||
final String[] textItems = getResources().getStringArray(R.array.pref_auto_lock_types);
|
||||
final boolean[] checkedItems = new boolean[items.length];
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
checkedItems[i] = _prefs.isAutoLockTypeEnabled(items[i]);
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.pref_auto_lock_prompt)
|
||||
.setMultiChoiceItems(textItems, checkedItems, (dialog, index, isChecked) -> checkedItems[index] = isChecked)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
int autoLock = Preferences.AUTO_LOCK_OFF;
|
||||
for (int i = 0; i < checkedItems.length; i++) {
|
||||
if (checkedItems[i]) {
|
||||
autoLock |= items[i];
|
||||
}
|
||||
}
|
||||
|
||||
_prefs.setAutoLockMask(autoLock);
|
||||
_autoLockPreference.setSummary(getAutoLockSummary());
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null);
|
||||
Dialogs.showSecureDialog(builder.create());
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
_passwordReminderPreference = findPreference("pref_password_reminder");
|
||||
}
|
||||
|
||||
|
@ -575,7 +603,6 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
|
||||
Intent intent = new Intent(getActivity(), AuthActivity.class);
|
||||
intent.putExtra("slots", ((AegisImporter.EncryptedState) state).getSlots());
|
||||
intent.putExtra("cancelAction", CancelAction.CLOSE);
|
||||
startActivityForResult(intent, CODE_IMPORT_DECRYPT);
|
||||
} else {
|
||||
state.decrypt(getActivity(), new DatabaseImporter.DecryptListener() {
|
||||
|
@ -842,6 +869,28 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
_pinKeyboardPreference.setChecked(enable);
|
||||
}
|
||||
|
||||
private String getAutoLockSummary() {
|
||||
final int[] settings = Preferences.AUTO_LOCK_SETTINGS;
|
||||
final String[] descriptions = getResources().getStringArray(R.array.pref_auto_lock_types);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < settings.length; i++) {
|
||||
if (_prefs.isAutoLockTypeEnabled(settings[i])) {
|
||||
if (builder.length() != 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
|
||||
builder.append(descriptions[i].toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
if (builder.length() == 0) {
|
||||
return getString(R.string.pref_auto_lock_summary_disabled);
|
||||
}
|
||||
|
||||
return getString(R.string.pref_auto_lock_summary, builder.toString());
|
||||
}
|
||||
|
||||
private class SetPasswordListener implements Dialogs.SlotListener {
|
||||
@Override
|
||||
public void onSlotResult(Slot slot, Cipher cipher) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue