aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AGENTS.md9
-rw-r--r--app/build.gradle.kts12
-rw-r--r--app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt2
-rw-r--r--autofill-parser/api.txt74
-rw-r--r--autofill-parser/build.gradle.kts2
-rw-r--r--autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt4
-rw-r--r--autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt4
-rw-r--r--build-logic/build.gradle.kts4
-rw-r--r--build-logic/settings.gradle.kts2
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/AndroidCommon.kt37
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/DependencyUpdatesPlugin.kt1
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinAndroidPlugin.kt7
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinKaptPlugin.kt51
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/PublishedAndroidLibraryPlugin.kt7
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/signing/AppSigning.kt6
-rw-r--r--gradle.properties9
-rw-r--r--gradle/libs.versions.toml21
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin44309 -> 48749 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xgradlew11
-rw-r--r--gradlew.bat86
-rw-r--r--passgen/diceware/build.gradle.kts4
-rw-r--r--settings.gradle.kts2
23 files changed, 153 insertions, 208 deletions
diff --git a/AGENTS.md b/AGENTS.md
index 3fa60b0c..e1ed9d1b 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -33,13 +33,13 @@ Needs JDK 17 and the Android SDK. `local.properties` (gitignored) holds `sdk.dir
./gradlew assembleFreeDebug # debug APK
./gradlew test -PslimTests # unit tests, as CI runs them
./gradlew spotlessApply # formatting (ktfmt); CI runs spotlessCheck
- ./gradlew lintFreeRelease
+ ./gradlew lint # all modules and variants, as CI runs it
Prefix with `CI=1` to match CI: it turns Kotlin warnings into errors, so a deprecation warning
that passes locally fails CI. Before committing, run:
CI=1 ./gradlew spotlessCheck test -PslimTests assembleFreeDebug assembleFreeRelease \
- lintFreeRelease metalavaCheckCompatibilityRelease
+ assembleNonFreeDebug assembleNonFreeRelease lint metalavaCheckCompatibilityRelease
Flavors: `free` (no proprietary dependencies, Sentry replaced by `sentry-stub`) and `nonFree`
(adds Sentry and Play Services SMS OTP).
@@ -63,6 +63,11 @@ itself, so it cannot be referenced with a local `./` path.
and hash included. A dependency bump that moves a baselined jar needs the path updated.
- `LaunchActivity` is exported and forwards `FILE_PATH` to `DecryptActivity` (not exported)
only after `isFileInRepo` checks it canonically resolves inside the store.
+- AGP 9 applies packaging excludes (`AndroidCommon`, e.g. `**/*.txt`) to unit-test java
+ resources as well. A module whose tests load such a file must remove the exclude locally
+ (see `passgen/diceware/build.gradle.kts`).
+- Hilt runs through KSP (no kapt; AGP 9's built-in Kotlin does not support it). Dagger compiler
+ options are `ksp { arg(...) }` in `app/build.gradle.kts`.
- JGit is pinned at 4.5.7 (2019). Newer JGit needs Java 11 APIs Android only partly desugars;
upgrading it is a large, separate job.
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 206a3972..bff7d73a 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -7,11 +7,11 @@
plugins {
id("com.github.android-password-store.android-application")
id("com.github.android-password-store.kotlin-android")
- id("com.github.android-password-store.kotlin-kapt")
id("com.github.android-password-store.versioning-plugin")
id("com.github.android-password-store.sentry")
id("com.github.android-password-store.rename-artifacts")
alias(libs.plugins.hilt)
+ alias(libs.plugins.ksp)
alias(libs.plugins.kotlin.composeCompiler)
}
@@ -35,9 +35,17 @@ android {
}
}
+ksp {
+ // https://dagger.dev/dev-guide/compiler-options
+ arg("dagger.fastInit", "enabled")
+ arg("dagger.experimentalDaggerErrorMessages", "enabled")
+ arg("dagger.hilt.shareTestComponents", "true")
+ arg("dagger.moduleBindingValidation", "ERROR")
+}
+
dependencies {
implementation(platform(libs.compose.bom))
- kapt(libs.dagger.hilt.compiler)
+ ksp(libs.dagger.hilt.compiler)
implementation(libs.androidx.annotation)
coreLibraryDesugaring(libs.android.desugarJdkLibs)
implementation(projects.autofillParser)
diff --git a/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt b/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt
index 2bd75aba..48804e7a 100644
--- a/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt
+++ b/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt
@@ -268,7 +268,7 @@ constructor(
}
.flowOn(dispatcherProvider.io())
- private fun shouldTake(file: File) =
+ private fun shouldTake(file: File): Boolean =
with(file) {
if (showHiddenContents) {
return !file.name.startsWith(".git")
diff --git a/autofill-parser/api.txt b/autofill-parser/api.txt
index a999127f..89cfaa3c 100644
--- a/autofill-parser/api.txt
+++ b/autofill-parser/api.txt
@@ -2,8 +2,6 @@
package com.github.androidpasswordstore.autofillparser {
public enum AutofillAction {
- method public static com.github.androidpasswordstore.autofillparser.AutofillAction valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static com.github.androidpasswordstore.autofillparser.AutofillAction[] values();
enum_constant public static final com.github.androidpasswordstore.autofillparser.AutofillAction FillOtpFromSms;
enum_constant public static final com.github.androidpasswordstore.autofillparser.AutofillAction Generate;
enum_constant public static final com.github.androidpasswordstore.autofillparser.AutofillAction Match;
@@ -14,13 +12,13 @@ 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 class AutofillScenario<T> {
- method public final java.util.List<T> getFieldsToSave();
- method public final boolean getHasFieldsToSave();
- method public final boolean getHasPasswordFieldsToSave();
- method public final boolean getHasUsername();
- method public abstract java.util.List<T> getPasswordFieldsToSave();
- method public abstract T? getUsername();
+ @RequiresApi(android.os.Build.VERSION_CODES.O) 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();
+ method @InaccessibleFromKotlin public final boolean getHasUsername();
+ method @InaccessibleFromKotlin public abstract java.util.List<T> getPasswordFieldsToSave();
+ method @InaccessibleFromKotlin public abstract T? getUsername();
method public final boolean hasFieldsToFillOn(com.github.androidpasswordstore.autofillparser.AutofillAction action);
property public final java.util.List<T> fieldsToSave;
property public final boolean hasFieldsToSave;
@@ -37,15 +35,16 @@ package com.github.androidpasswordstore.autofillparser {
}
public final class AutofillScenarioKt {
- method @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 @RequiresApi(android.os.Build.VERSION_CODES.O) public static String? getPasswordValue(com.github.androidpasswordstore.autofillparser.AutofillScenario<? extends android.app.assist.AssistStructure.ViewNode>);
- method @RequiresApi(android.os.Build.VERSION_CODES.O) public static String? getUsernameValue(com.github.androidpasswordstore.autofillparser.AutofillScenario<? extends android.app.assist.AssistStructure.ViewNode>);
+ 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;
}
public enum BrowserAutofillSupportLevel {
- method public static com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel[] values();
enum_constant public static final com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel FlakyFill;
enum_constant public static final com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel GeneralFill;
enum_constant public static final com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel GeneralFillAndSave;
@@ -59,13 +58,13 @@ package com.github.androidpasswordstore.autofillparser {
method public String? component1();
method public String? component2();
method public String? component3();
- method public com.github.androidpasswordstore.autofillparser.Credentials copy(String? username, String? password, String? otp);
- method public String? getOtp();
- method public String? getPassword();
- method public String? getUsername();
- property public final String? otp;
- property public final String? password;
- property public final String? username;
+ method public com.github.androidpasswordstore.autofillparser.Credentials copy(optional String? username, optional String? password, optional String? otp);
+ method @InaccessibleFromKotlin public String? getOtp();
+ method @InaccessibleFromKotlin public String? getPassword();
+ method @InaccessibleFromKotlin public String? getUsername();
+ property public String? otp;
+ property public String? password;
+ property public String? username;
}
public final class FeatureAndTrustDetectionKt {
@@ -73,15 +72,15 @@ package com.github.androidpasswordstore.autofillparser {
}
@RequiresApi(android.os.Build.VERSION_CODES.O) public final class FillableForm {
- method public com.github.androidpasswordstore.autofillparser.FormOrigin getFormOrigin();
- method public java.util.List<android.view.autofill.AutofillId> getIgnoredIds();
- method public Integer? getSaveFlags();
- method public com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> getScenario();
+ 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();
+ method @InaccessibleFromKotlin public com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> getScenario();
method public android.os.Bundle toClientState();
- property public final com.github.androidpasswordstore.autofillparser.FormOrigin formOrigin;
- property public final java.util.List<android.view.autofill.AutofillId> ignoredIds;
- property public final Integer? saveFlags;
- property public final com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> scenario;
+ property public com.github.androidpasswordstore.autofillparser.FormOrigin formOrigin;
+ property public java.util.List<android.view.autofill.AutofillId> ignoredIds;
+ property public Integer? saveFlags;
+ property public com.github.androidpasswordstore.autofillparser.AutofillScenario<android.view.autofill.AutofillId> scenario;
field public static final com.github.androidpasswordstore.autofillparser.FillableForm.Companion Companion;
}
@@ -95,8 +94,8 @@ package com.github.androidpasswordstore.autofillparser {
method public void onSuccess(android.content.IntentSender intentSender);
}
- public abstract sealed class FormOrigin {
- method public String getIdentifier();
+ public abstract sealed exhaustive class FormOrigin {
+ method @InaccessibleFromKotlin public String getIdentifier();
method public final String getPrettyIdentifier(android.content.Context context, optional boolean untrusted);
method public final android.os.Bundle toBundle();
property public String identifier;
@@ -106,7 +105,7 @@ package com.github.androidpasswordstore.autofillparser {
public static final class FormOrigin.App extends com.github.androidpasswordstore.autofillparser.FormOrigin {
ctor public FormOrigin.App(String identifier);
method public String component1();
- method public com.github.androidpasswordstore.autofillparser.FormOrigin.App copy(String identifier);
+ method public com.github.androidpasswordstore.autofillparser.FormOrigin.App copy(optional String identifier);
property public String identifier;
}
@@ -117,7 +116,7 @@ package com.github.androidpasswordstore.autofillparser {
public static final class FormOrigin.Web extends com.github.androidpasswordstore.autofillparser.FormOrigin {
ctor public FormOrigin.Web(String identifier);
method public String component1();
- method public com.github.androidpasswordstore.autofillparser.FormOrigin.Web copy(String identifier);
+ method public com.github.androidpasswordstore.autofillparser.FormOrigin.Web copy(optional String identifier);
property public String identifier;
}
@@ -127,12 +126,3 @@ package com.github.androidpasswordstore.autofillparser {
}
-package mozilla.components.lib.publicsuffixlist {
-
- public final class PublicSuffixListLoaderTest {
- ctor public PublicSuffixListLoaderTest();
- method public void testLoadingBundledPublicSuffixList();
- }
-
-}
-
diff --git a/autofill-parser/build.gradle.kts b/autofill-parser/build.gradle.kts
index 472f78fb..39157b37 100644
--- a/autofill-parser/build.gradle.kts
+++ b/autofill-parser/build.gradle.kts
@@ -15,7 +15,7 @@ android {
minSdk = 23
consumerProguardFiles("consumer-rules.pro")
}
- sourceSets { getByName("test") { resources.srcDir("src/main/assets") } }
+ 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 c273bdfc..3dc74f33 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
@@ -8,11 +8,11 @@ import android.app.assist.AssistStructure
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ApplicationInfoFlags
-import android.net.Uri
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
/**
@@ -150,7 +150,7 @@ private class AutofillFormParser(
}
private fun webOriginToFormOrigin(context: Context, origin: String): FormOrigin? {
- val uri = Uri.parse(origin) ?: return null
+ val uri = origin.toUri()
val scheme = uri.scheme ?: return null
if (scheme !in SUPPORTED_SCHEMES) return null
val host = uri.host ?: return null
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 2da3a102..d531dd49 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
@@ -8,11 +8,11 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ResolveInfoFlags
-import android.net.Uri
import android.os.Build
import android.provider.Settings
import android.service.autofill.SaveInfo
import androidx.annotation.RequiresApi
+import androidx.core.net.toUri
/**
* In order to add a new browser, do the following:
@@ -244,7 +244,7 @@ private fun getBrowserAutofillSupportLevel(
public fun getInstalledBrowsersWithAutofillSupportLevel(
context: Context
): List<Pair<String, BrowserAutofillSupportLevel>> {
- val testWebIntent = Intent(Intent.ACTION_VIEW).apply { data = Uri.parse("https://example.org") }
+ val testWebIntent = Intent(Intent.ACTION_VIEW).apply { data = "https://example.org".toUri() }
val installedBrowsers =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.packageManager.queryIntentActivities(
diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts
index 2bb5b6ea..dd9c0c8b 100644
--- a/build-logic/build.gradle.kts
+++ b/build-logic/build.gradle.kts
@@ -29,10 +29,6 @@ gradlePlugin {
id = "com.github.android-password-store.kotlin-common"
implementationClass = "app.passwordstore.gradle.KotlinCommonPlugin"
}
- register("kotlin-kapt") {
- id = "com.github.android-password-store.kotlin-kapt"
- implementationClass = "app.passwordstore.gradle.KotlinKaptPlugin"
- }
register("kotlin-jvm-library") {
id = "com.github.android-password-store.kotlin-jvm-library"
implementationClass = "app.passwordstore.gradle.KotlinJVMLibrary"
diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts
index 1488630b..ea43de31 100644
--- a/build-logic/settings.gradle.kts
+++ b/build-logic/settings.gradle.kts
@@ -6,7 +6,7 @@
rootProject.name = "build-logic"
-plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" }
+plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" }
dependencyResolutionManagement {
repositories {
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 a26faea9..94b9c112 100644
--- a/build-logic/src/main/kotlin/app/passwordstore/gradle/AndroidCommon.kt
+++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/AndroidCommon.kt
@@ -8,37 +8,29 @@ package app.passwordstore.gradle
import app.passwordstore.gradle.LintConfig.configureLint
import app.passwordstore.gradle.flavors.configureSlimTests
import com.android.build.api.dsl.ApplicationExtension
+import com.android.build.api.dsl.CommonExtension
import com.android.build.api.dsl.LibraryExtension
-import com.android.build.gradle.TestedExtension
import org.gradle.accessors.dm.LibrariesForLibs
import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
-import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.findByType
import org.gradle.kotlin.dsl.withType
object AndroidCommon {
fun configure(project: Project) {
- project.extensions.configure<TestedExtension> {
- compileSdkVersion(35)
- defaultConfig {
- minSdk = 26
- targetSdk = 34
- }
+ project.extensions.getByType(CommonExtension::class.java).apply {
+ compileSdk = 35
+ defaultConfig.minSdk = 26
- packagingOptions {
- resources.excludes.add("**/*.version")
- resources.excludes.add("**/*.txt")
- resources.excludes.add("**/*.kotlin_module")
- resources.excludes.add("**/plugin.properties")
- resources.excludes.add("**/META-INF/AL2.0")
- resources.excludes.add("**/META-INF/LGPL2.1")
- }
+ packaging.resources.excludes.add("**/*.version")
+ packaging.resources.excludes.add("**/*.txt")
+ packaging.resources.excludes.add("**/*.kotlin_module")
+ packaging.resources.excludes.add("**/plugin.properties")
+ packaging.resources.excludes.add("**/META-INF/AL2.0")
+ packaging.resources.excludes.add("**/META-INF/LGPL2.1")
- testOptions {
- animationsDisabled = true
- unitTests.isReturnDefaultValues = true
- }
+ testOptions.animationsDisabled = true
+ testOptions.unitTests.isReturnDefaultValues = true
project.tasks.withType<Test>().configureEach {
jvmArgs(
@@ -49,7 +41,10 @@ object AndroidCommon {
project.configureSlimTests()
}
- project.extensions.findByType<ApplicationExtension>()?.run { lint.configureLint(project) }
+ project.extensions.findByType<ApplicationExtension>()?.run {
+ defaultConfig.targetSdk = 34
+ lint.configureLint(project)
+ }
project.extensions.findByType<LibraryExtension>()?.run { lint.configureLint(project) }
val libs = project.extensions.getByName("libs") as LibrariesForLibs
project.dependencies.apply {
diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/DependencyUpdatesPlugin.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/DependencyUpdatesPlugin.kt
index 8dbe586f..e35a1843 100644
--- a/build-logic/src/main/kotlin/app/passwordstore/gradle/DependencyUpdatesPlugin.kt
+++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/DependencyUpdatesPlugin.kt
@@ -19,7 +19,6 @@ class DependencyUpdatesPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.pluginManager.apply(VersionCatalogUpdatePlugin::class)
project.extensions.configure<VersionCatalogUpdateExtension> {
- keep.keepUnusedLibraries.set(true)
versionSelector {
val currentVersion = Version.tryParse(it.currentVersion).getOrNull()
val newVersion = Version.tryParse(it.candidate.version).getOrNull()
diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinAndroidPlugin.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinAndroidPlugin.kt
index 67fef255..d679abd3 100644
--- a/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinAndroidPlugin.kt
+++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinAndroidPlugin.kt
@@ -11,16 +11,13 @@ import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
-import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper
@Suppress("Unused")
class KotlinAndroidPlugin : Plugin<Project> {
override fun apply(project: Project) {
- project.pluginManager.run {
- apply(KotlinAndroidPluginWrapper::class)
- apply(KotlinCommonPlugin::class)
- }
+ // AGP 9 compiles Kotlin itself (built-in Kotlin); only the shared compiler options remain.
+ project.pluginManager.apply(KotlinCommonPlugin::class)
project.extensions.getByType<KotlinProjectExtension>().jvmToolchain(JVM_TOOLCHAIN_ACTION)
}
}
diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinKaptPlugin.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinKaptPlugin.kt
deleted file mode 100644
index c4d6467a..00000000
--- a/build-logic/src/main/kotlin/app/passwordstore/gradle/KotlinKaptPlugin.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.gradle
-
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.kotlin.dsl.apply
-import org.gradle.kotlin.dsl.configure
-import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
-import org.jetbrains.kotlin.gradle.plugin.KaptExtension
-
-@Suppress("Unused")
-class KotlinKaptPlugin : Plugin<Project> {
-
- override fun apply(project: Project) {
- project.pluginManager.apply(Kapt3GradleSubplugin::class)
- project.afterEvaluate {
- project.extensions.configure<KaptExtension> {
- javacOptions {
- if (hasDaggerCompilerDependency()) {
- // https://dagger.dev/dev-guide/compiler-options#fastinit-mode
- option("-Adagger.fastInit=enabled")
- // Enable the better, experimental error messages
- // https://github.com/google/dagger/commit/0d2505a727b54f47b8677f42dd4fc5c1924e37f5
- option("-Adagger.experimentalDaggerErrorMessages=enabled")
- // Share test components for when we start leveraging Hilt for tests
- // https://github.com/google/dagger/releases/tag/dagger-2.34
- option("-Adagger.hilt.shareTestComponents=true")
- // KAPT nests errors causing real issues to be suppressed in CI logs
- option("-Xmaxerrs", "500")
- // Enables per-module validation for faster error detection
- // https://github.com/google/dagger/commit/325b516ac6a53d3fc973d247b5231fafda9870a2
- option("-Adagger.moduleBindingValidation=ERROR")
- }
- }
- }
- }
- project.tasks
- .matching { it.name.startsWith("kapt") && it.name.endsWith("UnitTestKotlin") }
- .configureEach { enabled = false }
- }
-
- private fun Project.hasDaggerCompilerDependency(): Boolean {
- return configurations.any {
- it.dependencies.any { dependency -> dependency.name == "hilt-compiler" }
- }
- }
-}
diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/PublishedAndroidLibraryPlugin.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/PublishedAndroidLibraryPlugin.kt
index 4e1ab741..055a921a 100644
--- a/build-logic/src/main/kotlin/app/passwordstore/gradle/PublishedAndroidLibraryPlugin.kt
+++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/PublishedAndroidLibraryPlugin.kt
@@ -10,8 +10,6 @@ package app.passwordstore.gradle
import com.vanniktech.maven.publish.AndroidMultiVariantLibrary
import com.vanniktech.maven.publish.MavenPublishBaseExtension
import com.vanniktech.maven.publish.MavenPublishBasePlugin
-import com.vanniktech.maven.publish.SonatypeHost
-import me.tylerbwong.gradle.metalava.Documentation
import me.tylerbwong.gradle.metalava.extension.MetalavaExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
@@ -28,7 +26,7 @@ class PublishedAndroidLibraryPlugin : Plugin<Project> {
apply("me.tylerbwong.gradle.metalava")
}
project.extensions.configure<MavenPublishBaseExtension> {
- publishToMavenCentral(SonatypeHost.DEFAULT, true)
+ publishToMavenCentral(automaticRelease = true)
if (project.providers.environmentVariable("CI").isPresent) {
signAllPublications()
}
@@ -36,10 +34,7 @@ class PublishedAndroidLibraryPlugin : Plugin<Project> {
pomFromGradleProperties()
}
project.extensions.configure<MetalavaExtension> {
- documentation.set(Documentation.PUBLIC)
inputKotlinNulls.set(true)
- outputKotlinNulls.set(true)
- reportLintsAsErrors.set(true)
reportWarningsAsErrors.set(true)
}
}
diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/signing/AppSigning.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/signing/AppSigning.kt
index f6a519c8..2a0825ed 100644
--- a/build-logic/src/main/kotlin/app/passwordstore/gradle/signing/AppSigning.kt
+++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/signing/AppSigning.kt
@@ -5,7 +5,7 @@
package app.passwordstore.gradle.signing
-import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
+import com.android.build.api.dsl.ApplicationExtension
import java.util.Properties
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
@@ -17,7 +17,7 @@ private const val KEYSTORE_CONFIG_PATH = "keystore.properties"
internal fun Project.configureBuildSigning() {
val keystoreConfigFile = rootProject.layout.projectDirectory.file(KEYSTORE_CONFIG_PATH)
if (keystoreConfigFile.asFile.exists()) {
- extensions.configure<BaseAppModuleExtension> {
+ extensions.configure<ApplicationExtension> {
val contents = providers.fileContents(keystoreConfigFile).asText
val keystoreProperties = Properties()
keystoreProperties.load(contents.get().byteInputStream())
@@ -30,7 +30,7 @@ internal fun Project.configureBuildSigning() {
}
}
val signingConfig = signingConfigs.getByName("release")
- buildTypes.all { setSigningConfig(signingConfig) }
+ buildTypes.all { this.signingConfig = signingConfig }
}
}
}
diff --git a/gradle.properties b/gradle.properties
index dde45722..b407f189 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -18,16 +18,7 @@ kotlin.incremental=true
# Kotlin code style
kotlin.code.style=official
-# Enable incremental annotation processor for KAPT
-kapt.incremental.apt=true
-
-# Turn off AP discovery in compile path to enable compile avoidance
-kapt.include.compile.classpath=false
-
-# New incremental compilation for Kotlin
-kotlin.compiler.preciseCompilationResultsBackup=true
kotlin.build.report.output=file
-kotlin.compiler.suppressExperimentalICOptimizationsWarning=true
# Make all Kotlin DSL warnings fail build
org.gradle.kotlin.dsl.allWarningsAsErrors=true
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 8cc0acf2..fb831d85 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,11 +1,11 @@
[versions]
-agp = "8.13.2"
+agp = "9.5.0"
androidxActivity = "1.9.2"
bouncycastle = "1.86"
coroutines = "1.9.0"
flowbinding = "1.2.0"
-hilt = "2.58"
-kotlin = "2.1.21"
+hilt = "2.60.1"
+kotlin = "2.4.21"
kotlinResult = "2.0.0"
lifecycle = "2.8.6"
moshi = "1.15.1"
@@ -36,18 +36,18 @@ aps-sublimeFuzzy = "com.github.android-password-store:sublime-fuzzy:2.3.4"
aps-zxingAndroidEmbedded = "com.github.android-password-store:zxing-android-embedded:4.2.1"
build-agp = { module = "com.android.tools.build:gradle", version.ref = "agp" }
build-diffutils = "io.github.java-diff-utils:java-diff-utils:4.12"
-build-download = "de.undercouch:gradle-download-task:5.6.0"
+build-download = "de.undercouch:gradle-download-task:5.7.0"
build-javapoet = "com.squareup:javapoet:1.13.0"
build-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
-build-mavenpublish = "com.vanniktech:gradle-maven-publish-plugin:0.29.0"
-build-metalava = "me.tylerbwong.gradle.metalava:plugin:0.3.5"
+build-mavenpublish = "com.vanniktech:gradle-maven-publish-plugin:0.37.0"
+build-metalava = "me.tylerbwong.gradle.metalava:plugin:0.5.1"
build-moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
build-moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" }
build-okhttp = "com.squareup.okhttp3:okhttp:5.0.0-alpha.14"
build-semver = "com.github.zafarkhaja:java-semver:0.10.2"
-build-sentry = "io.sentry.android.gradle:io.sentry.android.gradle.gradle.plugin:4.11.0"
-build-spotless = "com.diffplug.spotless:spotless-plugin-gradle:7.0.0.BETA2"
-build-vcu = "nl.littlerobots.version-catalog-update:nl.littlerobots.version-catalog-update.gradle.plugin:0.8.4"
+build-sentry = "io.sentry.android.gradle:io.sentry.android.gradle.gradle.plugin:6.25.0"
+build-spotless = "com.diffplug.spotless:spotless-plugin-gradle:8.10.4"
+build-vcu = "nl.littlerobots.version-catalog-update:nl.littlerobots.version-catalog-update.gradle.plugin:1.1.2"
compose-bom = "androidx.compose:compose-bom:2024.09.03"
compose-foundation-core = { module = "androidx.compose.foundation:foundation" }
compose-foundation-layout = { module = "androidx.compose.foundation:foundation-layout" }
@@ -86,7 +86,7 @@ thirdparty-leakcanary-plumber = "com.squareup.leakcanary:plumber-android-startup
thirdparty-logcat = "com.squareup.logcat:logcat:0.1"
thirdparty-modernAndroidPrefs = "de.maxr1998:modernandroidpreferences:2.4.0-beta2"
thirdparty-nonfree-googlePlayAuthApiPhone = "com.google.android.gms:play-services-auth-api-phone:18.1.0"
-thirdparty-nonfree-sentry = "io.sentry:sentry-android:7.15.0"
+thirdparty-nonfree-sentry = "io.sentry:sentry-android:8.62.0"
thirdparty-pgpainless = "org.pgpainless:pgpainless-core:1.7.8"
thirdparty-slack-lints = "com.slack.lint:slack-lint-checks:0.8.0"
thirdparty-slf4j-api = { module = "org.slf4j:slf4j-api", version = { strictly = "[1.7, 1.8[", prefer = "1.7.36" } }
@@ -106,4 +106,5 @@ testDependencies = [
[plugins]
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
+ksp = "com.google.devtools.ksp:2.3.13"
kotlin-composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 02f06ab2..9fa7f288 100644
--- a/gradle/wrapper/gradle-wrapper.jar
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f900c206..5907d39c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,8 +1,10 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionSha256Sum=7988ed071b2a07900e2ec715fca15c6ab72bce6db433af301fa7fa7e9407bd24
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.6-bin.zip
+distributionSha256Sum=21bd5da2de49b642904077f38c0efdc7778e92b3ed0889392dca61b97e389607
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.2-bin.zip
networkTimeout=10000
+retries=0
+retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 23d15a93..249efbb0 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
#!/bin/sh
#
-# Copyright © 2015-2021 the original authors.
+# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
##############################################################################
#
-# Gradle start up script for POSIX generated by Gradle.
+# gradlew start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
-# ksh Gradle
+# ksh gradlew
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
-CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -172,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
- CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
- -classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
diff --git a/gradlew.bat b/gradlew.bat
index 5eed7ee8..03754c4c 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -19,12 +19,39 @@
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
-@rem Gradle startup script for Windows
+@rem gradlew startup script for Windows
@rem
@rem ##########################################################################
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
+@rem Set local scope for the variables, and ensure extensions are enabled
+setlocal EnableExtensions
+
+@rem Catch executions from older scripts and ensure they exit cleanly.
+@rem This can be removed once we can be reasonably confident that few people
+@rem will be migrating directly to this new wrapper.
+goto afterSafetyNet
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+goto exitWithErrorLevel
+:afterSafetyNet
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -45,13 +72,14 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
-echo. 1>&2
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
+1>&2 echo.
+1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+1>&2 echo.
+1>&2 echo Please set the JAVA_HOME variable in your environment to match the
+1>&2 echo location of your Java installation.
-goto fail
+"%COMSPEC%" /c exit 1
+goto exitWithErrorLevel
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -59,36 +87,26 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
-echo. 1>&2
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
+1>&2 echo.
+1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+1>&2 echo.
+1>&2 echo Please set the JAVA_HOME variable in your environment to match the
+1>&2 echo location of your Java installation.
-goto fail
+"%COMSPEC%" /c exit 1
+goto exitWithErrorLevel
:execute
@rem Setup the command line
-set CLASSPATH=
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+@rem Execute gradlew
+@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
+@rem which allows us to clear the local environment before executing the java command
+endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel & goto exitWithErrorLevel
-:end
-@rem End local scope for the variables with windows NT shell
-if %ERRORLEVEL% equ 0 goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-set EXIT_CODE=%ERRORLEVEL%
-if %EXIT_CODE% equ 0 set EXIT_CODE=1
-if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
-exit /b %EXIT_CODE%
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@rem This label must not be changed. We rely on old scripts being able to jump to this point.
+:exitWithErrorLevel
+@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
+"%COMSPEC%" /c exit %ERRORLEVEL%
diff --git a/passgen/diceware/build.gradle.kts b/passgen/diceware/build.gradle.kts
index 413d9a0d..5caaa366 100644
--- a/passgen/diceware/build.gradle.kts
+++ b/passgen/diceware/build.gradle.kts
@@ -10,7 +10,9 @@ plugins {
android {
namespace = "app.passwordstore.passgen.diceware"
androidResources.enable = true
- sourceSets { getByName("test") { resources.srcDir("src/main/res/raw") } }
+ sourceSets { getByName("test") { resources.directories.add("src/main/res/raw") } }
+ // AGP 9 applies packaging excludes to unit test resources too; keep the word list.
+ packaging.resources.excludes.remove("**/*.txt")
}
dependencies {
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 04160f17..a27a0cbd 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -32,7 +32,7 @@ pluginManagement {
}
}
-plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" }
+plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" }
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)