diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-05 12:05:44 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-05 12:10:18 +0100 |
| commit | c85e7c8801caf7a73b46dda9ebf75be023cad9b4 (patch) | |
| tree | c180a57fd54e9b09e84297f9fc51670bf5f4bfab /app/src/main/java | |
| parent | 73486a180ba3ead1890924920094e7e678a8ef76 (diff) | |
| download | HardenedPass-c85e7c8801caf7a73b46dda9ebf75be023cad9b4.tar.gz HardenedPass-c85e7c8801caf7a73b46dda9ebf75be023cad9b4.zip | |
build: raise minSdk to 31
The project targets GrapheneOS, which only runs current Android. Android 12
gives the platform night mode API and removes the fingerprint dialog path that
needs AppCompat. Devices below it stay on 0.2.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'app/src/main/java')
18 files changed, 43 insertions, 334 deletions
diff --git a/app/src/main/java/app/passwordstore/Application.kt b/app/src/main/java/app/passwordstore/Application.kt index bda61265..60b58cc1 100644 --- a/app/src/main/java/app/passwordstore/Application.kt +++ b/app/src/main/java/app/passwordstore/Application.kt @@ -9,7 +9,6 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.SharedPreferences -import android.os.Build import android.os.StrictMode import androidx.appcompat.app.AppCompatDelegate.* import app.passwordstore.data.crypto.PGPPassphraseCache @@ -92,38 +91,24 @@ class Application : android.app.Application(), SharedPreferences.OnSharedPrefere } private fun setVmPolicy() { - val builder = + StrictMode.setVmPolicy( StrictMode.VmPolicy.Builder() .detectActivityLeaks() .detectFileUriExposure() .detectLeakedClosableObjects() .detectLeakedRegistrationObjects() .detectLeakedSqlLiteObjects() - - builder.detectContentUriWithoutPermission() - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - builder.detectCredentialProtectedWhileLocked().detectImplicitDirectBoot() - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - builder.detectNonSdkApiUsage() - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - builder.detectIncorrectContextUse().detectUnsafeIntentLaunch() - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - builder.penaltyListener(Executors.newSingleThreadExecutor()) { violation -> - logcat(VERBOSE) { violation.stackTraceToString() } - } - } else { - builder.penaltyLog() - } - - val policy = builder.build() - StrictMode.setVmPolicy(policy) + .detectContentUriWithoutPermission() + .detectCredentialProtectedWhileLocked() + .detectImplicitDirectBoot() + .detectNonSdkApiUsage() + .detectIncorrectContextUse() + .detectUnsafeIntentLaunch() + .penaltyListener(Executors.newSingleThreadExecutor()) { violation -> + logcat(VERBOSE) { violation.stackTraceToString() } + } + .build() + ) } private fun setNightMode() { diff --git a/app/src/main/java/app/passwordstore/injection/AutofillResponseBuilderModule.kt b/app/src/main/java/app/passwordstore/injection/AutofillResponseBuilderModule.kt index 46e61397..1717e4cc 100644 --- a/app/src/main/java/app/passwordstore/injection/AutofillResponseBuilderModule.kt +++ b/app/src/main/java/app/passwordstore/injection/AutofillResponseBuilderModule.kt @@ -5,8 +5,6 @@ package app.passwordstore.injection -import android.os.Build -import app.passwordstore.util.autofill.Api26AutofillResponseBuilder import app.passwordstore.util.autofill.Api30AutofillResponseBuilder import app.passwordstore.util.autofill.AutofillResponseBuilder import dagger.Module @@ -21,11 +19,6 @@ object AutofillResponseBuilderModule { @Provides @Reusable - fun provideAutofillResponseBuilder(): AutofillResponseBuilder.Factory { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - Api30AutofillResponseBuilder.Factory - } else { - Api26AutofillResponseBuilder.Factory - } - } + fun provideAutofillResponseBuilder(): AutofillResponseBuilder.Factory = + Api30AutofillResponseBuilder.Factory } diff --git a/app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt b/app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt index 7ba4df74..6354359f 100644 --- a/app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt @@ -9,7 +9,6 @@ import android.content.Context import android.content.Intent import android.content.IntentSender import android.content.SharedPreferences -import android.os.Build import android.os.Bundle import android.view.WindowManager import android.view.autofill.AutofillManager @@ -368,11 +367,7 @@ class AutofillDecryptActivity : AppCompatActivity() { context, decryptFileRequestCode++, intent, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE - } else { - PendingIntent.FLAG_CANCEL_CURRENT - }, + PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE, ) .intentSender } diff --git a/app/src/main/java/app/passwordstore/ui/autofill/AutofillFilterView.kt b/app/src/main/java/app/passwordstore/ui/autofill/AutofillFilterView.kt index c607b81f..86c2d51d 100644 --- a/app/src/main/java/app/passwordstore/ui/autofill/AutofillFilterView.kt +++ b/app/src/main/java/app/passwordstore/ui/autofill/AutofillFilterView.kt @@ -8,7 +8,6 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.IntentSender -import android.os.Build import android.os.Bundle import android.view.autofill.AutofillManager import androidx.activity.compose.setContent @@ -74,11 +73,7 @@ class AutofillFilterView : AppCompatActivity() { context, matchAndDecryptFileRequestCode++, intent, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE - } else { - PendingIntent.FLAG_CANCEL_CURRENT - }, + PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE, ) .intentSender } diff --git a/app/src/main/java/app/passwordstore/ui/autofill/AutofillPublisherChangedActivity.kt b/app/src/main/java/app/passwordstore/ui/autofill/AutofillPublisherChangedActivity.kt index 803896a5..b0dd5dd9 100644 --- a/app/src/main/java/app/passwordstore/ui/autofill/AutofillPublisherChangedActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/autofill/AutofillPublisherChangedActivity.kt @@ -9,7 +9,6 @@ import android.content.Context import android.content.Intent import android.content.IntentSender import android.content.pm.PackageManager -import android.os.Build import android.os.Bundle import android.service.autofill.FillResponse import android.text.format.DateUtils @@ -54,11 +53,7 @@ class AutofillPublisherChangedActivity : AppCompatActivity() { context, publisherChangedRequestCode++, intent, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE - } else { - PendingIntent.FLAG_CANCEL_CURRENT - }, + PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE, ) .intentSender } diff --git a/app/src/main/java/app/passwordstore/ui/crypto/CryptoExtensions.kt b/app/src/main/java/app/passwordstore/ui/crypto/CryptoExtensions.kt index 67343a2e..4a6813f7 100644 --- a/app/src/main/java/app/passwordstore/ui/crypto/CryptoExtensions.kt +++ b/app/src/main/java/app/passwordstore/ui/crypto/CryptoExtensions.kt @@ -7,9 +7,7 @@ package app.passwordstore.ui.crypto import android.content.ClipData import android.content.Intent -import android.os.Build import android.os.PersistableBundle -import androidx.annotation.StringRes import androidx.fragment.app.FragmentActivity import app.passwordstore.R import app.passwordstore.crypto.PGPIdentifier @@ -17,7 +15,6 @@ import app.passwordstore.data.repo.PasswordRepository import app.passwordstore.util.extensions.clipboard import app.passwordstore.util.extensions.getString import app.passwordstore.util.extensions.sharedPrefs -import app.passwordstore.util.extensions.snackbar import app.passwordstore.util.services.ClipboardService import app.passwordstore.util.settings.Constants import app.passwordstore.util.settings.PreferenceKeys @@ -51,23 +48,13 @@ fun getLongName(fullPath: String, repositoryPath: String, basename: String): Str } } -/** - * Copies provided [text] to the clipboard, flagged sensitive. Below Android 11 it also shows a - * snackbar, which can be disabled by passing [showSnackbar] as false. - */ -fun FragmentActivity.copyTextToClipboard( - text: String?, - showSnackbar: Boolean = true, - @StringRes snackbarTextRes: Int = R.string.clipboard_copied_text, -) { +/** Copies provided [text] to the clipboard, flagged sensitive. */ +fun FragmentActivity.copyTextToClipboard(text: String?) { val clipboard = clipboard ?: return val clip = ClipData.newPlainText("pgp_handler_result_pm", text) clip.description.extras = PersistableBundle().apply { putBoolean("android.content.extra.IS_SENSITIVE", true) } clipboard.setPrimaryClip(clip) - if (showSnackbar && Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { - snackbar(message = resources.getString(snackbarTextRes)) - } } /** diff --git a/app/src/main/java/app/passwordstore/ui/editor/EditorViewModel.kt b/app/src/main/java/app/passwordstore/ui/editor/EditorViewModel.kt index 6d2fbaec..fe5ee772 100644 --- a/app/src/main/java/app/passwordstore/ui/editor/EditorViewModel.kt +++ b/app/src/main/java/app/passwordstore/ui/editor/EditorViewModel.kt @@ -10,8 +10,6 @@ import android.content.SharedPreferences import android.graphics.Bitmap import android.graphics.ImageDecoder import android.net.Uri -import android.os.Build -import android.provider.MediaStore import androidx.annotation.StringRes import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -168,12 +166,8 @@ constructor( return withContext(dispatcherProvider.io()) { runCatching { val bitmap = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - ImageDecoder.decodeBitmap(ImageDecoder.createSource(resolver, uri)) - .copy(Bitmap.Config.ARGB_8888, false) - } else { - @Suppress("DEPRECATION") MediaStore.Images.Media.getBitmap(resolver, uri) - } + ImageDecoder.decodeBitmap(ImageDecoder.createSource(resolver, uri)) + .copy(Bitmap.Config.ARGB_8888, false) val pixels = IntArray(bitmap.width * bitmap.height) bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height) val source = RGBLuminanceSource(bitmap.width, bitmap.height, pixels) diff --git a/app/src/main/java/app/passwordstore/ui/settings/ProxyViewModel.kt b/app/src/main/java/app/passwordstore/ui/settings/ProxyViewModel.kt index fb2bfe93..10f82178 100644 --- a/app/src/main/java/app/passwordstore/ui/settings/ProxyViewModel.kt +++ b/app/src/main/java/app/passwordstore/ui/settings/ProxyViewModel.kt @@ -6,7 +6,6 @@ package app.passwordstore.ui.settings import android.net.InetAddresses -import android.os.Build import android.util.Patterns import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -19,10 +18,7 @@ import javax.inject.Inject /** A numeric address or a plain host name: no scheme, port or path, which the port field holds. */ fun isProxyHost(text: String): Boolean { - val numeric = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) InetAddresses.isNumericAddress(text) - else @Suppress("DEPRECATION") Patterns.IP_ADDRESS.matcher(text).matches() - return numeric || + return InetAddresses.isNumericAddress(text) || (!text.contains(':') && !text.contains('/') && Patterns.DOMAIN_NAME.matcher(text).matches()) } diff --git a/app/src/main/java/app/passwordstore/util/auth/BiometricAuthenticator.kt b/app/src/main/java/app/passwordstore/util/auth/BiometricAuthenticator.kt index 98f2229a..8f912879 100644 --- a/app/src/main/java/app/passwordstore/util/auth/BiometricAuthenticator.kt +++ b/app/src/main/java/app/passwordstore/util/auth/BiometricAuthenticator.kt @@ -5,7 +5,6 @@ package app.passwordstore.util.auth import android.app.KeyguardManager -import android.os.Build import androidx.annotation.StringRes import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager.Authenticators @@ -23,14 +22,10 @@ object BiometricAuthenticator { /** * Keystore keys that require user authentication are unlocked only by the device credential or a * Class 3 (strong) biometric, so a weak biometric would pass the prompt and then fail to sign or - * decrypt with `User not authenticated`. Android 9 and 10 do not support combining - * [Authenticators.BIOMETRIC_STRONG] with [Authenticators.DEVICE_CREDENTIAL], so they keep the - * weak class. + * decrypt with `User not authenticated`. */ private val VALID_AUTHENTICATORS = - Authenticators.DEVICE_CREDENTIAL or - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) Authenticators.BIOMETRIC_STRONG - else Authenticators.BIOMETRIC_WEAK + Authenticators.DEVICE_CREDENTIAL or Authenticators.BIOMETRIC_STRONG /** * Sealed class to wrap [BiometricPrompt]'s [Int]-based return codes into more easily-interpreted diff --git a/app/src/main/java/app/passwordstore/util/autofill/Api26AutofillResponseBuilder.kt b/app/src/main/java/app/passwordstore/util/autofill/Api26AutofillResponseBuilder.kt deleted file mode 100644 index f38011a7..00000000 --- a/app/src/main/java/app/passwordstore/util/autofill/Api26AutofillResponseBuilder.kt +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package app.passwordstore.util.autofill - -import android.content.Context -import android.content.IntentSender -import android.os.Build -import android.service.autofill.Dataset -import android.service.autofill.FillCallback -import android.service.autofill.FillRequest -import android.service.autofill.FillResponse -import android.service.autofill.SaveInfo -import app.passwordstore.ui.autofill.AutofillDecryptActivity -import app.passwordstore.ui.autofill.AutofillFilterView -import app.passwordstore.ui.autofill.AutofillPublisherChangedActivity -import app.passwordstore.ui.autofill.AutofillSaveActivity -import com.github.androidpasswordstore.autofillparser.AutofillAction -import com.github.androidpasswordstore.autofillparser.FillableForm -import com.github.androidpasswordstore.autofillparser.fillWith -import com.github.michaelbull.result.fold -import java.io.File -import logcat.LogPriority.ERROR -import logcat.asLog -import logcat.logcat - -class Api26AutofillResponseBuilder private constructor(form: FillableForm) : - AutofillResponseBuilder { - - object Factory : AutofillResponseBuilder.Factory { - override fun create(form: FillableForm): AutofillResponseBuilder = - Api26AutofillResponseBuilder(form) - } - - private val formOrigin = form.formOrigin - private val scenario = form.scenario - private val ignoredIds = form.ignoredIds - private val saveFlags = form.saveFlags - private val clientState = form.toClientState() - - // We do not offer save when the only relevant field is a username field or there is no field. - private val scenarioSupportsSave = scenario.hasPasswordFieldsToSave - private val canBeSaved = saveFlags != null && scenarioSupportsSave - - @Suppress("DEPRECATION") - private fun makeIntentDataset( - context: Context, - action: AutofillAction, - intentSender: IntentSender, - metadata: DatasetMetadata, - ): Dataset { - return Dataset.Builder(makeRemoteView(context, metadata)).run { - fillWith(scenario, action, credentials = null) - setAuthentication(intentSender) - build() - } - } - - private fun makeMatchDataset(context: Context, file: File): Dataset? { - if (!scenario.hasFieldsToFillOn(AutofillAction.Match)) return null - val metadata = makeFillMatchMetadata(context, file) - val intentSender = AutofillDecryptActivity.makeDecryptFileIntentSender(file, context) - return makeIntentDataset(context, AutofillAction.Match, intentSender, metadata) - } - - private fun makeSearchDataset(context: Context): Dataset? { - if (!scenario.hasFieldsToFillOn(AutofillAction.Search)) return null - val metadata = makeSearchAndFillMetadata(context) - val intentSender = AutofillFilterView.makeMatchAndDecryptFileIntentSender(context, formOrigin) - return makeIntentDataset(context, AutofillAction.Search, intentSender, metadata) - } - - private fun makeGenerateDataset(context: Context): Dataset? { - if (!scenario.hasFieldsToFillOn(AutofillAction.Generate)) return null - val metadata = makeGenerateAndFillMetadata(context) - val intentSender = AutofillSaveActivity.makeSaveIntentSender(context, null, formOrigin) - return makeIntentDataset(context, AutofillAction.Generate, intentSender, metadata) - } - - private fun makePublisherChangedDataset( - context: Context, - publisherChangedException: AutofillPublisherChangedException, - ): Dataset { - val metadata = makeWarningMetadata(context) - // If the user decides to trust the new publisher, they can choose reset the list of - // matches. In this case we need to immediately show a new `FillResponse` as if the app were - // autofilled for the first time. This `FillResponse` needs to be returned as a result from - // `AutofillPublisherChangedActivity`, which is why we create and pass it on here. - val fillResponseAfterReset = makeFillResponse(context, emptyList()) - val intentSender = - AutofillPublisherChangedActivity.makePublisherChangedIntentSender( - context, - publisherChangedException, - fillResponseAfterReset, - ) - return makeIntentDataset(context, AutofillAction.Match, intentSender, metadata) - } - - private fun makePublisherChangedResponse( - context: Context, - publisherChangedException: AutofillPublisherChangedException, - ): FillResponse { - return FillResponse.Builder().run { - addDataset(makePublisherChangedDataset(context, publisherChangedException)) - setIgnoredIds(*ignoredIds.toTypedArray()) - build() - } - } - - // TODO: Support multi-step authentication flows in apps via FLAG_DELAY_SAVE - // See: - // https://developer.android.com/reference/android/service/autofill/SaveInfo#FLAG_DELAY_SAVE - private fun makeSaveInfo(): SaveInfo? { - if (!canBeSaved) return null - check(saveFlags != null) { "saveFlags must not be null" } - val idsToSave = scenario.fieldsToSave.toTypedArray() - if (idsToSave.isEmpty()) return null - var saveDataTypes = SaveInfo.SAVE_DATA_TYPE_PASSWORD - if (scenario.hasUsername) { - saveDataTypes = saveDataTypes or SaveInfo.SAVE_DATA_TYPE_USERNAME - } - return SaveInfo.Builder(saveDataTypes, idsToSave).run { - setFlags(saveFlags) - build() - } - } - - private fun makeFillResponse(context: Context, matchedFiles: List<File>): FillResponse? { - var datasetCount = 0 - return FillResponse.Builder().run { - for (file in matchedFiles) { - makeMatchDataset(context, file)?.let { - datasetCount++ - addDataset(it) - } - } - makeGenerateDataset(context)?.let { - datasetCount++ - addDataset(it) - } - makeSearchDataset(context)?.let { - datasetCount++ - addDataset(it) - } - if (datasetCount == 0) return null - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - setHeader( - makeRemoteView( - context, - makeHeaderMetadata(formOrigin.getPrettyIdentifier(context, untrusted = true)), - ) - ) - } - makeSaveInfo()?.let { setSaveInfo(it) } - setClientState(clientState) - setIgnoredIds(*ignoredIds.toTypedArray()) - build() - } - } - - /** Creates and returns a suitable [FillResponse] to the Autofill framework. */ - override fun fillCredentials(context: Context, fillRequest: FillRequest, callback: FillCallback) { - AutofillMatcher.getMatchesFor(context, formOrigin) - .fold( - success = { matchedFiles -> callback.onSuccess(makeFillResponse(context, matchedFiles)) }, - failure = { e -> - logcat(ERROR) { e.asLog() } - callback.onSuccess(makePublisherChangedResponse(context, e)) - }, - ) - } -} diff --git a/app/src/main/java/app/passwordstore/util/autofill/Api30AutofillResponseBuilder.kt b/app/src/main/java/app/passwordstore/util/autofill/Api30AutofillResponseBuilder.kt index 55679b21..bf41327f 100644 --- a/app/src/main/java/app/passwordstore/util/autofill/Api30AutofillResponseBuilder.kt +++ b/app/src/main/java/app/passwordstore/util/autofill/Api30AutofillResponseBuilder.kt @@ -31,7 +31,6 @@ import logcat.asLog import logcat.logcat /** Implements [AutofillResponseBuilder]'s methods for API 30 and above */ -@RequiresApi(Build.VERSION_CODES.R) class Api30AutofillResponseBuilder private constructor(form: FillableForm) : AutofillResponseBuilder { diff --git a/app/src/main/java/app/passwordstore/util/autofill/AutofillResponseBuilder.kt b/app/src/main/java/app/passwordstore/util/autofill/AutofillResponseBuilder.kt index bfa47d74..a98aefc5 100644 --- a/app/src/main/java/app/passwordstore/util/autofill/AutofillResponseBuilder.kt +++ b/app/src/main/java/app/passwordstore/util/autofill/AutofillResponseBuilder.kt @@ -6,7 +6,6 @@ package app.passwordstore.util.autofill import android.content.Context -import android.os.Build import android.os.Bundle import android.service.autofill.Dataset import android.service.autofill.FillCallback @@ -34,19 +33,11 @@ interface AutofillResponseBuilder { action: AutofillAction, ): Dataset { val scenario = AutofillScenario.fromClientState(clientState) - // Before Android P, Datasets used for fill-in had to come with a RemoteViews, even - // though they are rarely shown. // FIXME: We should clone the original dataset here and add the credentials to be filled // in. Otherwise, the entry in the cached list of datasets will be overwritten by the // fill-in dataset without any visual representation. This causes it to be missing from // the Autofill suggestions shown after the user clears the filled out form fields. - val builder = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - Dataset.Builder() - } else { - @Suppress("DEPRECATION") Dataset.Builder(makeRemoteView(context, makeEmptyMetadata())) - } - return builder.run { + return Dataset.Builder().run { if (scenario != null) fillWith(scenario, action, credentials) else logcat(LogPriority.ERROR) { "Failed to recover scenario from client state" } build() diff --git a/app/src/main/java/app/passwordstore/util/autofill/AutofillViewUtils.kt b/app/src/main/java/app/passwordstore/util/autofill/AutofillViewUtils.kt index 59fd5cca..321c111a 100644 --- a/app/src/main/java/app/passwordstore/util/autofill/AutofillViewUtils.kt +++ b/app/src/main/java/app/passwordstore/util/autofill/AutofillViewUtils.kt @@ -10,7 +10,6 @@ import android.content.Context import android.content.Intent import android.content.res.Resources import android.graphics.drawable.Icon -import android.os.Build import android.service.autofill.InlinePresentation import android.view.View import android.widget.RemoteViews @@ -47,8 +46,6 @@ fun makeInlinePresentation( imeSpec: InlinePresentationSpec, metadata: DatasetMetadata, ): InlinePresentation? { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return null - if (UiVersions.INLINE_UI_VERSION_1 !in UiVersions.getVersions(imeSpec.style)) return null val launchIntent = @@ -56,11 +53,7 @@ fun makeInlinePresentation( context, 0, Intent(context, LaunchActivity::class.java), - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_MUTABLE - } else { - 0 - }, + PendingIntent.FLAG_MUTABLE, ) val slice = InlineSuggestionUi.newContentBuilder(launchIntent).run { @@ -101,8 +94,6 @@ fun makeGenerateAndFillMetadata(context: Context) = R.drawable.ic_autofill_new_password, ) -fun makeEmptyMetadata() = DatasetMetadata("PLACEHOLDER", "PLACEHOLDER", R.mipmap.ic_launcher) - fun makeWarningMetadata(context: Context) = DatasetMetadata( context.getString(R.string.oreo_autofill_warning_publisher_dataset_title), diff --git a/app/src/main/java/app/passwordstore/util/crypto/KeystorePreferences.kt b/app/src/main/java/app/passwordstore/util/crypto/KeystorePreferences.kt index f4d43e95..e23ec896 100644 --- a/app/src/main/java/app/passwordstore/util/crypto/KeystorePreferences.kt +++ b/app/src/main/java/app/passwordstore/util/crypto/KeystorePreferences.kt @@ -7,7 +7,6 @@ package app.passwordstore.util.crypto import android.content.Context import android.content.SharedPreferences -import android.os.Build import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import android.security.keystore.StrongBoxUnavailableException @@ -66,18 +65,14 @@ fun keystoreAesKey( .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .apply(configure) - .apply { - if (useStrongBox && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - setIsStrongBoxBacked(true) - } - } + .setIsStrongBoxBacked(useStrongBox) .build() return KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore").run { init(spec) generateKey() } } - if (!strongBox || Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return generate(false) + if (!strongBox) return generate(false) return try { generate(true) } catch (_: StrongBoxUnavailableException) { @@ -91,14 +86,10 @@ fun keystoreAesKey( */ fun KeyGenParameterSpec.Builder.requireUserAuthentication(seconds: Int) { setUserAuthenticationRequired(true) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - setUserAuthenticationParameters( - seconds, - KeyProperties.AUTH_DEVICE_CREDENTIAL or KeyProperties.AUTH_BIOMETRIC_STRONG, - ) - } else { - @Suppress("DEPRECATION") setUserAuthenticationValidityDurationSeconds(seconds) - } + setUserAuthenticationParameters( + seconds, + KeyProperties.AUTH_DEVICE_CREDENTIAL or KeyProperties.AUTH_BIOMETRIC_STRONG, + ) } /** diff --git a/app/src/main/java/app/passwordstore/util/extensions/AndroidExtensions.kt b/app/src/main/java/app/passwordstore/util/extensions/AndroidExtensions.kt index 6528334d..9bc93d8e 100644 --- a/app/src/main/java/app/passwordstore/util/extensions/AndroidExtensions.kt +++ b/app/src/main/java/app/passwordstore/util/extensions/AndroidExtensions.kt @@ -30,7 +30,7 @@ import com.github.michaelbull.result.Result import com.google.android.material.snackbar.Snackbar import logcat.logcat -/** Get an instance of [AutofillManager]. Only available on Android Oreo and above */ +/** Get an instance of [AutofillManager] */ val Context.autofillManager: AutofillManager? get() = getSystemService() diff --git a/app/src/main/java/app/passwordstore/util/git/sshj/SshKey.kt b/app/src/main/java/app/passwordstore/util/git/sshj/SshKey.kt index 7c8f5ba5..e2a2c2dd 100644 --- a/app/src/main/java/app/passwordstore/util/git/sshj/SshKey.kt +++ b/app/src/main/java/app/passwordstore/util/git/sshj/SshKey.kt @@ -7,7 +7,6 @@ package app.passwordstore.util.git.sshj import android.content.Context import android.content.pm.PackageManager import android.net.Uri -import android.os.Build import android.provider.OpenableColumns import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyInfo @@ -157,9 +156,7 @@ object SshKey { context.sharedPrefs.edit { putString(PreferenceKeys.GIT_REMOTE_KEY_TYPE, value?.value) } private val isStrongBoxSupported by unsafeLazy { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) - context.packageManager.hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE) - else false + context.packageManager.hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE) } private enum class Type(val value: String) { @@ -198,9 +195,7 @@ object SshKey { setKeySize(256) setAlgorithmParameterSpec(java.security.spec.ECGenParameterSpec("secp256r1")) setDigests(KeyProperties.DIGEST_SHA256) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - setIsStrongBoxBacked(isStrongBoxSupported) - } + setIsStrongBoxBacked(isStrongBoxSupported) }, ), } @@ -316,17 +311,7 @@ object SshKey { val parameterSpec = KeyGenParameterSpec.Builder(KEYSTORE_ALIAS, KeyProperties.PURPOSE_SIGN).run { apply(algorithm.applyToSpec) - if (requireAuthentication) { - setUserAuthenticationRequired(true) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - setUserAuthenticationParameters( - 30, - KeyProperties.AUTH_DEVICE_CREDENTIAL or KeyProperties.AUTH_BIOMETRIC_STRONG, - ) - } else { - @Suppress("DEPRECATION") setUserAuthenticationValidityDurationSeconds(30) - } - } + if (requireAuthentication) requireUserAuthentication(30) build() } val keyPair = diff --git a/app/src/main/java/app/passwordstore/util/services/ClipboardService.kt b/app/src/main/java/app/passwordstore/util/services/ClipboardService.kt index b4bb0438..3347da78 100644 --- a/app/src/main/java/app/passwordstore/util/services/ClipboardService.kt +++ b/app/src/main/java/app/passwordstore/util/services/ClipboardService.kt @@ -12,7 +12,6 @@ import android.app.PendingIntent import android.app.Service import android.content.ClipData import android.content.Intent -import android.os.Build import android.os.IBinder import androidx.core.app.NotificationCompat import androidx.core.content.getSystemService @@ -121,11 +120,7 @@ class ClipboardService : Service() { this, 0, clearIntent, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE - } else { - PendingIntent.FLAG_UPDATE_CURRENT - }, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE, ) val notification = createNotification(pendingIntent, clearTimeMs) diff --git a/app/src/main/java/app/passwordstore/util/services/OreoAutofillService.kt b/app/src/main/java/app/passwordstore/util/services/OreoAutofillService.kt index beeb4be7..a8b525d5 100644 --- a/app/src/main/java/app/passwordstore/util/services/OreoAutofillService.kt +++ b/app/src/main/java/app/passwordstore/util/services/OreoAutofillService.kt @@ -5,7 +5,6 @@ package app.passwordstore.util.services import android.content.Context -import android.os.Build import android.os.CancellationSignal import android.service.autofill.AutofillService import android.service.autofill.FillCallback @@ -74,16 +73,12 @@ class OreoAutofillService : AutofillService() { return } if (structure.activityComponent.packageName in DENYLISTED_PACKAGES) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - callback.onSuccess( - FillResponse.Builder().run { - disableAutofill(DISABLE_AUTOFILL_DURATION_MS) - build() - } - ) - } else { - callback.onSuccess(null) - } + callback.onSuccess( + FillResponse.Builder().run { + disableAutofill(DISABLE_AUTOFILL_DURATION_MS) + build() + } + ) return } val formToFill = |
