diff --git a/app/build.gradle b/app/build.gradle index 92e291fa..75cfc707 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,17 +3,8 @@ apply plugin: 'com.google.protobuf' apply plugin: 'dagger.hilt.android.plugin' apply plugin: 'com.mikepenz.aboutlibraries.plugin' -def getCmdOutput = { cmd -> - def stdout = new ByteArrayOutputStream() - exec { - commandLine cmd - standardOutput = stdout - } - return stdout.toString().trim() -} - -def getGitHash = { -> return getCmdOutput(["git", "rev-parse", "--short", "HEAD"]) } -def getGitBranch = { -> return getCmdOutput(["git", "rev-parse", "--abbrev-ref", "HEAD"]) } +def getGitHash = { -> return "unknown" } +def getGitBranch = { -> return "unknown" } def packageName = "com.beemdevelopment.aegis" def fileProviderAuthority = "${packageName}.fileprovider" @@ -26,7 +17,7 @@ android { defaultConfig { applicationId "${packageName}" - minSdkVersion 23 + minSdkVersion 16 targetSdkVersion 35 versionCode 80 versionName "3.4.1" @@ -98,7 +89,7 @@ android { cruncherEnabled = false } defaultConfig { - vectorDrawables.generatedDensities = [] + vectorDrawables.useSupportLibrary = true } packagingOptions { @@ -112,8 +103,8 @@ android { } compileOptions { - targetCompatibility JavaVersion.VERSION_17 - sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_1_8 coreLibraryDesugaringEnabled true } lint { @@ -191,7 +182,7 @@ dependencies { implementation "com.github.topjohnwu.libsu:core:${libsuVersion}" implementation "com.github.topjohnwu.libsu:io:${libsuVersion}" implementation "com.google.guava:guava:${guavaVersion}-android" - implementation 'com.google.android.material:material:1.12.0' + implementation 'com.google.android.material:material:1.4.0' implementation 'com.google.protobuf:protobuf-javalite:4.31.0' implementation 'com.google.zxing:core:3.5.3' implementation('com.mikepenz:aboutlibraries:11.2.3') { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 09337184..631699b5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + @@ -20,17 +22,13 @@ + android:theme="@style/Theme.Aegis.Launch"> + android:exported="true" + tools:targetApi="n"> @@ -122,7 +121,8 @@ android:label="@string/tile_open_scanner" android:icon="@drawable/ic_aegis_quicksettings" android:permission="android.permission.BIND_QUICK_SETTINGS_TILE" - android:exported="true"> + android:exported="true" + tools:targetApi="n"> diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricSlotInitializer.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricSlotInitializer.java index 45dd33a8..6802dbda 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricSlotInitializer.java +++ b/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricSlotInitializer.java @@ -28,12 +28,16 @@ public class BiometricSlotInitializer extends BiometricPrompt.AuthenticationCall public BiometricSlotInitializer(Fragment fragment, Listener listener) { _listener = listener; - _prompt = new BiometricPrompt(fragment, new UiThreadExecutor(), this); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + _prompt = new BiometricPrompt(fragment, new UiThreadExecutor(), this); + } } public BiometricSlotInitializer(FragmentActivity activity, Listener listener) { _listener = listener; - _prompt = new BiometricPrompt(activity, new UiThreadExecutor(), this); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + _prompt = new BiometricPrompt(activity, new UiThreadExecutor(), this); + } } /** @@ -43,6 +47,11 @@ public class BiometricSlotInitializer extends BiometricPrompt.AuthenticationCall * initialized and delivered back through the listener. */ public void authenticate(BiometricPrompt.PromptInfo info) { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) { + fail(0, "Biometric authentication is not supported on this device."); + return; + } + if (_slot != null) { throw new IllegalStateException("Biometric authentication already in progress"); } @@ -114,19 +123,25 @@ public class BiometricSlotInitializer extends BiometricPrompt.AuthenticationCall @Override public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { - super.onAuthenticationError(errorCode, errString); - fail(errorCode, errString.toString()); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + super.onAuthenticationError(errorCode, errString); + fail(errorCode, errString.toString()); + } } @Override public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { - super.onAuthenticationSucceeded(result); - _listener.onInitializeSlot(_slot, Objects.requireNonNull(result.getCryptoObject()).getCipher()); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + super.onAuthenticationSucceeded(result); + _listener.onInitializeSlot(_slot, Objects.requireNonNull(result.getCryptoObject()).getCipher()); + } } @Override public void onAuthenticationFailed() { - super.onAuthenticationFailed(); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + super.onAuthenticationFailed(); + } } public interface Listener { diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricsHelper.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricsHelper.java index 99b2a9a9..ecd23cbe 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricsHelper.java +++ b/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricsHelper.java @@ -11,9 +11,19 @@ public class BiometricsHelper { } public static BiometricManager getManager(Context context) { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) { + return null; + } + BiometricManager manager = BiometricManager.from(context); - if (manager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG) == BiometricManager.BIOMETRIC_SUCCESS) { - return manager; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + if (manager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG) == BiometricManager.BIOMETRIC_SUCCESS) { + return manager; + } + } else { + if (manager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) { + return manager; + } } return null; } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/AboutActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/AboutActivity.java index 4b974b68..95af7de5 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/AboutActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/AboutActivity.java @@ -37,6 +37,13 @@ public class AboutActivity extends AegisActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + if (android.os.Build.VERSION.SDK_INT < 21) { + Toast.makeText(this, "About screen is not supported on this device.", Toast.LENGTH_LONG).show(); + finish(); + return; + } + if (abortIfOrphan(savedInstanceState)) { return; } 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 a30c552c..5ff89283 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/AuthActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/AuthActivity.java @@ -129,38 +129,40 @@ public class AuthActivity extends AegisActivity { // only show the biometric prompt if the api version is new enough, permission is granted, a scanner is found and a biometric slot is found _slots = _vaultFile.getHeader().getSlots(); - if (_slots.has(BiometricSlot.class) && BiometricsHelper.isAvailable(this)) { - boolean invalidated = false; - - try { - // find a biometric slot with an id that matches an alias in the keystore - for (BiometricSlot slot : _slots.findAll(BiometricSlot.class)) { - String id = slot.getUUID().toString(); - KeyStoreHandle handle = new KeyStoreHandle(); - if (handle.containsKey(id)) { - SecretKey key = handle.getKey(id); - // if 'key' is null, it was permanently invalidated - if (key == null) { - invalidated = true; - continue; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + if (_slots.has(BiometricSlot.class) && BiometricsHelper.isAvailable(this)) { + boolean invalidated = false; + + try { + // find a biometric slot with an id that matches an alias in the keystore + for (BiometricSlot slot : _slots.findAll(BiometricSlot.class)) { + String id = slot.getUUID().toString(); + KeyStoreHandle handle = new KeyStoreHandle(); + if (handle.containsKey(id)) { + SecretKey key = handle.getKey(id); + // if 'key' is null, it was permanently invalidated + if (key == null) { + invalidated = true; + continue; + } + + _bioSlot = slot; + _bioKey = key; + biometricsButton.setVisibility(View.VISIBLE); + invalidated = false; + break; } - - _bioSlot = slot; - _bioKey = key; - biometricsButton.setVisibility(View.VISIBLE); - invalidated = false; - break; } + } catch (KeyStoreHandleException e) { + e.printStackTrace(); + Dialogs.showErrorDialog(this, R.string.biometric_init_error, e); } - } catch (KeyStoreHandleException e) { - e.printStackTrace(); - Dialogs.showErrorDialog(this, R.string.biometric_init_error, e); - } - // display a help message if a matching invalidated keystore entry was found - if (invalidated) { - boxBiometricInfo.setVisibility(View.VISIBLE); - biometricsButton.setVisibility(View.GONE); + // display a help message if a matching invalidated keystore entry was found + if (invalidated) { + boxBiometricInfo.setVisibility(View.VISIBLE); + biometricsButton.setVisibility(View.GONE); + } } } @@ -177,21 +179,23 @@ public class AuthActivity extends AegisActivity { _decryptButton.setEnabled(false); }); - biometricsButton.setOnClickListener(v -> { - if (_prefs.isPasswordReminderNeeded()) { - Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Aegis_AlertDialog_Warning) - .setTitle(getString(R.string.password_reminder_dialog_title)) - .setMessage(getString(R.string.password_reminder_dialog_message)) - .setCancelable(false) - .setIconAttribute(android.R.attr.alertDialogIcon) - .setPositiveButton(android.R.string.ok, (dialog1, which) -> { - showBiometricPrompt(); - }) - .create()); - } else { - showBiometricPrompt(); - } - }); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + biometricsButton.setOnClickListener(v -> { + if (_prefs.isPasswordReminderNeeded()) { + Dialogs.showSecureDialog(new MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Aegis_AlertDialog_Warning) + .setTitle(getString(R.string.password_reminder_dialog_title)) + .setMessage(getString(R.string.password_reminder_dialog_message)) + .setCancelable(false)) + .setIconAttribute(android.R.attr.alertDialogIcon) + .setPositiveButton(android.R.string.ok, (dialog1, which) -> { + showBiometricPrompt(); + }) + .create()); + } else { + showBiometricPrompt(); + } + }); + } } @Override @@ -216,8 +220,10 @@ public class AuthActivity extends AegisActivity { focusPasswordField(); } - if (_bioKey != null && _bioPrompt == null && !_inhibitBioPrompt && !remindPassword) { - _bioPrompt = showBiometricPrompt(); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + if (_bioKey != null && _bioPrompt == null && !_inhibitBioPrompt && !remindPassword) { + _bioPrompt = showBiometricPrompt(); + } } _inhibitBioPrompt = false; @@ -225,9 +231,11 @@ public class AuthActivity extends AegisActivity { @Override public void onPause() { - if (!isChangingConfigurations() && _bioPrompt != null) { - _bioPrompt.cancelAuthentication(); - _bioPrompt = null; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + if (!isChangingConfigurations() && _bioPrompt != null) { + _bioPrompt.cancelAuthentication(); + _bioPrompt = null; + } } super.onPause(); @@ -235,8 +243,10 @@ public class AuthActivity extends AegisActivity { @Override public void onAttachedToWindow() { - if (_bioKey != null && _prefs.isPasswordReminderNeeded()) { - showPasswordReminder(); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + if (_bioKey != null && _prefs.isPasswordReminderNeeded()) { + showPasswordReminder(); + } } } @@ -268,28 +278,31 @@ public class AuthActivity extends AegisActivity { } public BiometricPrompt showBiometricPrompt() { - InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(_textPassword.getWindowToken(), 0); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(_textPassword.getWindowToken(), 0); - Cipher cipher; - try { - cipher = _bioSlot.createDecryptCipher(_bioKey); - } catch (SlotException e) { - e.printStackTrace(); - Dialogs.showErrorDialog(this, R.string.biometric_init_error, e); - return null; - } + Cipher cipher; + try { + cipher = _bioSlot.createDecryptCipher(_bioKey); + } catch (SlotException e) { + e.printStackTrace(); + Dialogs.showErrorDialog(this, R.string.biometric_init_error, e); + return null; + } - BiometricPrompt.CryptoObject cryptoObj = new BiometricPrompt.CryptoObject(cipher); - BiometricPrompt prompt = new BiometricPrompt(this, new UiThreadExecutor(), new BiometricPromptListener()); + BiometricPrompt.CryptoObject cryptoObj = new BiometricPrompt.CryptoObject(cipher); + BiometricPrompt prompt = new BiometricPrompt(this, new UiThreadExecutor(), new BiometricPromptListener()); - BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder() - .setTitle(getString(R.string.authentication)) - .setNegativeButtonText(getString(android.R.string.cancel)) - .setConfirmationRequired(false) - .build(); - prompt.authenticate(info, cryptoObj); - return prompt; + BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder() + .setTitle(getString(R.string.authentication)) + .setNegativeButtonText(getString(android.R.string.cancel)) + .setConfirmationRequired(false) + .build(); + prompt.authenticate(info, cryptoObj); + return prompt; + } + return null; } private void finish(MasterKey key, boolean isSlotRepaired) { @@ -366,37 +379,43 @@ public class AuthActivity extends AegisActivity { private class BiometricPromptListener extends BiometricPrompt.AuthenticationCallback { @Override public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { - super.onAuthenticationError(errorCode, errString); - _bioPrompt = null; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + super.onAuthenticationError(errorCode, errString); + _bioPrompt = null; - if (!BiometricsHelper.isCanceled(errorCode)) { - _auditLogRepository.addVaultUnlockFailedBiometricsEvent(); - Toast.makeText(AuthActivity.this, errString, Toast.LENGTH_LONG).show(); + if (!BiometricsHelper.isCanceled(errorCode)) { + _auditLogRepository.addVaultUnlockFailedBiometricsEvent(); + Toast.makeText(AuthActivity.this, errString, Toast.LENGTH_LONG).show(); + } } } @Override public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { - super.onAuthenticationSucceeded(result); - _bioPrompt = null; - - MasterKey key; - BiometricSlot slot = _slots.find(BiometricSlot.class); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + super.onAuthenticationSucceeded(result); + _bioPrompt = null; + + MasterKey key; + BiometricSlot slot = _slots.find(BiometricSlot.class); + + try { + key = slot.getKey(result.getCryptoObject().getCipher()); + } catch (SlotException | SlotIntegrityException e) { + e.printStackTrace(); + Dialogs.showErrorDialog(AuthActivity.this, R.string.biometric_decrypt_error, e); + return; + } - try { - key = slot.getKey(result.getCryptoObject().getCipher()); - } catch (SlotException | SlotIntegrityException e) { - e.printStackTrace(); - Dialogs.showErrorDialog(AuthActivity.this, R.string.biometric_decrypt_error, e); - return; + finish(key, false); } - - finish(key, false); } @Override public void onAuthenticationFailed() { - super.onAuthenticationFailed(); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + super.onAuthenticationFailed(); + } } } } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java index 592d15d7..932593b5 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java @@ -240,10 +240,15 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene dialog.dismiss(); startScanImageActivity(); }); - view.findViewById(R.id.fab_scan).setOnClickListener(v3 -> { - dialog.dismiss(); - startScanActivity(); - }); + View fabScan = view.findViewById(R.id.fab_scan); + if (android.os.Build.VERSION.SDK_INT < 21) { + fabScan.setVisibility(View.GONE); + } else { + fabScan.setOnClickListener(v3 -> { + dialog.dismiss(); + startScanActivity(); + }); + } Dialogs.showSecureDialog(dialog); }); @@ -805,7 +810,9 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene switch (action) { case "scan": - startScanActivity(); + if (android.os.Build.VERSION.SDK_INT >= 21) { + startScanActivity(); + } break; } @@ -974,6 +981,10 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene _menu = menu; getMenuInflater().inflate(R.menu.menu_main, menu); + if (android.os.Build.VERSION.SDK_INT < 21) { + menu.findItem(R.id.action_about).setVisible(false); + } + updateLockIcon(); updateSortCategoryMenu(); @@ -1050,8 +1061,10 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene if (itemId == R.id.action_settings) { startPreferencesActivity(); } else if (itemId == R.id.action_about) { - Intent intent = new Intent(this, AboutActivity.class); - startActivity(intent); + if (android.os.Build.VERSION.SDK_INT >= 21) { + Intent intent = new Intent(this, AboutActivity.class); + startActivity(intent); + } } else if (itemId == R.id.action_lock) { _vaultManager.lock(true); } else { diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/ScannerActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/ScannerActivity.java index cc0d6095..16fcdee1 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/ScannerActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/ScannerActivity.java @@ -33,6 +33,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +@androidx.annotation.RequiresApi(21) public class ScannerActivity extends AegisActivity implements QrCodeAnalyzer.Listener { private ProcessCameraProvider _cameraProvider; private ListenableFuture _cameraProviderFuture; @@ -52,6 +53,13 @@ public class ScannerActivity extends AegisActivity implements QrCodeAnalyzer.Lis @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + if (android.os.Build.VERSION.SDK_INT < 21) { + Toast.makeText(this, R.string.camera_not_supported, Toast.LENGTH_LONG).show(); + finish(); + return; + } + if (abortIfOrphan(savedInstanceState)) { return; } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/SecurityPreferencesFragment.java b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/SecurityPreferencesFragment.java index e1123ca2..f86d4c3d 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/SecurityPreferencesFragment.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/SecurityPreferencesFragment.java @@ -109,34 +109,36 @@ public class SecurityPreferencesFragment extends PreferencesFragment { _biometricsPreference = requirePreference("pref_biometrics"); _biometricsPreference.setOnPreferenceChangeListener((preference, newValue) -> { - VaultFileCredentials creds = _vaultManager.getVault().getCredentials(); - SlotList slots = creds.getSlots(); - - if (!slots.has(BiometricSlot.class)) { - if (BiometricsHelper.isAvailable(requireContext())) { - BiometricSlotInitializer initializer = new BiometricSlotInitializer(SecurityPreferencesFragment.this, new RegisterBiometricsListener()); - BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder() - .setTitle(getString(R.string.set_up_biometric)) - .setNegativeButtonText(getString(android.R.string.cancel)) - .build(); - initializer.authenticate(info); - } - } else { - // remove the biometric slot - BiometricSlot slot = slots.find(BiometricSlot.class); - slots.remove(slot); - _vaultManager.getVault().setCredentials(creds); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + VaultFileCredentials creds = _vaultManager.getVault().getCredentials(); + SlotList slots = creds.getSlots(); - // remove the KeyStore key - try { - KeyStoreHandle handle = new KeyStoreHandle(); - handle.deleteKey(slot.getUUID().toString()); - } catch (KeyStoreHandleException e) { - e.printStackTrace(); + if (!slots.has(BiometricSlot.class)) { + if (BiometricsHelper.isAvailable(requireContext())) { + BiometricSlotInitializer initializer = new BiometricSlotInitializer(SecurityPreferencesFragment.this, new RegisterBiometricsListener()); + BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder() + .setTitle(getString(R.string.set_up_biometric)) + .setNegativeButtonText(getString(android.R.string.cancel)) + .build(); + initializer.authenticate(info); + } + } else { + // remove the biometric slot + BiometricSlot slot = slots.find(BiometricSlot.class); + slots.remove(slot); + _vaultManager.getVault().setCredentials(creds); + + // remove the KeyStore key + try { + KeyStoreHandle handle = new KeyStoreHandle(); + handle.deleteKey(slot.getUUID().toString()); + } catch (KeyStoreHandleException e) { + e.printStackTrace(); + } + + saveAndBackupVault(); + updateEncryptionPreferences(); } - - saveAndBackupVault(); - updateEncryptionPreferences(); } return false; @@ -271,12 +273,20 @@ public class SecurityPreferencesFragment extends PreferencesFragment { SlotList slots = _vaultManager.getVault().getCredentials().getSlots(); boolean multiBackupPassword = slots.findBackupPasswordSlots().size() > 1; boolean multiPassword = slots.findRegularPasswordSlots().size() > 1; - boolean multiBio = slots.findAll(BiometricSlot.class).size() > 1; - boolean canUseBio = BiometricsHelper.isAvailable(requireContext()); _setPasswordPreference.setEnabled(!multiPassword); - _biometricsPreference.setEnabled(canUseBio && !multiBio); - _biometricsPreference.setChecked(slots.has(BiometricSlot.class), true); - _passwordReminderPreference.setVisible(slots.has(BiometricSlot.class)); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + boolean multiBio = slots.findAll(BiometricSlot.class).size() > 1; + boolean canUseBio = BiometricsHelper.isAvailable(requireContext()); + _biometricsPreference.setEnabled(canUseBio && !multiBio); + _biometricsPreference.setChecked(slots.has(BiometricSlot.class), true); + _passwordReminderPreference.setVisible(slots.has(BiometricSlot.class)); + } else { + _biometricsPreference.setEnabled(false); + _biometricsPreference.setChecked(false, true); + _passwordReminderPreference.setVisible(false); + } + _backupPasswordChangePreference.setEnabled(!multiBackupPassword); } else { _setPasswordPreference.setEnabled(false); @@ -399,25 +409,29 @@ public class SecurityPreferencesFragment extends PreferencesFragment { private class RegisterBiometricsListener implements BiometricSlotInitializer.Listener { @Override public void onInitializeSlot(BiometricSlot slot, Cipher cipher) { - VaultFileCredentials creds = _vaultManager.getVault().getCredentials(); - try { - slot.setKey(creds.getKey(), cipher); - } catch (SlotException e) { - e.printStackTrace(); - onSlotInitializationFailed(0, e.toString()); - return; - } - creds.getSlots().add(slot); - _vaultManager.getVault().setCredentials(creds); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + VaultFileCredentials creds = _vaultManager.getVault().getCredentials(); + try { + slot.setKey(creds.getKey(), cipher); + } catch (SlotException e) { + e.printStackTrace(); + onSlotInitializationFailed(0, e.toString()); + return; + } + creds.getSlots().add(slot); + _vaultManager.getVault().setCredentials(creds); - saveAndBackupVault(); - updateEncryptionPreferences(); + saveAndBackupVault(); + updateEncryptionPreferences(); + } } @Override public void onSlotInitializationFailed(int errorCode, @NonNull CharSequence errString) { - if (!BiometricsHelper.isCanceled(errorCode)) { - Dialogs.showErrorDialog(requireContext(), R.string.encryption_enable_biometrics_error, errString); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + if (!BiometricsHelper.isCanceled(errorCode)) { + Dialogs.showErrorDialog(requireContext(), R.string.encryption_enable_biometrics_error, errString); + } } } } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/slides/SecuritySetupSlide.java b/app/src/main/java/com/beemdevelopment/aegis/ui/slides/SecuritySetupSlide.java index 23082fa4..4cb14fe0 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/slides/SecuritySetupSlide.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/slides/SecuritySetupSlide.java @@ -98,12 +98,14 @@ public class SecuritySetupSlide extends SlideFragment { } private void showBiometricPrompt() { - BiometricSlotInitializer initializer = new BiometricSlotInitializer(this, new BiometricsListener()); - BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder() - .setTitle(getString(R.string.set_up_biometric)) - .setNegativeButtonText(getString(android.R.string.cancel)) - .build(); - initializer.authenticate(info); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + BiometricSlotInitializer initializer = new BiometricSlotInitializer(this, new BiometricsListener()); + BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder() + .setTitle(getString(R.string.set_up_biometric)) + .setNegativeButtonText(getString(android.R.string.cancel)) + .build(); + initializer.authenticate(info); + } } private void deriveKey() { @@ -119,8 +121,10 @@ public class SecuritySetupSlide extends SlideFragment { case CRYPT_TYPE_NONE: return true; case CRYPT_TYPE_BIOMETRIC: - if (!_creds.getSlots().has(BiometricSlot.class)) { - return false; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + if (!_creds.getSlots().has(BiometricSlot.class)) { + return false; + } } // intentional fallthrough case CRYPT_TYPE_PASS: @@ -140,7 +144,7 @@ public class SecuritySetupSlide extends SlideFragment { Toast.makeText(requireContext(), R.string.password_equality_error, Toast.LENGTH_SHORT).show(); } else if (_cryptType != SecurityPickerSlide.CRYPT_TYPE_BIOMETRIC) { deriveKey(); - } else if (!_creds.getSlots().has(BiometricSlot.class)) { + } else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M && !_creds.getSlots().has(BiometricSlot.class)) { showBiometricPrompt(); } } @@ -170,22 +174,26 @@ public class SecuritySetupSlide extends SlideFragment { private class BiometricsListener implements BiometricSlotInitializer.Listener { @Override public void onInitializeSlot(BiometricSlot slot, Cipher cipher) { - try { - slot.setKey(_creds.getKey(), cipher); - _creds.getSlots().add(slot); - } catch (SlotException e) { - e.printStackTrace(); - onSlotInitializationFailed(0, e.toString()); - return; - } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + try { + slot.setKey(_creds.getKey(), cipher); + _creds.getSlots().add(slot); + } catch (SlotException e) { + e.printStackTrace(); + onSlotInitializationFailed(0, e.toString()); + return; + } - deriveKey(); + deriveKey(); + } } @Override public void onSlotInitializationFailed(int errorCode, @NonNull CharSequence errString) { - if (!BiometricsHelper.isCanceled(errorCode)) { - Dialogs.showErrorDialog(requireContext(), R.string.encryption_enable_biometrics_error, errString); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + if (!BiometricsHelper.isCanceled(errorCode)) { + Dialogs.showErrorDialog(requireContext(), R.string.encryption_enable_biometrics_error, errString); + } } } } diff --git a/app/src/main/res/layout/activity_auth.xml b/app/src/main/res/layout/activity_auth.xml index f547fe82..ee251765 100644 --- a/app/src/main/res/layout/activity_auth.xml +++ b/app/src/main/res/layout/activity_auth.xml @@ -16,7 +16,8 @@ android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" - android:paddingHorizontal="48dp" + android:paddingLeft="48dp" + android:paddingRight="48dp" android:paddingTop="48dp" android:orientation="vertical"> @@ -126,6 +127,7 @@ android:textAllCaps="true" android:textStyle="bold" android:textColor="?attr/colorOnSurfaceDim" - android:paddingVertical="50dp" /> + android:paddingTop="50dp" + android:paddingBottom="50dp" /> diff --git a/app/src/main/res/layout/activity_edit_entry.xml b/app/src/main/res/layout/activity_edit_entry.xml index e9f19027..b9445fc2 100644 --- a/app/src/main/res/layout/activity_edit_entry.xml +++ b/app/src/main/res/layout/activity_edit_entry.xml @@ -240,7 +240,6 @@ + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp"> %d item selected %d items selected + Camera is not supported on this device.