diff options
48 files changed, 87 insertions, 497 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 15d6447e..90375040 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,8 +30,7 @@ android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppThemeM3" - tools:ignore="GoogleAppIndexingWarning" - tools:targetApi="s"> + tools:ignore="GoogleAppIndexingWarning"> <activity android:name=".ui.main.LaunchActivity" 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 = diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index a0a20dc7..7baf4113 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -37,7 +37,6 @@ <string name="git_commit_move_multiple_text">Verschiebe mehrere Passwörter nach %1$s.</string> <string name="git_commit_gpg_id">Initialisiere PGP-IDs in %1$s.</string> <!-- PGPHandler --> - <string name="clipboard_copied_text">In die Zwischenablage kopiert</string> <string name="file_toast_text">Bitte setzen Sie einen Dateinamen.</string> <string name="empty_toast_text">Sie können kein leeres Passwort oder leere Extra-Angaben setzen.</string> <!-- Git Async Task --> @@ -232,7 +231,6 @@ <string name="pref_app_theme_title">App-Farbthema</string> <string name="pref_app_theme_value_light">Hell</string> <string name="pref_app_theme_value_dark">Dunkel</string> - <string name="pref_app_theme_value_battery_saver">Durch Energiesparmodus gesetzt</string> <string name="pref_app_theme_value_follow_system">Systemstandard</string> <string name="connection_mode_ssh_key">SSH-Schlüssel</string> <string name="connection_mode_basic_authentication">Passwort</string> diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index d4d4d424..57be2b30 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -36,7 +36,6 @@ <string name="git_commit_move_text">Renommer %1$sà %2$s. </string> <string name="git_commit_move_multiple_text">Déplacement de mots de passe vers %1$s.</string> <!-- PGPHandler --> - <string name="clipboard_copied_text">Copié dans le presse-papiers</string> <string name="file_toast_text">Veuillez fournir un nom de fichier</string> <string name="empty_toast_text">Vous ne pouvez pas utiliser un mot de passe vide ou des données supplémentaires vide</string> <!-- Git Async Task --> @@ -220,7 +219,6 @@ <string name="pref_app_theme_title">Thème de l\'application</string> <string name="pref_app_theme_value_light">Clair</string> <string name="pref_app_theme_value_dark">Sombre</string> - <string name="pref_app_theme_value_battery_saver">Défini par l\'économiseur de batterie</string> <string name="pref_app_theme_value_follow_system">Valeur par défaut du système</string> <string name="connection_mode_ssh_key">Clé SSH</string> <string name="connection_mode_basic_authentication">Mot de passe</string> diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index f966230c..b7a59428 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -37,7 +37,6 @@ <string name="git_commit_move_multiple_text">Mover varios contrasinais a %1$s.</string> <string name="git_commit_gpg_id">Iniciar cos IDs GPG en %1$s.</string> <!-- PGPHandler --> - <string name="clipboard_copied_text">Copiado ao portapapeis</string> <string name="file_toast_text">Debes proporcionar un nome de ficheiro</string> <string name="empty_toast_text">Non podes deixar baleiro o contrasinal ou o contido extra</string> <!-- Git Async Task --> @@ -234,7 +233,6 @@ a app desde unha fonte de confianza, como a Play Store, Amazon Appstore, F-Droid <string name="pref_app_theme_title">Decorado da App</string> <string name="pref_app_theme_value_light">Claro</string> <string name="pref_app_theme_value_dark">Escuro</string> - <string name="pref_app_theme_value_battery_saver">Establecido polo Aforrador de enerxía</string> <string name="pref_app_theme_value_follow_system">Por omisión do sistema</string> <string name="connection_mode_ssh_key">Chave SSH</string> <string name="connection_mode_basic_authentication">Contrasinal</string> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index e7c05909..e15b903f 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -36,7 +36,6 @@ <string name="git_commit_move_text">Rinomina %1$s in %2$s.</string> <string name="git_commit_move_multiple_text">Sposta più password in %1$s.</string> <!-- PGPHandler --> - <string name="clipboard_copied_text">Copiato negli appunti</string> <string name="file_toast_text">Inserisci un nome per il file</string> <string name="empty_toast_text">Non puoi usare una password vuota o dei contenuti extra vuoti</string> <!-- Git Async Task --> @@ -215,7 +214,6 @@ <string name="pref_app_theme_title">Tema dell\'App</string> <string name="pref_app_theme_value_light">Chiaro</string> <string name="pref_app_theme_value_dark">Scuro</string> - <string name="pref_app_theme_value_battery_saver">Impostato dal Risparmio Energetico</string> <string name="pref_app_theme_value_follow_system">Predefinito del sistema</string> <string name="connection_mode_ssh_key">Chiave SSH</string> <string name="connection_mode_basic_authentication">Password</string> diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index add3de87..f6535e05 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -35,7 +35,6 @@ <string name="git_commit_move_multiple_text">여러개의 비밀번호를 %1$s 로 옮겼습니다.</string> <string name="git_commit_gpg_id">%1$s의 GPG ID를 초기화 합니다.</string> <!-- PGPHandler --> - <string name="clipboard_copied_text">클립보드로 복사됨</string> <string name="file_toast_text">파일 이름이 필요합니다</string> <string name="empty_toast_text">비어있는 비밀번호나 추가 정보를 사용할 수 없습니다</string> <!-- Git Async Task --> @@ -228,7 +227,6 @@ <string name="pref_app_theme_title">앱 테마</string> <string name="pref_app_theme_value_light">밝은 테마</string> <string name="pref_app_theme_value_dark">어두운 테마</string> - <string name="pref_app_theme_value_battery_saver">배터리 절약모드 설정</string> <string name="pref_app_theme_value_follow_system">시스템 기본값</string> <string name="connection_mode_ssh_key">SSH 키</string> <string name="connection_mode_basic_authentication">비밀번호</string> diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index 4d41b2f6..d130bc70 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -35,7 +35,6 @@ <string name="git_commit_move_text">Zmień nazwę %1$s na %2$s.</string> <string name="git_commit_move_multiple_text">Przenieś hasła do %1$s.</string> <!-- PGPHandler --> - <string name="clipboard_copied_text">Skopiowano do schowka</string> <string name="file_toast_text">Podaj nazwę pliku</string> <string name="empty_toast_text">Nie możesz użyć pustego hasła lub pustej dodatkowej zawartości</string> <!-- Git Async Task --> @@ -227,7 +226,6 @@ <string name="pref_app_theme_title">Motyw aplikacji</string> <string name="pref_app_theme_value_light">Jasny</string> <string name="pref_app_theme_value_dark">Ciemny</string> - <string name="pref_app_theme_value_battery_saver">Ustawiany przez funkcję oszczędzania energii</string> <string name="pref_app_theme_value_follow_system">Domyślny</string> <string name="connection_mode_ssh_key">Klucz SSH</string> <string name="connection_mode_basic_authentication">Hasło</string> diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 14e862c5..4ee08b50 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -37,7 +37,6 @@ <string name="git_commit_move_multiple_text">Mova múltiplas senhas para %1$s.</string> <string name="git_commit_gpg_id">Inicializar IDs GPG em %1$s.</string> <!-- PGPHandler --> - <string name="clipboard_copied_text">Copiado para a área de transferência</string> <string name="file_toast_text">Por favor, informe um nome de arquivo</string> <string name="empty_toast_text">Você não pode usar uma senha vazia ou conteúdo extra vazio</string> <!-- Git Async Task --> @@ -232,7 +231,6 @@ <string name="pref_app_theme_title">Tema do aplicativo</string> <string name="pref_app_theme_value_light">Claro</string> <string name="pref_app_theme_value_dark">Escuro</string> - <string name="pref_app_theme_value_battery_saver">Configurado pela Economia de bateria</string> <string name="pref_app_theme_value_follow_system">Padrão do sistema</string> <string name="connection_mode_ssh_key">Chave SSH</string> <string name="connection_mode_basic_authentication">Senha</string> diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ddc33aa3..a966640d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -41,7 +41,6 @@ <string name="git_commit_move_multiple_text">Переместить несколько паролей в %1$s.</string> <string name="git_commit_gpg_id">Инициализация GPG ID через %1$s.</string> <!-- PGPHandler --> - <string name="clipboard_copied_text">Скопировано в буфер обмена</string> <string name="file_toast_text">Пожалуйста, укажите имя файла</string> <string name="empty_toast_text">Вы не можете использовать пустой пароль или пустое поле информации</string> <!-- Git Async Task --> @@ -225,7 +224,6 @@ <string name="pref_app_theme_title">Тема приложения</string> <string name="pref_app_theme_value_light">Светлая</string> <string name="pref_app_theme_value_dark">Темная</string> - <string name="pref_app_theme_value_battery_saver">Задается экономией батареи</string> <string name="pref_app_theme_value_follow_system">Системная</string> <string name="connection_mode_ssh_key">SSH ключ</string> <string name="connection_mode_basic_authentication">Пароль</string> diff --git a/app/src/main/res/values-v29/arrays.xml b/app/src/main/res/values-v29/arrays.xml deleted file mode 100644 index aa78032c..00000000 --- a/app/src/main/res/values-v29/arrays.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<resources> - <string-array name="app_theme_options"> - <item>@string/pref_app_theme_value_light</item> - <item>@string/pref_app_theme_value_dark</item> - <item>@string/pref_app_theme_value_follow_system</item> - </string-array> - <string-array name="app_theme_values"> - <item>light</item> - <item>dark</item> - <item>follow_system</item> - </string-array> -</resources> diff --git a/app/src/main/res/values-v29/prefs.xml b/app/src/main/res/values-v29/prefs.xml deleted file mode 100644 index 383b5f8b..00000000 --- a/app/src/main/res/values-v29/prefs.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<resources> - <string name="app_theme_def" translatable="false">follow_system</string> -</resources> diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 3f6abeb3..dae54d05 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -36,7 +36,6 @@ <string name="git_commit_move_multiple_text">将多个密码移至%1$s</string> <string name="git_commit_gpg_id">初始化 %1$s 中的 GPG ID</string> <!-- PGPHandler --> - <string name="clipboard_copied_text">复制到剪切板</string> <string name="file_toast_text">请提供文件名</string> <string name="empty_toast_text">您不能使用空密码或空额外内容</string> <!-- Git Async Task --> @@ -233,7 +232,6 @@ <string name="pref_app_theme_title">应用主题</string> <string name="pref_app_theme_value_light">浅色</string> <string name="pref_app_theme_value_dark">深色</string> - <string name="pref_app_theme_value_battery_saver">由省电模式设定</string> <string name="pref_app_theme_value_follow_system">系统默认</string> <string name="connection_mode_ssh_key">SSH密钥</string> <string name="connection_mode_basic_authentication">密码</string> diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 2891987d..dde1a99f 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -33,12 +33,12 @@ <string-array name="app_theme_options"> <item>@string/pref_app_theme_value_light</item> <item>@string/pref_app_theme_value_dark</item> - <item>@string/pref_app_theme_value_battery_saver</item> + <item>@string/pref_app_theme_value_follow_system</item> </string-array> <string-array name="app_theme_values"> <item>light</item> <item>dark</item> - <item>battery_saver</item> + <item>follow_system</item> </string-array> </resources> diff --git a/app/src/main/res/values/prefs.xml b/app/src/main/res/values/prefs.xml index a5977d9d..383b5f8b 100644 --- a/app/src/main/res/values/prefs.xml +++ b/app/src/main/res/values/prefs.xml @@ -4,5 +4,5 @@ --> <resources> - <string name="app_theme_def" translatable="false">battery_saver</string> + <string name="app_theme_def" translatable="false">follow_system</string> </resources> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f1a858fd..755fa9bc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -44,7 +44,6 @@ <string name="git_commit_gpg_id">Initialize GPG IDs in %1$s.</string> <!-- PGPHandler --> - <string name="clipboard_copied_text">Copied to clipboard</string> <string name="file_toast_text">Please provide a file name</string> <string name="empty_toast_text">You cannot use an empty password or empty extra content</string> @@ -263,7 +262,6 @@ <string name="pref_app_theme_title">App theme</string> <string name="pref_app_theme_value_light">Light</string> <string name="pref_app_theme_value_dark">Dark</string> - <string name="pref_app_theme_value_battery_saver">Set by Battery Saver</string> <string name="pref_app_theme_value_follow_system">System default</string> <string name="connection_mode_ssh_key">SSH key</string> <string name="connection_mode_basic_authentication">Password</string> diff --git a/app/src/test/java/app/passwordstore/util/viewmodel/StrictDomainRegexTest.kt b/app/src/test/java/app/passwordstore/util/viewmodel/StrictDomainRegexTest.kt index 0ddb72f8..59c1de05 100644 --- a/app/src/test/java/app/passwordstore/util/viewmodel/StrictDomainRegexTest.kt +++ b/app/src/test/java/app/passwordstore/util/viewmodel/StrictDomainRegexTest.kt @@ -10,13 +10,11 @@ import kotlin.test.assertNull import kotlin.test.assertTrue import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config private infix fun String.matchedForDomain(domain: String) = SearchableRepositoryViewModel.generateStrictDomainRegex(domain)?.containsMatchIn(this) == true @RunWith(RobolectricTestRunner::class) -@Config(sdk = [23]) class StrictDomainRegexTest { @Test diff --git a/autofill-parser/api.txt b/autofill-parser/api.txt index 89cfaa3c..09b2974f 100644 --- a/autofill-parser/api.txt +++ b/autofill-parser/api.txt @@ -12,7 +12,7 @@ package com.github.androidpasswordstore.autofillparser { method public static String computeCertificatesHash(android.content.Context context, String appPackage); } - @RequiresApi(android.os.Build.VERSION_CODES.O) public abstract sealed nonexhaustive class AutofillScenario<T> { + public abstract sealed nonexhaustive class AutofillScenario<T> { method @InaccessibleFromKotlin public final java.util.List<T> getFieldsToSave(); method @InaccessibleFromKotlin public final boolean getHasFieldsToSave(); method @InaccessibleFromKotlin public final boolean getHasPasswordFieldsToSave(); @@ -35,13 +35,13 @@ package com.github.androidpasswordstore.autofillparser { } public final class AutofillScenarioKt { - method @KotlinOnly @RequiresApi(android.os.Build.VERSION_CODES.O) public static void fillWith(android.service.autofill.Dataset.Builder, com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> scenario, com.github.androidpasswordstore.autofillparser.AutofillAction action, com.github.androidpasswordstore.autofillparser.Credentials? credentials); - method @InaccessibleFromKotlin @RequiresApi(android.os.Build.VERSION_CODES.O) public static void fillWithAutofillId(android.service.autofill.Dataset.Builder, com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> scenario, com.github.androidpasswordstore.autofillparser.AutofillAction action, com.github.androidpasswordstore.autofillparser.Credentials? credentials); - method @InaccessibleFromKotlin @RequiresApi(android.os.Build.VERSION_CODES.O) public static String? getPasswordValue(com.github.androidpasswordstore.autofillparser.AutofillScenario<? extends android.app.assist.AssistStructure.ViewNode>); - method @InaccessibleFromKotlin @RequiresApi(android.os.Build.VERSION_CODES.O) public static String? getUsernameValue(com.github.androidpasswordstore.autofillparser.AutofillScenario<? extends android.app.assist.AssistStructure.ViewNode>); - method @RequiresApi(android.os.Build.VERSION_CODES.O) public static com.github.androidpasswordstore.autofillparser.AutofillScenario<android.app.assist.AssistStructure.ViewNode>? recoverNodes(com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId>, android.app.assist.AssistStructure structure); - property @RequiresApi(android.os.Build.VERSION_CODES.O) public static String? com.github.androidpasswordstore.autofillparser.AutofillScenario<android.app.assist.AssistStructure.ViewNode>.passwordValue; - property @RequiresApi(android.os.Build.VERSION_CODES.O) public static String? com.github.androidpasswordstore.autofillparser.AutofillScenario<android.app.assist.AssistStructure.ViewNode>.usernameValue; + method @KotlinOnly public static void fillWith(android.service.autofill.Dataset.Builder, com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> scenario, com.github.androidpasswordstore.autofillparser.AutofillAction action, com.github.androidpasswordstore.autofillparser.Credentials? credentials); + method @InaccessibleFromKotlin public static void fillWithAutofillId(android.service.autofill.Dataset.Builder, com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> scenario, com.github.androidpasswordstore.autofillparser.AutofillAction action, com.github.androidpasswordstore.autofillparser.Credentials? credentials); + method @InaccessibleFromKotlin public static String? getPasswordValue(com.github.androidpasswordstore.autofillparser.AutofillScenario<? extends android.app.assist.AssistStructure.ViewNode>); + method @InaccessibleFromKotlin public static String? getUsernameValue(com.github.androidpasswordstore.autofillparser.AutofillScenario<? extends android.app.assist.AssistStructure.ViewNode>); + method public static com.github.androidpasswordstore.autofillparser.AutofillScenario<android.app.assist.AssistStructure.ViewNode>? recoverNodes(com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId>, android.app.assist.AssistStructure structure); + property public static String? com.github.androidpasswordstore.autofillparser.AutofillScenario<android.app.assist.AssistStructure.ViewNode>.passwordValue; + property public static String? com.github.androidpasswordstore.autofillparser.AutofillScenario<android.app.assist.AssistStructure.ViewNode>.usernameValue; } public enum BrowserAutofillSupportLevel { @@ -68,10 +68,10 @@ package com.github.androidpasswordstore.autofillparser { } public final class FeatureAndTrustDetectionKt { - method @RequiresApi(android.os.Build.VERSION_CODES.O) public static java.util.List<kotlin.Pair<java.lang.String,com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel>> getInstalledBrowsersWithAutofillSupportLevel(android.content.Context context); + method public static java.util.List<kotlin.Pair<java.lang.String,com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel>> getInstalledBrowsersWithAutofillSupportLevel(android.content.Context context); } - @RequiresApi(android.os.Build.VERSION_CODES.O) public final class FillableForm { + public final class FillableForm { method @InaccessibleFromKotlin public com.github.androidpasswordstore.autofillparser.FormOrigin getFormOrigin(); method @InaccessibleFromKotlin public java.util.List<android.view.autofill.AutofillId> getIgnoredIds(); method @InaccessibleFromKotlin public Integer? getSaveFlags(); @@ -88,7 +88,7 @@ package com.github.androidpasswordstore.autofillparser { method public com.github.androidpasswordstore.autofillparser.FillableForm? parseAssistStructure(android.content.Context context, android.app.assist.AssistStructure structure, boolean isManualRequest, optional kotlin.sequences.Sequence<java.lang.String> customSuffixes); } - @RequiresApi(android.os.Build.VERSION_CODES.O) public final class FixedSaveCallback { + public final class FixedSaveCallback { ctor public FixedSaveCallback(android.content.Context context, android.service.autofill.SaveCallback callback); method public void onFailure(CharSequence message); method public void onSuccess(android.content.IntentSender intentSender); diff --git a/autofill-parser/build.gradle.kts b/autofill-parser/build.gradle.kts index 39157b37..30a952b9 100644 --- a/autofill-parser/build.gradle.kts +++ b/autofill-parser/build.gradle.kts @@ -11,10 +11,7 @@ plugins { } android { - defaultConfig { - minSdk = 23 - consumerProguardFiles("consumer-rules.pro") - } + defaultConfig { consumerProguardFiles("consumer-rules.pro") } sourceSets { getByName("test") { resources.directories.add("src/main/assets") } } namespace = "com.github.androidpasswordstore.autofillparser" } diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt index 3dc74f33..eba3b2bc 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt @@ -11,7 +11,6 @@ import android.content.pm.PackageManager.ApplicationInfoFlags import android.os.Build import android.os.Bundle import android.view.autofill.AutofillId -import androidx.annotation.RequiresApi import androidx.core.net.toUri import logcat.logcat @@ -69,7 +68,6 @@ public sealed class FormOrigin(public open val identifier: String) { /** * Manages the detection of fields to fill in an [AssistStructure] and determines the [FormOrigin]. */ -@RequiresApi(Build.VERSION_CODES.O) private class AutofillFormParser( context: Context, structure: AssistStructure, @@ -200,7 +198,6 @@ public data class Credentials(val username: String?, val password: String?, val * Represents a collection of fields in a specific app that can be filled or saved. This is the * entry point to all fill and save features. */ -@RequiresApi(Build.VERSION_CODES.O) public class FillableForm private constructor( public val formOrigin: FormOrigin, diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillHelper.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillHelper.kt index 9ff646c9..b177b5c0 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillHelper.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillHelper.kt @@ -15,7 +15,6 @@ import android.service.autofill.SaveCallback import android.util.Base64 import android.view.autofill.AutofillId import android.widget.Toast -import androidx.annotation.RequiresApi import java.security.MessageDigest import logcat.LogPriority.ERROR import logcat.logcat @@ -56,24 +55,22 @@ public fun computeCertificatesHash(context: Context, appPackage: String): String .signatures .orEmpty() val stableHashOld = stableHash(signaturesOld.map { it.toByteArray() }) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - val info = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - context.packageManager.getPackageInfo( - appPackage, - PackageManager.PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES.toLong()), - ) - } else { - context.packageManager.getPackageInfo(appPackage, PackageManager.GET_SIGNING_CERTIFICATES) - } - val signaturesNew = - info.signingInfo?.signingCertificateHistory ?: info.signingInfo?.apkContentsSigners.orEmpty() - val stableHashNew = stableHash(signaturesNew.map { it.toByteArray() }) - if (stableHashNew != stableHashOld) - logcat("CertificatesHash", ERROR) { - "Mismatch between old and new hash: $stableHashNew != $stableHashOld" - } - } + val info = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + context.packageManager.getPackageInfo( + appPackage, + PackageManager.PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES.toLong()), + ) + } else { + context.packageManager.getPackageInfo(appPackage, PackageManager.GET_SIGNING_CERTIFICATES) + } + val signaturesNew = + info.signingInfo?.signingCertificateHistory ?: info.signingInfo?.apkContentsSigners.orEmpty() + val stableHashNew = stableHash(signaturesNew.map { it.toByteArray() }) + if (stableHashNew != stableHashOld) + logcat("CertificatesHash", ERROR) { + "Mismatch between old and new hash: $stableHashNew != $stableHashOld" + } return stableHashOld } @@ -82,15 +79,12 @@ public fun computeCertificatesHash(context: Context, appPackage: String): String * its `webDomain` and `webScheme`, if available. */ internal val AssistStructure.ViewNode.webOrigin: String? - @RequiresApi(Build.VERSION_CODES.O) get() = webDomain?.let { domain -> - val scheme = - (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) webScheme else null) ?: "https" + val scheme = webScheme ?: "https" "$scheme://$domain" } -@RequiresApi(Build.VERSION_CODES.O) public class FixedSaveCallback(private val context: Context, private val callback: SaveCallback) { private val applicationContext = context.applicationContext @@ -120,13 +114,7 @@ public class FixedSaveCallback(private val context: Context, private val callbac applicationContext.startIntentSender(intentSender, null, 0, 0, 0, options) callback.onSuccess() } - Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> callback.onSuccess(intentSender) - else -> { - callback.onSuccess() - // On SDKs < 28, we cannot advise the Autofill framework to launch the save intent in - // the context of the app that triggered the save request. Hence, we launch it here. - applicationContext.startIntentSender(intentSender, null, 0, 0, 0) - } + else -> callback.onSuccess(intentSender) } } } @@ -147,7 +135,6 @@ private fun visitViewNode( } } -@RequiresApi(Build.VERSION_CODES.O) internal fun AssistStructure.findNodeByAutofillId( autofillId: AutofillId ): AssistStructure.ViewNode? { diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillScenario.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillScenario.kt index 8a5b7fe3..15faabde 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillScenario.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillScenario.kt @@ -11,7 +11,6 @@ import android.service.autofill.Dataset import android.service.autofill.Field import android.view.autofill.AutofillId import android.view.autofill.AutofillValue -import androidx.annotation.RequiresApi import androidx.core.os.BundleCompat import logcat.LogPriority.ERROR import logcat.asLog @@ -30,7 +29,6 @@ public enum class AutofillAction { * [FormField], [AssistStructure.ViewNode] or [AutofillId], depending on how much metadata about the * field is needed and available in the particular situation. */ -@RequiresApi(Build.VERSION_CODES.O) public sealed class AutofillScenario<out T : Any> { public companion object { @@ -187,7 +185,6 @@ public sealed class AutofillScenario<out T : Any> { get() = username != null } -@RequiresApi(Build.VERSION_CODES.O) internal data class ClassifiedAutofillScenario<T : Any>( override val username: T?, override val fillUsername: Boolean, @@ -212,7 +209,6 @@ internal data class ClassifiedAutofillScenario<T : Any>( get() = newPassword.ifEmpty { currentPassword } } -@RequiresApi(Build.VERSION_CODES.O) internal data class GenericAutofillScenario<T : Any>( override val username: T?, override val fillUsername: Boolean, @@ -236,7 +232,6 @@ internal data class GenericAutofillScenario<T : Any>( get() = genericPassword } -@RequiresApi(Build.VERSION_CODES.O) internal fun AutofillScenario<FormField>.passesOriginCheck(singleOriginMode: Boolean): Boolean { return if (singleOriginMode) { // In single origin mode, only the browsers URL bar (which is never filled) should have @@ -249,7 +244,6 @@ internal fun AutofillScenario<FormField>.passesOriginCheck(singleOriginMode: Boo } } -@RequiresApi(Build.VERSION_CODES.O) @JvmName("fillWithAutofillId") public fun Dataset.Builder.fillWith( scenario: AutofillScenario<AutofillId>, @@ -272,7 +266,6 @@ public fun Dataset.Builder.fillWith( } } -@RequiresApi(Build.VERSION_CODES.O) internal inline fun <T : Any, S : Any> AutofillScenario<T>.map( transform: (T) -> S ): AutofillScenario<S> { @@ -292,7 +285,6 @@ internal inline fun <T : Any, S : Any> AutofillScenario<T>.map( return builder.build() } -@RequiresApi(Build.VERSION_CODES.O) @JvmName("toBundleAutofillId") internal fun AutofillScenario<AutofillId>.toBundle(): Bundle = when (this) { @@ -321,7 +313,6 @@ internal fun AutofillScenario<AutofillId>.toBundle(): Bundle = } } -@RequiresApi(Build.VERSION_CODES.O) public fun AutofillScenario<AutofillId>.recoverNodes( structure: AssistStructure ): AutofillScenario<AssistStructure.ViewNode>? { @@ -329,13 +320,11 @@ public fun AutofillScenario<AutofillId>.recoverNodes( } public val AutofillScenario<AssistStructure.ViewNode>.usernameValue: String? - @RequiresApi(Build.VERSION_CODES.O) get() { val value = username?.autofillValue ?: return null return if (value.isText) value.textValue.toString() else null } public val AutofillScenario<AssistStructure.ViewNode>.passwordValue: String? - @RequiresApi(Build.VERSION_CODES.O) get() { val distinctValues = passwordFieldsToSave diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategy.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategy.kt index bc227bad..5e1cebad 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategy.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategy.kt @@ -4,8 +4,6 @@ */ package com.github.androidpasswordstore.autofillparser -import android.os.Build -import androidx.annotation.RequiresApi import com.github.androidpasswordstore.autofillparser.CertaintyLevel.Certain import com.github.androidpasswordstore.autofillparser.CertaintyLevel.Likely @@ -22,7 +20,6 @@ private inline fun <T> Pair<T, T>.none(predicate: T.() -> Boolean) = * The strategy used to detect [AutofillScenario] s; expressed using the DSL implemented in * [AutofillDsl]. */ -@RequiresApi(Build.VERSION_CODES.O) internal val autofillStrategy = strategy { // Match two new password fields, an optional current password field right below or above, and diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategyDsl.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategyDsl.kt index 673c7466..a9b01f25 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategyDsl.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategyDsl.kt @@ -4,14 +4,11 @@ */ package com.github.androidpasswordstore.autofillparser -import android.os.Build -import androidx.annotation.RequiresApi import logcat.LogPriority.WARN import logcat.logcat @DslMarker internal annotation class AutofillDsl -@RequiresApi(Build.VERSION_CODES.O) internal interface FieldMatcher { fun match(fields: List<FormField>, alreadyMatched: List<FormField>): List<FormField>? @@ -72,7 +69,6 @@ internal interface FieldMatcher { } } -@RequiresApi(Build.VERSION_CODES.O) internal class SingleFieldMatcher( private val take: (FormField, List<FormField>) -> Boolean, private val tieBreakers: List<(FormField, List<FormField>) -> Boolean>, @@ -136,7 +132,6 @@ internal class SingleFieldMatcher( } } -@RequiresApi(Build.VERSION_CODES.O) private class PairOfFieldsMatcher( private val take: (Pair<FormField, FormField>, List<FormField>) -> Boolean, private val tieBreakers: List<(Pair<FormField, FormField>, List<FormField>) -> Boolean>, @@ -174,7 +169,6 @@ private class PairOfFieldsMatcher( } } -@RequiresApi(Build.VERSION_CODES.O) internal class AutofillRule private constructor( private val matchers: List<AutofillRuleMatcher>, @@ -384,7 +378,6 @@ private constructor( } } -@RequiresApi(Build.VERSION_CODES.O) internal class AutofillStrategy private constructor(private val rules: List<AutofillRule>) { @AutofillDsl @@ -436,6 +429,5 @@ internal class AutofillStrategy private constructor(private val rules: List<Auto } } -@RequiresApi(Build.VERSION_CODES.O) internal fun strategy(block: AutofillStrategy.Builder.() -> Unit) = AutofillStrategy.Builder().apply(block).build() diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt index de28cd80..2db02f51 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt @@ -11,7 +11,6 @@ import android.content.pm.PackageManager.ResolveInfoFlags import android.os.Build import android.provider.Settings import android.service.autofill.SaveInfo -import androidx.annotation.RequiresApi import androidx.core.net.toUri /** @@ -143,7 +142,6 @@ private fun getBrowserMultiOriginMethod(appPackage: String): BrowserMultiOriginM * Some browsers may not issue save requests automatically and thus need * `FLAG_SAVE_ON_ALL_VIEW_INVISIBLE` to be set. */ -@RequiresApi(Build.VERSION_CODES.O) private val BROWSER_SAVE_FLAG = mapOf( "com.duckduckgo.mobile.android" to 0, @@ -159,7 +157,6 @@ private val BROWSER_SAVE_FLAG = "com.opera.touch" to 0, ) -@RequiresApi(Build.VERSION_CODES.O) private val BROWSER_SAVE_FLAG_IF_NO_ACCESSIBILITY = mapOf( "com.android.chrome" to SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE, @@ -180,7 +177,6 @@ private fun isNoAccessibilityServiceEnabled(context: Context): Boolean { .isNullOrEmpty() } -@RequiresApi(Build.VERSION_CODES.O) private fun getBrowserSaveFlag(context: Context, appPackage: String): Int? = BROWSER_SAVE_FLAG[appPackage] ?: BROWSER_SAVE_FLAG_IF_NO_ACCESSIBILITY[appPackage]?.takeIf { @@ -192,7 +188,6 @@ internal data class BrowserAutofillSupportInfo( val saveFlags: Int?, ) -@RequiresApi(Build.VERSION_CODES.O) internal fun getBrowserAutofillSupportInfoIfTrusted( context: Context, appPackage: String, @@ -215,7 +210,6 @@ public enum class BrowserAutofillSupportLevel { GeneralFillAndSave, } -@RequiresApi(Build.VERSION_CODES.O) private fun getBrowserAutofillSupportLevel( context: Context, appPackage: String, @@ -230,19 +224,9 @@ private fun getBrowserAutofillSupportLevel( BrowserAutofillSupportLevel.PasswordFill browserInfo.saveFlags == null -> BrowserAutofillSupportLevel.GeneralFill else -> BrowserAutofillSupportLevel.GeneralFillAndSave - }.takeUnless { supportLevel -> - // On Android Oreo, only browsers with native Autofill support can be used with Password - // Store - // (compatibility mode is only available on Android Pie and higher). Since all known - // browsers - // with native Autofill support offer full save support as well, we reuse the list of those - // browsers here. - supportLevel != BrowserAutofillSupportLevel.GeneralFillAndSave && - Build.VERSION.SDK_INT < Build.VERSION_CODES.P - } ?: BrowserAutofillSupportLevel.None + } } -@RequiresApi(Build.VERSION_CODES.O) public fun getInstalledBrowsersWithAutofillSupportLevel( context: Context ): List<Pair<String, BrowserAutofillSupportLevel>> { diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FormField.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FormField.kt index 983a8f0a..b8df0664 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FormField.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FormField.kt @@ -5,11 +5,9 @@ package com.github.androidpasswordstore.autofillparser import android.app.assist.AssistStructure -import android.os.Build import android.text.InputType import android.view.View import android.view.autofill.AutofillId -import androidx.annotation.RequiresApi import androidx.autofill.HintConstants import java.util.Locale @@ -24,7 +22,6 @@ internal enum class CertaintyLevel { * Represents a single potentially fillable or saveable field together with all meta data extracted * from its [AssistStructure.ViewNode]. */ -@RequiresApi(Build.VERSION_CODES.O) internal class FormField( node: AssistStructure.ViewNode, private val index: Int, @@ -91,7 +88,6 @@ internal class FormField( .toSet() .toList() - @RequiresApi(Build.VERSION_CODES.O) private fun isSupportedHint(hint: String?) = hint in HINTS_FILLABLE private val EXCLUDED_TERMS = diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/PublicSuffixListCache.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/PublicSuffixListCache.kt index 8eb113d5..d14358e8 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/PublicSuffixListCache.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/PublicSuffixListCache.kt @@ -6,7 +6,6 @@ package com.github.androidpasswordstore.autofillparser import android.content.Context import android.net.InetAddresses -import android.os.Build import android.util.Patterns import kotlinx.coroutines.runBlocking import mozilla.components.lib.publicsuffixlist.PublicSuffixList @@ -45,21 +44,13 @@ internal fun getPublicSuffixPlusOne( // We do not check whether the domain actually exists (actually, not even whether its TLD // exists). As long as we restrict ourselves to syntactically valid domain names, // getPublicSuffixPlusOne will return non-colliding results. - if (!Patterns.DOMAIN_NAME.matcher(domain).matches() || isNumericAddress(domain)) { + if (!Patterns.DOMAIN_NAME.matcher(domain).matches() || InetAddresses.isNumericAddress(domain)) { domain } else { getCanonicalSuffix(context, domain, customSuffixes) } } -private fun isNumericAddress(domain: String): Boolean { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - InetAddresses.isNumericAddress(domain) - } else { - @Suppress("DEPRECATION") Patterns.IP_ADDRESS.matcher(domain).matches() - } -} - /** * Returns: * - [domain], if [domain] equals [suffix]; diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/AndroidCommon.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/AndroidCommon.kt index 94b9c112..70d48586 100644 --- a/build-logic/src/main/kotlin/app/passwordstore/gradle/AndroidCommon.kt +++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/AndroidCommon.kt @@ -20,7 +20,7 @@ object AndroidCommon { fun configure(project: Project) { project.extensions.getByType(CommonExtension::class.java).apply { compileSdk = 35 - defaultConfig.minSdk = 26 + defaultConfig.minSdk = 31 packaging.resources.excludes.add("**/*.version") packaging.resources.excludes.add("**/*.txt") diff --git a/scripts/boot-emulator.sh b/scripts/boot-emulator.sh index 910340f1..92a053f9 100755 --- a/scripts/boot-emulator.sh +++ b/scripts/boot-emulator.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Creates and boots a display-less emulator (defaulting to Android 8.1, our min SDK) +# Creates and boots a display-less emulator (defaulting to Android 12, our min SDK) set -euo pipefail @@ -9,8 +9,8 @@ set -euo pipefail exit 1 } -[ -n "${ANDROID_API_LEVEL:-}" ] || { echo "ANDROID_API_LEVEL not defined; defaulting to 26"; } -API_LEVEL="${ANDROID_API_LEVEL:-26}" +[ -n "${ANDROID_API_LEVEL:-}" ] || { echo "ANDROID_API_LEVEL not defined; defaulting to 31"; } +API_LEVEL="${ANDROID_API_LEVEL:-31}" ARCH="x86_64" if [[ "$(uname -m)" == "arm64" && "$(uname)" == "Darwin" ]]; then diff --git a/scripts/setup-environment.sh b/scripts/setup-environment.sh index 30dd0c19..3c6c2fd1 100755 --- a/scripts/setup-environment.sh +++ b/scripts/setup-environment.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Installs the latest command line tools and sets up the necessary packages for an Android emulator -# for API level $ANDROID_API_LEVEL, or API 30 if unspecified. +# for API level $ANDROID_API_LEVEL, or API 31 if unspecified. set -euo pipefail @@ -23,9 +23,9 @@ else exit 1 fi -[ -n "${ANDROID_API_LEVEL:-}" ] || { echo "ANDROID_API_LEVEL not defined; defaulting to 30"; } +[ -n "${ANDROID_API_LEVEL:-}" ] || { echo "ANDROID_API_LEVEL not defined; defaulting to 31"; } -API_LEVEL="${ANDROID_API_LEVEL:-30}" +API_LEVEL="${ANDROID_API_LEVEL:-31}" unzip -qo /tmp/tools.zip -d "${ANDROID_SDK_ROOT}/latest" mkdir -p "${ANDROID_SDK_ROOT}/cmdline-tools" diff --git a/ui/compose/src/main/kotlin/app/passwordstore/ui/compose/theme/Theme.kt b/ui/compose/src/main/kotlin/app/passwordstore/ui/compose/theme/Theme.kt index 6d762fb4..a268c4b4 100644 --- a/ui/compose/src/main/kotlin/app/passwordstore/ui/compose/theme/Theme.kt +++ b/ui/compose/src/main/kotlin/app/passwordstore/ui/compose/theme/Theme.kt @@ -6,7 +6,6 @@ package app.passwordstore.ui.compose.theme import android.app.Activity -import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme @@ -84,7 +83,7 @@ public fun APSTheme( ) { val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + dynamicColor -> { val context = LocalContext.current if (darkTheme) { dynamicDarkColorScheme(context) |
