summaryrefslogtreecommitdiffstats
path: root/app/src/main/java
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-05 12:05:44 +0100
committerDanilo M. <danix@danix.xyz>2026-11-05 12:10:18 +0100
commitc85e7c8801caf7a73b46dda9ebf75be023cad9b4 (patch)
treec180a57fd54e9b09e84297f9fc51670bf5f4bfab /app/src/main/java
parent73486a180ba3ead1890924920094e7e678a8ef76 (diff)
downloadHardenedPass-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')
-rw-r--r--app/src/main/java/app/passwordstore/Application.kt39
-rw-r--r--app/src/main/java/app/passwordstore/injection/AutofillResponseBuilderModule.kt11
-rw-r--r--app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt7
-rw-r--r--app/src/main/java/app/passwordstore/ui/autofill/AutofillFilterView.kt7
-rw-r--r--app/src/main/java/app/passwordstore/ui/autofill/AutofillPublisherChangedActivity.kt7
-rw-r--r--app/src/main/java/app/passwordstore/ui/crypto/CryptoExtensions.kt17
-rw-r--r--app/src/main/java/app/passwordstore/ui/editor/EditorViewModel.kt10
-rw-r--r--app/src/main/java/app/passwordstore/ui/settings/ProxyViewModel.kt6
-rw-r--r--app/src/main/java/app/passwordstore/util/auth/BiometricAuthenticator.kt9
-rw-r--r--app/src/main/java/app/passwordstore/util/autofill/Api26AutofillResponseBuilder.kt173
-rw-r--r--app/src/main/java/app/passwordstore/util/autofill/Api30AutofillResponseBuilder.kt1
-rw-r--r--app/src/main/java/app/passwordstore/util/autofill/AutofillResponseBuilder.kt11
-rw-r--r--app/src/main/java/app/passwordstore/util/autofill/AutofillViewUtils.kt11
-rw-r--r--app/src/main/java/app/passwordstore/util/crypto/KeystorePreferences.kt21
-rw-r--r--app/src/main/java/app/passwordstore/util/extensions/AndroidExtensions.kt2
-rw-r--r--app/src/main/java/app/passwordstore/util/git/sshj/SshKey.kt21
-rw-r--r--app/src/main/java/app/passwordstore/util/services/ClipboardService.kt7
-rw-r--r--app/src/main/java/app/passwordstore/util/services/OreoAutofillService.kt17
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 =