diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 0f674eae..1f68ab92 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -10,11 +10,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout DeepLinkDispatch - uses: actions/checkout@v6 - - name: Set up JDK 22 - uses: actions/setup-java@v5 + uses: actions/checkout@v7 + - name: Set up JDK 25 + uses: actions/setup-java@v6 with: distribution: 'corretto' - java-version: '22' + java-version: '25' + - name: Set up Android SDK + uses: android-actions/setup-android@v4.0.4 + - name: Install Android SDK + run: sdkmanager 'platforms;android-37.2' 'build-tools;37.0.0' - name: Build/Test DeepLinkDispatch - run: ./gradlew assemble check javadoc lintKotlin + run: ./gradlew assemble assembleAndroidTest check javadoc lintKotlin diff --git a/.github/workflows/deploy_snapshot.yml b/.github/workflows/deploy_snapshot.yml index 563eb959..5d89f2f4 100644 --- a/.github/workflows/deploy_snapshot.yml +++ b/.github/workflows/deploy_snapshot.yml @@ -13,21 +13,19 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout DeepLinkDispatch - uses: actions/checkout@v6 - - name: Set up JDK 22 - uses: actions/setup-java@v5 + uses: actions/checkout@v7 + - name: Set up JDK 25 + uses: actions/setup-java@v6 with: distribution: 'corretto' - java-version: '22' - - name: Update gradle.properties - uses: DamianReeves/write-file-action@master - with: - path: gradle.properties - write-mode: append - contents: | - mavenCentralUsername=${{ secrets.NEXUS_USER }} - mavenCentralPassword=${{ secrets.NEXUS_PASSWORD }} - + java-version: '25' + - name: Set up Android SDK + uses: android-actions/setup-android@v4.0.4 + - name: Install Android SDK + run: sdkmanager 'platforms;android-37.2' 'build-tools;37.0.0' - name: Deploy new snapshot of DeepLinkDispatch shell: bash + env: + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.NEXUS_USER }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.NEXUS_PASSWORD }} run: ./gradlew -PenforceSnapshotVersion=true publishAllPublicationsToMavenCentral diff --git a/README.md b/README.md index 164ef47a..ed08393c 100644 --- a/README.md +++ b/README.md @@ -836,3 +836,17 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ``` + +## Building from source + +Use JDK 25 (LTS) and install Android SDK Platform 37.2 and Build Tools 37.0.0. +The Gradle wrapper supplies Gradle; build and test dependency versions are centralized in +[`dependencies.gradle`](dependencies.gradle). + +```shell +./gradlew assemble assembleAndroidTest check javadoc lintKotlin +``` + +Android modules use AGP's built-in Kotlin support. The kapt sample uses +`com.android.legacy-kapt`; KSP samples continue to use `com.google.devtools.ksp`. +The compile SDK is independent of the existing minimum and target SDK levels. diff --git a/build.gradle b/build.gradle index 702cfdf3..4cee0d82 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,8 @@ buildscript { dependencies { classpath deps.androidPlugin classpath deps.kotlinGradlePlugin + classpath "org.jetbrains.kotlin:kotlin-sam-with-receiver:$versions.kotlinVersion" + classpath "com.android.legacy-kapt:com.android.legacy-kapt.gradle.plugin:$androidConfig.agpVersion" classpath deps.gradleMavenPublishPlugin classpath deps.benchmarkGradlePlugin // Dokka is needed on classpath for vanniktech publish plugin @@ -34,20 +36,24 @@ allprojects { } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { + compilerOptions { allWarningsAsErrors = true } } // Apply consistent JVM toolchain and target to all subprojects subprojects { + plugins.withId('checkstyle') { + checkstyle.toolVersion = rootProject.ext.versions.checkstyleVersion + } afterEvaluate { def toolchainVersion = rootProject.ext.jvmToolchainVersion - def targetVersion = rootProject.ext.jvmTargetVersion + // Gradle/AGP plugins run on Java 17+; library bytecode remains Java 11. + def targetVersion = plugins.hasPlugin('java-gradle-plugin') ? 17 : rootProject.ext.jvmTargetVersion // For Kotlin projects (both pure JVM and Android) if (plugins.hasPlugin('org.jetbrains.kotlin.jvm') || plugins.hasPlugin('kotlin') || - plugins.hasPlugin('kotlin-android') || plugins.hasPlugin('org.jetbrains.kotlin.android')) { + plugins.hasPlugin('com.android.library') || plugins.hasPlugin('com.android.application')) { kotlin { jvmToolchain(toolchainVersion) } @@ -58,7 +64,7 @@ subprojects { } } // For Java-only projects (without Kotlin) - else if (plugins.hasPlugin('java') && !plugins.hasPlugin('kotlin-dsl')) { + else if (plugins.hasPlugin('java')) { java { toolchain { languageVersion = JavaLanguageVersion.of(toolchainVersion) @@ -77,7 +83,7 @@ subprojects { } // For pure Java projects - if (plugins.hasPlugin('java') && !plugins.hasPlugin('kotlin-dsl')) { + if (plugins.hasPlugin('java')) { java { sourceCompatibility = JavaVersion.toVersion(targetVersion) targetCompatibility = JavaVersion.toVersion(targetVersion) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 08c663a7..916584a3 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,7 +1,8 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { - `kotlin-dsl` + id("org.jetbrains.kotlin.jvm") + id("org.jetbrains.kotlin.plugin.sam.with.receiver") `java-gradle-plugin` } @@ -11,7 +12,7 @@ apply(from = "../dependencies.gradle") val deps: Map by project val jvmToolchainVersion: Int by extra -val jvmTargetVersion: Int by extra +val jvmTargetVersion = 17 // Gradle and AGP require Java 17 or newer. repositories { google() @@ -19,6 +20,10 @@ repositories { gradlePluginPortal() } +samWithReceiver { + annotation("org.gradle.api.HasImplicitReceiver") +} + java { toolchain.languageVersion.set(JavaLanguageVersion.of(jvmToolchainVersion)) sourceCompatibility = JavaVersion.toVersion(jvmTargetVersion) diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 00000000..4a55619c --- /dev/null +++ b/buildSrc/settings.gradle.kts @@ -0,0 +1,13 @@ +pluginManagement { + apply(from = "../dependencies.gradle") + @Suppress("UNCHECKED_CAST") + val versions = extra["versions"] as Map + repositories { + gradlePluginPortal() + mavenCentral() + } + plugins { + id("org.jetbrains.kotlin.jvm") version versions.getValue("kotlinVersion") + id("org.jetbrains.kotlin.plugin.sam.with.receiver") version versions.getValue("kotlinVersion") + } +} diff --git a/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/DeepLinkEntry.kt b/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/DeepLinkEntry.kt index eb45ffed..2eaaa81f 100644 --- a/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/DeepLinkEntry.kt +++ b/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/DeepLinkEntry.kt @@ -272,21 +272,35 @@ sealed class DeepLinkEntry( */ override fun compareTo(other: DeepLinkEntry): Int { return when { - /** + /* * Specific conditions added for fully concrete links. * Concrete link will always return -1 for firstNonConcreteIndex, * so the general comparison logic will not work. */ - this.firstNonConcreteIndex < 0 && this.firstNonConcreteIndex != other.firstNonConcreteIndex -> -1 - other.firstNonConcreteIndex < 0 && other.firstNonConcreteIndex != this.firstNonConcreteIndex -> 1 - this.firstNonConcreteIndex < other.firstNonConcreteIndex -> 1 + this.firstNonConcreteIndex < 0 && this.firstNonConcreteIndex != other.firstNonConcreteIndex -> { + -1 + } + + other.firstNonConcreteIndex < 0 && other.firstNonConcreteIndex != this.firstNonConcreteIndex -> { + 1 + } + + this.firstNonConcreteIndex < other.firstNonConcreteIndex -> { + 1 + } + this.firstNonConcreteIndex == other.firstNonConcreteIndex -> { when { // Both are fully concrete - this.firstNonConcreteIndex == -1 -> 0 + this.firstNonConcreteIndex == -1 -> { + 0 + } + // Compare by total number of non-concrete elements (fewer is more concrete) - this.totalNonConcreteElements != other.totalNonConcreteElements -> + this.totalNonConcreteElements != other.totalNonConcreteElements -> { this.totalNonConcreteElements.compareTo(other.totalNonConcreteElements) + } + // Same number of non-concrete elements, compare types and positions pairwise else -> { val thisElements = this.nonConcreteElementIndicesAndTypes @@ -327,7 +341,10 @@ sealed class DeepLinkEntry( } } } - else -> -1 + + else -> { + -1 + } } } } diff --git a/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/NodeMetadata.kt b/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/NodeMetadata.kt index 5a35f292..0fad5a13 100644 --- a/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/NodeMetadata.kt +++ b/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/NodeMetadata.kt @@ -46,9 +46,11 @@ class NodeMetadata( validateIfConfigurablePathSegment(this) -> { MetadataMasks.CONFIGURABLE_PATH_SEGMENT_MASK } + validateIfComponentParam(this) -> { MetadataMasks.COMPONENT_PARAM_MASK } + else -> { 0 } diff --git a/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/UrlTree.kt b/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/UrlTree.kt index 3e9bbded..bb352224 100644 --- a/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/UrlTree.kt +++ b/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/UrlTree.kt @@ -228,27 +228,32 @@ data class Root( private fun uriMatch(deepLinkEntry: DeepLinkEntry) = when (deepLinkEntry) { - is DeepLinkEntry.ActivityDeeplinkEntry -> + is DeepLinkEntry.ActivityDeeplinkEntry -> { UriMatch( MatchType.Activity, deepLinkEntry.uriTemplate, deepLinkEntry.className, null, ) - is DeepLinkEntry.MethodDeeplinkEntry -> + } + + is DeepLinkEntry.MethodDeeplinkEntry -> { UriMatch( MatchType.Method, deepLinkEntry.uriTemplate, deepLinkEntry.className, deepLinkEntry.method, ) - is DeepLinkEntry.HandlerDeepLinkEntry -> + } + + is DeepLinkEntry.HandlerDeepLinkEntry -> { UriMatch( MatchType.Handler, deepLinkEntry.uriTemplate, deepLinkEntry.className, null, ) + } } } diff --git a/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/base/Utils.kt b/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/base/Utils.kt index 99cd6a90..87cd57b3 100644 --- a/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/base/Utils.kt +++ b/deeplinkdispatch-base/src/main/java/com/airbnb/deeplinkdispatch/base/Utils.kt @@ -19,9 +19,13 @@ import java.io.InputStream private fun Char.modifiedUtf8ByteSize(): Int { val codePoint = this.code return when { - codePoint == 0 -> 2 // Null character is encoded as 2 bytes in Modified UTF-8 + codePoint == 0 -> 2 + + // Null character is encoded as 2 bytes in Modified UTF-8 codePoint <= 0x7F -> 1 + codePoint <= 0x7FF -> 2 + else -> 3 } } diff --git a/deeplinkdispatch-gradle-plugin/build.gradle.kts b/deeplinkdispatch-gradle-plugin/build.gradle.kts index dff2f3f6..c4ae18ad 100644 --- a/deeplinkdispatch-gradle-plugin/build.gradle.kts +++ b/deeplinkdispatch-gradle-plugin/build.gradle.kts @@ -5,20 +5,26 @@ apply(from = "$rootDir/publishing.gradle") val deps: Map by project val jvmToolchainVersion: Int by rootProject.extra -val jvmTargetVersion: Int by rootProject.extra +val jvmTargetVersion = 17 // Matches the minimum JVM required by Gradle and AGP. +val androidConfig: Map by project repositories { google() mavenCentral() gradlePluginPortal() - maven(url = "https://oss.sonatype.org/service/local/repositories/snapshots/content/") + maven(url = "https://central.sonatype.com/repository/maven-snapshots/") } plugins { - `kotlin-dsl` + id("org.jetbrains.kotlin.jvm") + id("org.jetbrains.kotlin.plugin.sam.with.receiver") `java-gradle-plugin` } +samWithReceiver { + annotation("org.gradle.api.HasImplicitReceiver") +} + // JVM toolchain and target - uses central versions from dependencies.gradle java { toolchain { @@ -55,6 +61,9 @@ dependencies { tasks.test { useJUnit() + systemProperty("test.compileSdk", androidConfig.getValue("compileSdkVersion").toString()) + systemProperty("test.compileSdkMinor", androidConfig.getValue("compileSdkMinorVersion").toString()) + systemProperty("test.buildTools", androidConfig.getValue("buildToolsVersion").toString()) testLogging { events("passed", "skipped", "failed") showStandardStreams = true diff --git a/deeplinkdispatch-gradle-plugin/src/main/java/com/airbnb/deeplinkdispatch/gradleplugin/RelocateDeepLinkAssetsTask.kt b/deeplinkdispatch-gradle-plugin/src/main/java/com/airbnb/deeplinkdispatch/gradleplugin/RelocateDeepLinkAssetsTask.kt index 7b563b1d..ac695710 100644 --- a/deeplinkdispatch-gradle-plugin/src/main/java/com/airbnb/deeplinkdispatch/gradleplugin/RelocateDeepLinkAssetsTask.kt +++ b/deeplinkdispatch-gradle-plugin/src/main/java/com/airbnb/deeplinkdispatch/gradleplugin/RelocateDeepLinkAssetsTask.kt @@ -8,6 +8,7 @@ import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction +import org.gradle.work.DisableCachingByDefault /** * Task that relocates the KSP-generated assets from the resources directory to a safe location. @@ -22,6 +23,7 @@ import org.gradle.api.tasks.TaskAction * This is a separate task (rather than a doLast on KSP) because doLast doesn't run when * KSP is restored FROM-CACHE. This task will always run after KSP, even if KSP was cached. */ +@DisableCachingByDefault(because = "Relocation deletes KSP inputs, which must also happen after a cache restore") abstract class RelocateDeepLinkAssetsTask : DefaultTask() { /** diff --git a/deeplinkdispatch-gradle-plugin/src/main/java/com/airbnb/deeplinkdispatch/gradleplugin/RelocateDeepLinkManifestTask.kt b/deeplinkdispatch-gradle-plugin/src/main/java/com/airbnb/deeplinkdispatch/gradleplugin/RelocateDeepLinkManifestTask.kt index e675a588..d6e4ab61 100644 --- a/deeplinkdispatch-gradle-plugin/src/main/java/com/airbnb/deeplinkdispatch/gradleplugin/RelocateDeepLinkManifestTask.kt +++ b/deeplinkdispatch-gradle-plugin/src/main/java/com/airbnb/deeplinkdispatch/gradleplugin/RelocateDeepLinkManifestTask.kt @@ -8,6 +8,7 @@ import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction +import org.gradle.work.DisableCachingByDefault /** * Task that relocates the KSP-generated manifest from the resources directory to a safe location. @@ -24,6 +25,7 @@ import org.gradle.api.tasks.TaskAction * source file, and when KSP is restored from cache, it recreates the file but Gradle's * up-to-date check only compares content, not file existence. */ +@DisableCachingByDefault(because = "Relocation deletes KSP inputs, which must also happen after a cache restore") abstract class RelocateDeepLinkManifestTask : DefaultTask() { @get:InputFiles diff --git a/deeplinkdispatch-gradle-plugin/src/test/kotlin/com/airbnb/deeplinkdispatch/gradleplugin/AarManifestExclusionTest.kt b/deeplinkdispatch-gradle-plugin/src/test/kotlin/com/airbnb/deeplinkdispatch/gradleplugin/AarManifestExclusionTest.kt index 3115596b..3cb47786 100644 --- a/deeplinkdispatch-gradle-plugin/src/test/kotlin/com/airbnb/deeplinkdispatch/gradleplugin/AarManifestExclusionTest.kt +++ b/deeplinkdispatch-gradle-plugin/src/test/kotlin/com/airbnb/deeplinkdispatch/gradleplugin/AarManifestExclusionTest.kt @@ -78,14 +78,18 @@ class AarManifestExclusionTest { fun `AAR classes jar does not contain deeplinkdispatch manifest`() { buildFile.writeText(""" plugins { - id 'com.android.library' version '8.2.0' - id 'org.jetbrains.kotlin.android' version '1.9.22' + id 'com.android.library' id 'com.airbnb.deeplinkdispatch.manifest-generation' } android { namespace 'com.test.library' - compileSdk 34 + compileSdk { + version = release(${System.getProperty("test.compileSdk")}) { + it.minorApiLevel = ${System.getProperty("test.compileSdkMinor")} + } + } + buildToolsVersion '${System.getProperty("test.buildTools")}' defaultConfig { minSdk 21 } @@ -93,9 +97,6 @@ class AarManifestExclusionTest { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } - kotlinOptions { - jvmTarget = '11' - } } """.trimIndent()) @@ -163,14 +164,18 @@ class AarManifestExclusionTest { fun `AAR root manifest contains merged intent filters`() { buildFile.writeText(""" plugins { - id 'com.android.library' version '8.2.0' - id 'org.jetbrains.kotlin.android' version '1.9.22' + id 'com.android.library' id 'com.airbnb.deeplinkdispatch.manifest-generation' } android { namespace 'com.test.library' - compileSdk 34 + compileSdk { + version = release(${System.getProperty("test.compileSdk")}) { + it.minorApiLevel = ${System.getProperty("test.compileSdkMinor")} + } + } + buildToolsVersion '${System.getProperty("test.buildTools")}' defaultConfig { minSdk 21 } @@ -178,9 +183,6 @@ class AarManifestExclusionTest { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } - kotlinOptions { - jvmTarget = '11' - } } """.trimIndent()) diff --git a/deeplinkdispatch-gradle-plugin/src/test/kotlin/com/airbnb/deeplinkdispatch/gradleplugin/ManifestGenerationPluginTest.kt b/deeplinkdispatch-gradle-plugin/src/test/kotlin/com/airbnb/deeplinkdispatch/gradleplugin/ManifestGenerationPluginTest.kt index 5c68e806..3d7f94b5 100644 --- a/deeplinkdispatch-gradle-plugin/src/test/kotlin/com/airbnb/deeplinkdispatch/gradleplugin/ManifestGenerationPluginTest.kt +++ b/deeplinkdispatch-gradle-plugin/src/test/kotlin/com/airbnb/deeplinkdispatch/gradleplugin/ManifestGenerationPluginTest.kt @@ -71,14 +71,18 @@ class ManifestGenerationPluginTest { fun `plugin applies successfully to library module`() { buildFile.writeText(""" plugins { - id 'com.android.library' version '8.2.0' - id 'org.jetbrains.kotlin.android' version '1.9.22' + id 'com.android.library' id 'com.airbnb.deeplinkdispatch.manifest-generation' } android { namespace 'com.test.library' - compileSdk 34 + compileSdk { + version = release(${System.getProperty("test.compileSdk")}) { + it.minorApiLevel = ${System.getProperty("test.compileSdkMinor")} + } + } + buildToolsVersion '${System.getProperty("test.buildTools")}' defaultConfig { minSdk 21 @@ -101,14 +105,18 @@ class ManifestGenerationPluginTest { fun `plugin fails on application module with clear error message`() { buildFile.writeText(""" plugins { - id 'com.android.application' version '8.2.0' - id 'org.jetbrains.kotlin.android' version '1.9.22' + id 'com.android.application' id 'com.airbnb.deeplinkdispatch.manifest-generation' } android { namespace 'com.test.app' - compileSdk 34 + compileSdk { + version = release(${System.getProperty("test.compileSdk")}) { + it.minorApiLevel = ${System.getProperty("test.compileSdkMinor")} + } + } + buildToolsVersion '${System.getProperty("test.buildTools")}' defaultConfig { applicationId "com.test.app" @@ -134,14 +142,18 @@ class ManifestGenerationPluginTest { fun `relocate task is registered for each variant`() { buildFile.writeText(""" plugins { - id 'com.android.library' version '8.2.0' - id 'org.jetbrains.kotlin.android' version '1.9.22' + id 'com.android.library' id 'com.airbnb.deeplinkdispatch.manifest-generation' } android { namespace 'com.test.library' - compileSdk 34 + compileSdk { + version = release(${System.getProperty("test.compileSdk")}) { + it.minorApiLevel = ${System.getProperty("test.compileSdkMinor")} + } + } + buildToolsVersion '${System.getProperty("test.buildTools")}' defaultConfig { minSdk 21 @@ -170,14 +182,18 @@ class ManifestGenerationPluginTest { fun `manifest merge task is registered for each variant`() { buildFile.writeText(""" plugins { - id 'com.android.library' version '8.2.0' - id 'org.jetbrains.kotlin.android' version '1.9.22' + id 'com.android.library' id 'com.airbnb.deeplinkdispatch.manifest-generation' } android { namespace 'com.test.library' - compileSdk 34 + compileSdk { + version = release(${System.getProperty("test.compileSdk")}) { + it.minorApiLevel = ${System.getProperty("test.compileSdkMinor")} + } + } + buildToolsVersion '${System.getProperty("test.buildTools")}' defaultConfig { minSdk 21 @@ -199,14 +215,18 @@ class ManifestGenerationPluginTest { fun `assembleDebug succeeds without KSP generated manifest`() { buildFile.writeText(""" plugins { - id 'com.android.library' version '8.2.0' - id 'org.jetbrains.kotlin.android' version '1.9.22' + id 'com.android.library' id 'com.airbnb.deeplinkdispatch.manifest-generation' } android { namespace 'com.test.library' - compileSdk 34 + compileSdk { + version = release(${System.getProperty("test.compileSdk")}) { + it.minorApiLevel = ${System.getProperty("test.compileSdkMinor")} + } + } + buildToolsVersion '${System.getProperty("test.buildTools")}' defaultConfig { minSdk 21 @@ -229,14 +249,18 @@ class ManifestGenerationPluginTest { fun `plugin works with product flavors`() { buildFile.writeText(""" plugins { - id 'com.android.library' version '8.2.0' - id 'org.jetbrains.kotlin.android' version '1.9.22' + id 'com.android.library' id 'com.airbnb.deeplinkdispatch.manifest-generation' } android { namespace 'com.test.library' - compileSdk 34 + compileSdk { + version = release(${System.getProperty("test.compileSdk")}) { + it.minorApiLevel = ${System.getProperty("test.compileSdkMinor")} + } + } + buildToolsVersion '${System.getProperty("test.buildTools")}' defaultConfig { minSdk 21 @@ -271,14 +295,18 @@ class ManifestGenerationPluginTest { fun `manifest merge task is up-to-date on second run`() { buildFile.writeText(""" plugins { - id 'com.android.library' version '8.2.0' - id 'org.jetbrains.kotlin.android' version '1.9.22' + id 'com.android.library' id 'com.airbnb.deeplinkdispatch.manifest-generation' } android { namespace 'com.test.library' - compileSdk 34 + compileSdk { + version = release(${System.getProperty("test.compileSdk")}) { + it.minorApiLevel = ${System.getProperty("test.compileSdkMinor")} + } + } + buildToolsVersion '${System.getProperty("test.buildTools")}' defaultConfig { minSdk 21 @@ -315,14 +343,18 @@ class ManifestGenerationPluginTest { buildFile.writeText(""" plugins { - id 'com.android.library' version '8.2.0' - id 'org.jetbrains.kotlin.android' version '1.9.22' + id 'com.android.library' id 'com.airbnb.deeplinkdispatch.manifest-generation' } android { namespace 'com.test.library' - compileSdk 34 + compileSdk { + version = release(${System.getProperty("test.compileSdk")}) { + it.minorApiLevel = ${System.getProperty("test.compileSdkMinor")} + } + } + buildToolsVersion '${System.getProperty("test.buildTools")}' defaultConfig { minSdk 21 diff --git a/deeplinkdispatch-gradle-plugin/src/test/kotlin/com/airbnb/deeplinkdispatch/gradleplugin/RelocateDeepLinkManifestTaskTest.kt b/deeplinkdispatch-gradle-plugin/src/test/kotlin/com/airbnb/deeplinkdispatch/gradleplugin/RelocateDeepLinkManifestTaskTest.kt index 774cd604..8410047d 100644 --- a/deeplinkdispatch-gradle-plugin/src/test/kotlin/com/airbnb/deeplinkdispatch/gradleplugin/RelocateDeepLinkManifestTaskTest.kt +++ b/deeplinkdispatch-gradle-plugin/src/test/kotlin/com/airbnb/deeplinkdispatch/gradleplugin/RelocateDeepLinkManifestTaskTest.kt @@ -65,14 +65,18 @@ class RelocateDeepLinkManifestTaskTest { fun `relocate task moves manifest from KSP resources to safe location`() { buildFile.writeText(""" plugins { - id 'com.android.library' version '8.2.0' - id 'org.jetbrains.kotlin.android' version '1.9.22' + id 'com.android.library' id 'com.airbnb.deeplinkdispatch.manifest-generation' } android { namespace 'com.test.library' - compileSdk 34 + compileSdk { + version = release(${System.getProperty("test.compileSdk")}) { + it.minorApiLevel = ${System.getProperty("test.compileSdkMinor")} + } + } + buildToolsVersion '${System.getProperty("test.buildTools")}' defaultConfig { minSdk 21 } @@ -118,14 +122,18 @@ class RelocateDeepLinkManifestTaskTest { fun `relocate task handles missing source file gracefully`() { buildFile.writeText(""" plugins { - id 'com.android.library' version '8.2.0' - id 'org.jetbrains.kotlin.android' version '1.9.22' + id 'com.android.library' id 'com.airbnb.deeplinkdispatch.manifest-generation' } android { namespace 'com.test.library' - compileSdk 34 + compileSdk { + version = release(${System.getProperty("test.compileSdk")}) { + it.minorApiLevel = ${System.getProperty("test.compileSdkMinor")} + } + } + buildToolsVersion '${System.getProperty("test.buildTools")}' defaultConfig { minSdk 21 } diff --git a/deeplinkdispatch-processor/build.gradle b/deeplinkdispatch-processor/build.gradle index 8d7acafc..c1308a55 100644 --- a/deeplinkdispatch-processor/build.gradle +++ b/deeplinkdispatch-processor/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'kotlin' apply plugin: 'org.jmailen.kotlinter' apply plugin: 'checkstyle' apply from: '../publishing.gradle' +apply from: '../gradle/android-test-stubs.gradle' dependencies { implementation project(':deeplinkdispatch-base') @@ -11,13 +12,15 @@ dependencies { implementation deps.androidXAnnotations implementation deps.ksp implementation deps.xProcessor + // XProcessing must read metadata emitted by the current Kotlin compiler. + implementation deps.kotlinMetadata testImplementation deps.junit testImplementation deps.assertJ testImplementation deps.android - // For test compile we need a reference of the DeepLinkDelegate (which has android dependencies) - // Cannot depend on them from Maven as they are .aar and not .jar files (this is a java project) - testImplementation fileTree(dir: 'libs', include: ['androidx.localbroadcastmanager-1.0.0-beta1.jar','androidx.core-1.0.0-beta1.jar']) + // Only the API classes are needed by the synthetic compilation fixtures. + testAndroidStubs deps.androidxCoreTest + testAndroidStubs deps.localBroadcastManager testImplementation deps.compileTesting testImplementation deps.compileTestingKsp testImplementation deps.mockk diff --git a/deeplinkdispatch-processor/libs/androidx.core-1.0.0-beta1.jar b/deeplinkdispatch-processor/libs/androidx.core-1.0.0-beta1.jar deleted file mode 100644 index e1a3c99b..00000000 Binary files a/deeplinkdispatch-processor/libs/androidx.core-1.0.0-beta1.jar and /dev/null differ diff --git a/deeplinkdispatch-processor/libs/androidx.localbroadcastmanager-1.0.0-beta1.jar b/deeplinkdispatch-processor/libs/androidx.localbroadcastmanager-1.0.0-beta1.jar deleted file mode 100644 index 906deb48..00000000 Binary files a/deeplinkdispatch-processor/libs/androidx.localbroadcastmanager-1.0.0-beta1.jar and /dev/null differ diff --git a/deeplinkdispatch-processor/src/main/java/com/airbnb/deeplinkdispatch/DeepLinkProcessor.kt b/deeplinkdispatch-processor/src/main/java/com/airbnb/deeplinkdispatch/DeepLinkProcessor.kt index 2431333a..7a219862 100644 --- a/deeplinkdispatch-processor/src/main/java/com/airbnb/deeplinkdispatch/DeepLinkProcessor.kt +++ b/deeplinkdispatch-processor/src/main/java/com/airbnb/deeplinkdispatch/DeepLinkProcessor.kt @@ -249,7 +249,7 @@ class DeepLinkProcessor( ).mapNotNull { uriAndActivityFqn -> try { when { - element is XMethodElement -> + element is XMethodElement -> { DeepLinkAnnotatedElement.MethodAnnotatedElement( uri = uriAndActivityFqn.uri, activityClassFqn = uriAndActivityFqn.activityClassFqn, @@ -258,8 +258,9 @@ class DeepLinkProcessor( categories = uriAndActivityFqn.categories, element = element, ) + } - element is XTypeElement && element.isActivity() -> + element is XTypeElement && element.isActivity() -> { DeepLinkAnnotatedElement.ActivityAnnotatedElement( uri = uriAndActivityFqn.uri, activityClassFqn = uriAndActivityFqn.activityClassFqn, @@ -268,6 +269,7 @@ class DeepLinkProcessor( categories = uriAndActivityFqn.categories, element = element, ) + } element is XTypeElement && element.isHandler() -> { verifyHandlerMatchArgs(element, uriAndActivityFqn.uri) @@ -281,11 +283,12 @@ class DeepLinkProcessor( ) } - else -> + else -> { error( "Internal error: Elements can only be 'MethodAnnotatedElement', " + "'ActivityAnnotatedElement' or 'HandlerAnnotatedElement'", ) + } } } catch (_: MalformedURLException) { environment.messager.printMessage( @@ -889,14 +892,16 @@ class DeepLinkProcessor( val uriTemplate = element.uriTemplate try { when (element) { - is DeepLinkAnnotatedElement.ActivityAnnotatedElement -> + is DeepLinkAnnotatedElement.ActivityAnnotatedElement -> { urisTrie.addToTrie( DeepLinkEntry.ActivityDeeplinkEntry( uriTemplate = uriTemplate, className = element.className, ), ) - is DeepLinkAnnotatedElement.MethodAnnotatedElement -> + } + + is DeepLinkAnnotatedElement.MethodAnnotatedElement -> { urisTrie.addToTrie( DeepLinkEntry.MethodDeeplinkEntry( uriTemplate = uriTemplate, @@ -904,13 +909,16 @@ class DeepLinkProcessor( method = element.method, ), ) - is DeepLinkAnnotatedElement.HandlerAnnotatedElement -> + } + + is DeepLinkAnnotatedElement.HandlerAnnotatedElement -> { urisTrie.addToTrie( DeepLinkEntry.HandlerDeepLinkEntry( uriTemplate = uriTemplate, className = element.className, ), ) + } } } catch (e: IllegalArgumentException) { logError( @@ -1224,27 +1232,37 @@ class DeepLinkProcessor( if (comparisonResult == 0) { val element1Representation = when (element1) { - is DeepLinkAnnotatedElement.ActivityAnnotatedElement -> + is DeepLinkAnnotatedElement.ActivityAnnotatedElement -> { element1.uriTemplate + DeepLinkAnnotatedElement.ActivityAnnotatedElement::class.simpleName - is DeepLinkAnnotatedElement.HandlerAnnotatedElement -> + } + + is DeepLinkAnnotatedElement.HandlerAnnotatedElement -> { "handler_" + element1.uriTemplate + DeepLinkAnnotatedElement.ActivityAnnotatedElement::class.simpleName - is DeepLinkAnnotatedElement.MethodAnnotatedElement -> + } + + is DeepLinkAnnotatedElement.MethodAnnotatedElement -> { element1.uriTemplate + element1.method + DeepLinkAnnotatedElement.MethodAnnotatedElement::class.simpleName + } } val element2Representation = when (element2) { - is DeepLinkAnnotatedElement.ActivityAnnotatedElement -> + is DeepLinkAnnotatedElement.ActivityAnnotatedElement -> { element2.uriTemplate + DeepLinkAnnotatedElement.ActivityAnnotatedElement::class.simpleName - is DeepLinkAnnotatedElement.HandlerAnnotatedElement -> + } + + is DeepLinkAnnotatedElement.HandlerAnnotatedElement -> { "handler_" + element2.uriTemplate + DeepLinkAnnotatedElement.ActivityAnnotatedElement::class.simpleName - is DeepLinkAnnotatedElement.MethodAnnotatedElement -> + } + + is DeepLinkAnnotatedElement.MethodAnnotatedElement -> { element2.uriTemplate + element2.method + DeepLinkAnnotatedElement.MethodAnnotatedElement::class.simpleName + } } comparisonResult = element1Representation.compareTo(element2Representation) } diff --git a/deeplinkdispatch-processor/src/main/java/com/airbnb/deeplinkdispatch/metadata/writers/GenericDocumentationWriter.kt b/deeplinkdispatch-processor/src/main/java/com/airbnb/deeplinkdispatch/metadata/writers/GenericDocumentationWriter.kt index 3a64a300..54eadc03 100644 --- a/deeplinkdispatch-processor/src/main/java/com/airbnb/deeplinkdispatch/metadata/writers/GenericDocumentationWriter.kt +++ b/deeplinkdispatch-processor/src/main/java/com/airbnb/deeplinkdispatch/metadata/writers/GenericDocumentationWriter.kt @@ -34,6 +34,7 @@ internal class GenericDocumentationWriter : Writer { print(CLASS_METHOD_NAME_DELIMITER) print(element.method) } + else -> { // Nothing } } diff --git a/deeplinkdispatch-processor/src/test/java/com/airbnb/deeplinkdispatch/DeepLinkProcessorKspTest.kt b/deeplinkdispatch-processor/src/test/java/com/airbnb/deeplinkdispatch/DeepLinkProcessorKspTest.kt index 686a30bb..3db69741 100644 --- a/deeplinkdispatch-processor/src/test/java/com/airbnb/deeplinkdispatch/DeepLinkProcessorKspTest.kt +++ b/deeplinkdispatch-processor/src/test/java/com/airbnb/deeplinkdispatch/DeepLinkProcessorKspTest.kt @@ -1816,19 +1816,44 @@ class DeepLinkProcessorKspTest : BaseDeepLinkProcessorTest() { val c = literal[i] if (c == '\\' && i + 1 < literal.length) { when (val n = literal[i + 1]) { - 'n' -> out.append('\n').also { i += 2 } - 'r' -> out.append('\r').also { i += 2 } - 't' -> out.append('\t').also { i += 2 } - 'b' -> out.append('\b').also { i += 2 } - '\\' -> out.append('\\').also { i += 2 } - '"' -> out.append('"').also { i += 2 } - '\'' -> out.append('\'').also { i += 2 } - '0' -> out.append('\u0000').also { i += 2 } + 'n' -> { + out.append('\n').also { i += 2 } + } + + 'r' -> { + out.append('\r').also { i += 2 } + } + + 't' -> { + out.append('\t').also { i += 2 } + } + + 'b' -> { + out.append('\b').also { i += 2 } + } + + '\\' -> { + out.append('\\').also { i += 2 } + } + + '"' -> { + out.append('"').also { i += 2 } + } + + '\'' -> { + out.append('\'').also { i += 2 } + } + + '0' -> { + out.append('\u0000').also { i += 2 } + } + 'u' -> { val hex = literal.substring(i + 2, i + 6) out.append(hex.toInt(16).toChar()) i += 6 } + else -> { out.append(n) i += 2 diff --git a/deeplinkdispatch/build.gradle b/deeplinkdispatch/build.gradle index 0f01fc34..540375bd 100644 --- a/deeplinkdispatch/build.gradle +++ b/deeplinkdispatch/build.gradle @@ -1,7 +1,6 @@ apply plugin: 'com.android.library' apply plugin: 'checkstyle' -apply plugin: 'kotlin-android' apply from: '../publishing.gradle' apply plugin: 'org.jmailen.kotlinter' @@ -24,11 +23,16 @@ checkstyle { android { namespace = 'com.airbnb.android.deeplinkdispatch' - compileSdk androidConfig.compileSdkVersion + compileSdk { + version = release(androidConfig.compileSdkVersion) { + it.minorApiLevel = androidConfig.compileSdkMinorVersion + } + } + buildToolsVersion = androidConfig.buildToolsVersion defaultConfig { minSdk = androidConfig.minSdkVersion - targetSdk = androidConfig.compileSdkVersion + targetSdk = androidConfig.targetSdkVersion testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles 'proguard-rules.pro' } diff --git a/deeplinkdispatch/src/main/java/com/airbnb/deeplinkdispatch/BaseDeepLinkDelegate.kt b/deeplinkdispatch/src/main/java/com/airbnb/deeplinkdispatch/BaseDeepLinkDelegate.kt index 0e663517..18dc1e9b 100644 --- a/deeplinkdispatch/src/main/java/com/airbnb/deeplinkdispatch/BaseDeepLinkDelegate.kt +++ b/deeplinkdispatch/src/main/java/com/airbnb/deeplinkdispatch/BaseDeepLinkDelegate.kt @@ -116,13 +116,19 @@ open class BaseDeepLinkDelegate ) { if (result.isSuccessful) { when (result.deepLinkMatchResult?.deeplinkEntry) { - is DeepLinkEntry.MethodDeeplinkEntry -> + is DeepLinkEntry.MethodDeeplinkEntry -> { result.methodResult.taskStackBuilder?.startActivities() ?: result.methodResult.intent?.let { activity.startActivity(it) } - is DeepLinkEntry.ActivityDeeplinkEntry -> + } + + is DeepLinkEntry.ActivityDeeplinkEntry -> { result.methodResult.intent?.let { activity.startActivity(it) } - is DeepLinkEntry.HandlerDeepLinkEntry -> + } + + is DeepLinkEntry.HandlerDeepLinkEntry -> { callDeeplinkHandler(activity, result) + } + null -> { // No - op } @@ -207,7 +213,7 @@ open class BaseDeepLinkDelegate typeNameMap .map { (annotation, type) -> when (annotation.type) { - DeepLinkParamType.Path -> + DeepLinkParamType.Path -> { mapNotNullableType( value = parameters.getOrElse( @@ -216,12 +222,15 @@ open class BaseDeepLinkDelegate type = type, uriTemplate = uriTemplate, ) - DeepLinkParamType.Query -> + } + + DeepLinkParamType.Query -> { mapNullableType( value = parameters[annotation.name], type = type, uriTemplate = uriTemplate, ) + } } }.toTypedArray() @@ -233,20 +242,45 @@ open class BaseDeepLinkDelegate if (value == null) return null return try { typeConverters()[type]?.convert(value = value) ?: when (type) { - Boolean::class.javaObjectType -> value.toBoolean() - Int::class.javaObjectType -> value.toInt() - Long::class.javaObjectType -> value.toLong() - Short::class.javaObjectType -> value.toShort() - Byte::class.javaObjectType -> value.toByte() - Double::class.javaObjectType -> value.toDouble() - Float::class.javaObjectType -> value.toFloat() - String::class.javaObjectType -> value - else -> + Boolean::class.javaObjectType -> { + value.toBoolean() + } + + Int::class.javaObjectType -> { + value.toInt() + } + + Long::class.javaObjectType -> { + value.toLong() + } + + Short::class.javaObjectType -> { + value.toShort() + } + + Byte::class.javaObjectType -> { + value.toByte() + } + + Double::class.javaObjectType -> { + value.toDouble() + } + + Float::class.javaObjectType -> { + value.toFloat() + } + + String::class.javaObjectType -> { + value + } + + else -> { error( "Missing type converter for type $type! You must register a custom" + " type converter via the DeepLinkDelegate constructor element for all" + " but simple data types.", ) + } } } catch (e: NumberFormatException) { typeConversionErrorNullable(uriTemplate, type, value) @@ -260,20 +294,45 @@ open class BaseDeepLinkDelegate ): Any = try { typeConverters()[type]?.convert(value = value) ?: when (type) { - Boolean::class.javaPrimitiveType -> value.toBoolean() - Int::class.javaPrimitiveType -> value.toInt() - Long::class.javaPrimitiveType -> value.toLong() - Short::class.javaPrimitiveType -> value.toShort() - Byte::class.javaPrimitiveType -> value.toByte() - Double::class.javaPrimitiveType -> value.toDouble() - Float::class.javaPrimitiveType -> value.toFloat() - String::class.javaObjectType -> value - else -> + Boolean::class.javaPrimitiveType -> { + value.toBoolean() + } + + Int::class.javaPrimitiveType -> { + value.toInt() + } + + Long::class.javaPrimitiveType -> { + value.toLong() + } + + Short::class.javaPrimitiveType -> { + value.toShort() + } + + Byte::class.javaPrimitiveType -> { + value.toByte() + } + + Double::class.javaPrimitiveType -> { + value.toDouble() + } + + Float::class.javaPrimitiveType -> { + value.toFloat() + } + + String::class.javaObjectType -> { + value + } + + else -> { error( "Missing type converter for type $type! You must register a custom" + " type converter via the DeepLinkDelegate constructor element for all" + " but simple data types.", ) + } } } catch (e: NumberFormatException) { typeConversionErrorNonNullable(uriTemplate, type, value) @@ -396,8 +455,10 @@ open class BaseDeepLinkDelegate ): IntermediateDeepLinkResult { val clazz = matchedDeeplinkEntry.clazz return when (matchedDeeplinkEntry) { - is DeepLinkEntry.ActivityDeeplinkEntry -> + is DeepLinkEntry.ActivityDeeplinkEntry -> { IntermediateDeepLinkResult(Intent(activity, clazz), null, null) + } + is DeepLinkEntry.MethodDeeplinkEntry -> { try { try { @@ -433,6 +494,7 @@ open class BaseDeepLinkDelegate ) } } + is DeepLinkEntry.HandlerDeepLinkEntry -> { IntermediateDeepLinkResult( Intent(activity, clazz), @@ -507,17 +569,23 @@ open class BaseDeepLinkDelegate methodInvocation: Any?, ): IntermediateDeepLinkResult = when (method.returnType) { - TaskStackBuilder::class.java -> + TaskStackBuilder::class.java -> { intentFromTaskStackBuilder( methodInvocation as TaskStackBuilder?, method.name, ) - DeepLinkMethodResult::class.java -> + } + + DeepLinkMethodResult::class.java -> { intentFromDeepLinkMethodResult( methodInvocation as DeepLinkMethodResult?, method.name, ) - else -> IntermediateDeepLinkResult(methodInvocation as Intent?, null, null) + } + + else -> { + IntermediateDeepLinkResult(methodInvocation as Intent?, null, null) + } } private fun intentFromDeepLinkMethodResult( @@ -584,9 +652,15 @@ open class BaseDeepLinkDelegate registries.mapNotNull { it.idxMatch(uri, configurablePathSegmentReplacements) } return when (entryIdxMatches.size) { // Found no match - 0 -> null + 0 -> { + null + } + // Found one match - 1 -> entryIdxMatches.first() + 1 -> { + entryIdxMatches.first() + } + // Found multiple matches. Sort matches by concreteness: // No variable element > containing placeholders > are a configurable path segment else -> { diff --git a/dependencies.gradle b/dependencies.gradle index 7d684a2c..f613e297 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,42 +1,46 @@ def versions = [ - kotlinVersion : '2.3.0', + kotlinVersion : '2.4.20', appCompatVersion : '1.6.1', localBroadcastManagerVersion : '1.1.0', - roboelectricVersion : '4.14.1', - benchmarkVersion : '1.4.1', - kotlinCompileTestingVersion : '0.12.1', - kspVersion : '2.3.4', - xProcessorVersion : '2.8.4', - mockkVersion : '1.14.7', - ktlintGradlePluginVersion : '5.2.0', + roboelectricVersion : '4.17', + benchmarkVersion : '1.5.0', + kotlinCompileTestingVersion : '0.14.0', + kspVersion : '2.3.12', + xProcessorVersion : '2.8.5', + mockkVersion : '1.14.11', + ktlintGradlePluginVersion : '5.7.0', androidXTestingVersion : '1.7.0', + androidxCoreTestVersion : '1.19.1', testRulesVersion : '1.7.0', - autoCommonVersion : '1.0', + autoCommonVersion : '1.2.2', javaPoetVersion : '1.13.0', jsr305Version : '3.0.2', okioVersion : '3.6.0', androidXAnnotationsVersion : '1.3.0', - gradleMavenPublishPluginVersion : '0.35.0', - dokkaPluginVersion : '2.1.0', + gradleMavenPublishPluginVersion : '0.37.0', + dokkaPluginVersion : '2.2.0', junitVersion : '4.13.2', - assertJVersion : '3.22.0', + checkstyleVersion : '14.1.0', + assertJVersion : '3.27.7', androidVersion : '4.1.1.4', testRunnerVersion : '1.7.0', constraintLayoutVersion : '2.2.1', testExtVersion : '1.3.0', - androicCommonToolsVersion : '30.4.1' + androidCommonToolsVersion : '32.4.1' ] ext.androidConfig = [ - agpVersion : '8.13.2', - compileSdkVersion : 35, + agpVersion : '9.4.1', + compileSdkVersion : 37, + compileSdkMinorVersion : 2, + buildToolsVersion : '37.0.0', minSdkVersion : 16, minSdkVersionBenchmark : 21, targetSdkVersion : 35 ] // Central JVM configuration for all modules -ext.jvmToolchainVersion = 22 // JDK used for compilation +ext.jvmToolchainVersion = 25 // Latest LTS JDK used for compilation ext.jvmTargetVersion = 11 // Bytecode target (compatibility) ext.versions = versions @@ -54,14 +58,16 @@ ext.deps = [ // https://vanniktech.github.io/gradle-maven-publish-plugin/ gradleMavenPublishPlugin : "com.vanniktech:gradle-maven-publish-plugin:$versions.gradleMavenPublishPluginVersion", dokkaPlugin : "org.jetbrains.dokka:dokka-gradle-plugin:$versions.dokkaPluginVersion", + kotlinMetadata : "org.jetbrains.kotlin:kotlin-metadata-jvm:$versions.kotlinVersion", ksp : "com.google.devtools.ksp:symbol-processing-api:$versions.kspVersion", kspGradlePlugin : "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:$versions.kspVersion", xProcessor : "androidx.room:room-compiler-processing:$versions.xProcessorVersion", ktlintGradlePlugin : "org.jmailen.gradle:kotlinter-gradle:$versions.ktlintGradlePluginVersion", constraintLayout : "androidx.constraintlayout:constraintlayout:$versions.constraintLayoutVersion", - androidCommonTools : "com.android.tools:common:$versions.androicCommonToolsVersion", + androidCommonTools : "com.android.tools:common:$versions.androidCommonToolsVersion", // Testing + androidxCoreTest : "androidx.core:core:$versions.androidxCoreTestVersion", androidxTestCore : "androidx.test:core:$versions.androidXTestingVersion", androidxTestRunner : "androidx.test:runner:$versions.testRunnerVersion", androidxTestRules : "androidx.test:rules:$versions.testRulesVersion", diff --git a/gradle/android-test-stubs.gradle b/gradle/android-test-stubs.gradle new file mode 100644 index 00000000..b3f7dce4 --- /dev/null +++ b/gradle/android-test-stubs.gradle @@ -0,0 +1,40 @@ +import org.gradle.api.artifacts.transform.TransformAction +import org.gradle.api.artifacts.transform.TransformOutputs +import org.gradle.api.artifacts.transform.TransformParameters +import org.gradle.api.artifacts.transform.InputArtifact +import org.gradle.api.attributes.Attribute +import org.gradle.api.attributes.Usage +import org.gradle.api.file.FileSystemLocation +import org.gradle.api.provider.Provider + +import java.util.zip.ZipFile + +// JVM processor tests need AndroidX classes without applying the Android plugin. +// Resolve current AARs from Maven and extract their classes instead of checking in JARs. +abstract class AndroidTestClasses implements TransformAction { + @InputArtifact + abstract Provider getInputArtifact() + + @Override + void transform(TransformOutputs outputs) { + new ZipFile(inputArtifact.get().asFile).withCloseable { archive -> + archive.getInputStream(archive.getEntry('classes.jar')).withCloseable { input -> + outputs.file('classes.jar').withOutputStream { output -> output << input } + } + } + } +} + +def artifactType = Attribute.of('artifactType', String) +dependencies.registerTransform(AndroidTestClasses) { + from.attribute(artifactType, 'aar') + to.attribute(artifactType, 'jar') +} +def testAndroidStubs = configurations.create('testAndroidStubs') { + canBeConsumed = false + transitive = false + attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) +} +dependencies.add('testImplementation', testAndroidStubs.incoming.artifactView { + attributes.attribute(artifactType, 'jar') +}.files) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f8e1ee31..5097068a 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 23449a2b..1eca32e7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,10 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +distributionSha256Sum=bafd5ce9cfaea0fbccfdc8439a1ac42fbd4cd9c89dc9a988228d8a2639a58e6c +distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.0-bin.zip networkTimeout=10000 +retries=0 +retryBackOffMs=500 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 99d33d47..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. @@ -15,10 +15,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # -# Gradle start up script for POSIX generated by Gradle. +# gradlew start up script for POSIX generated by Gradle. # # Important for running: # @@ -27,13 +29,38 @@ # 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: # * functions; -# * expansions «$var)}», «## {, $, : - = ? + %# ; -# * «command» and «test» builtins. +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# 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/. # ############################################################################## @@ -59,7 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -87,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -145,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" ) @@ -154,7 +179,7 @@ if "$cygwin" || "$msys" ; then if case $arg in #( -*) false ;; # don't mess with options #( - /?*) t=${arg#)}; t=/${t%%/*} # looks like a POSIX filepath + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac @@ -162,11 +187,14 @@ if "$cygwin" || "$msys" ; then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of - # temporary argument variables. This effectively «shifts» them all in - # one go. - temp="$1" - shift - set -- "$@" "$arg" + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done fi @@ -175,31 +203,46 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, GRADLE_OPTS, and GRADLE_USER_HOME to pass to the JVM -# * --module-path (only if needed) -# * the main class name -# * -classpath -# * -D...appname settings -# * args from the command line +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. -# shellcheck disable=SC2086 set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" -# Stop when "xeli" is set, with eXit status from the last command -if [ -n "${BASH_VERSION-}" ] ; then - shopt -s expand_aliases - alias stop='[[ ${-} == *x* ]] && set +x ; return' +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi -# Use "exec" to preserve process ID. Otherwise, gradle would run in a subprocess, -# and the JVM would be unable to see any signal. -# Not using "exec" would mean: -# 1. The shell would wait for 'java' to return. -# 2. The shell would then return the exit code of 'java'. -# 3. The Java program would not have access to any job signals, like SIGINT. +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index e509b2dd..3185a43f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,93 +1,112 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -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 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -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 - -goto fail - -:execute -@rem Setup the command line - - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* - -: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 +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem gradlew startup script for Windows +@rem +@rem ########################################################################## + +@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=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +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. + +"%COMSPEC%" /c exit 1 +goto exitWithErrorLevel + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +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. + +"%COMSPEC%" /c exit 1 +goto exitWithErrorLevel + +:execute +@rem Setup the command line + + + +@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 + +@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/sample-benchmark/build.gradle b/sample-benchmark/build.gradle index 1dc26e9d..1a152b89 100644 --- a/sample-benchmark/build.gradle +++ b/sample-benchmark/build.gradle @@ -1,11 +1,15 @@ apply plugin: 'com.android.library' apply plugin: 'androidx.benchmark' -apply plugin: 'kotlin-android' apply plugin: 'org.jmailen.kotlinter' android { namespace = "com.airbnb.deeplinkdispatch.samplebenchmark" - compileSdk = androidConfig.compileSdkVersion + compileSdk { + version = release(androidConfig.compileSdkVersion) { + it.minorApiLevel = androidConfig.compileSdkMinorVersion + } + } + buildToolsVersion = androidConfig.buildToolsVersion defaultConfig { minSdk = androidConfig. diff --git a/sample-benchmarkable-library/build.gradle b/sample-benchmarkable-library/build.gradle index da9e5f98..4ddd0aa1 100644 --- a/sample-benchmarkable-library/build.gradle +++ b/sample-benchmarkable-library/build.gradle @@ -1,15 +1,19 @@ apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' apply plugin: 'com.google.devtools.ksp' apply plugin: 'com.airbnb.deeplinkdispatch.manifest-generation' android { namespace = 'com.airbnb.deeplinkdispatch.sample.benchmarkable' - compileSdk androidConfig.compileSdkVersion + compileSdk { + version = release(androidConfig.compileSdkVersion) { + it.minorApiLevel = androidConfig.compileSdkMinorVersion + } + } + buildToolsVersion = androidConfig.buildToolsVersion defaultConfig { minSdk = androidConfig.minSdkVersion - targetSdk = androidConfig.compileSdkVersion + targetSdk = androidConfig.targetSdkVersion javaCompileOptions { annotationProcessorOptions { diff --git a/sample-kapt-library/build.gradle b/sample-kapt-library/build.gradle index f6fa40f7..e89aab3d 100644 --- a/sample-kapt-library/build.gradle +++ b/sample-kapt-library/build.gradle @@ -1,6 +1,5 @@ apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' +apply plugin: 'com.android.legacy-kapt' apply plugin: 'org.jmailen.kotlinter' // This is just here to show an example how DLD works with eitehr `annotationProcessor` (sample-library) @@ -9,11 +8,16 @@ apply plugin: 'org.jmailen.kotlinter' android { namespace = 'com.airbnb.deeplinkdispatch.sample.kaptlibrary' - compileSdk androidConfig.compileSdkVersion + compileSdk { + version = release(androidConfig.compileSdkVersion) { + it.minorApiLevel = androidConfig.compileSdkMinorVersion + } + } + buildToolsVersion = androidConfig.buildToolsVersion defaultConfig { minSdk = androidConfig.minSdkVersion - targetSdk = androidConfig.compileSdkVersion + targetSdk = androidConfig.targetSdkVersion javaCompileOptions { annotationProcessorOptions { diff --git a/sample-ksp-library/build.gradle b/sample-ksp-library/build.gradle index 006ecfb0..e6ce9cea 100644 --- a/sample-ksp-library/build.gradle +++ b/sample-ksp-library/build.gradle @@ -2,7 +2,6 @@ // Published-library integration is documented in README.md under Manifest Generation. apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' apply plugin: 'org.jmailen.kotlinter' apply plugin: 'com.google.devtools.ksp' apply plugin: 'com.airbnb.deeplinkdispatch.manifest-generation' @@ -14,11 +13,16 @@ apply plugin: 'com.airbnb.deeplinkdispatch.manifest-generation' android { namespace = "com.airbnb.deeplinkdispatch.sampleksplibrary" - compileSdk androidConfig.compileSdkVersion + compileSdk { + version = release(androidConfig.compileSdkVersion) { + it.minorApiLevel = androidConfig.compileSdkMinorVersion + } + } + buildToolsVersion = androidConfig.buildToolsVersion defaultConfig { minSdk = androidConfig.minSdkVersion - targetSdk = androidConfig.compileSdkVersion + targetSdk = androidConfig.targetSdkVersion javaCompileOptions { annotationProcessorOptions { diff --git a/sample-library/build.gradle b/sample-library/build.gradle index 55eb6ac9..77bad9d0 100644 --- a/sample-library/build.gradle +++ b/sample-library/build.gradle @@ -1,14 +1,18 @@ apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' apply plugin: 'org.jmailen.kotlinter' android { namespace = 'com.airbnb.deeplinkdispatch.sample.library' - compileSdk androidConfig.compileSdkVersion + compileSdk { + version = release(androidConfig.compileSdkVersion) { + it.minorApiLevel = androidConfig.compileSdkMinorVersion + } + } + buildToolsVersion = androidConfig.buildToolsVersion defaultConfig { minSdk = androidConfig.minSdkVersion - targetSdk = androidConfig.compileSdkVersion + targetSdk = androidConfig.targetSdkVersion javaCompileOptions { annotationProcessorOptions { diff --git a/sample/build.gradle b/sample/build.gradle index f1066bb7..c8fc00f8 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,7 +1,6 @@ plugins { id("com.android.application") id("checkstyle") - id("kotlin-android") id("com.google.devtools.ksp") id('org.jmailen.kotlinter') } @@ -14,13 +13,18 @@ checkstyle { android { namespace = 'com.airbnb.deeplinkdispatch.sample' - compileSdk androidConfig.compileSdkVersion + compileSdk { + version = release(androidConfig.compileSdkVersion) { + it.minorApiLevel = androidConfig.compileSdkMinorVersion + } + } + buildToolsVersion = androidConfig.buildToolsVersion defaultConfig { applicationId "com.airbnb.deeplinkdispatch.sample" // Sample app uses minSdk 21 because the test dependency (androidx.test:core) requires it minSdk = 21 - targetSdk = androidConfig.compileSdkVersion + targetSdk = androidConfig.targetSdkVersion versionCode 1 versionName "1.0" } diff --git a/sample/src/main/java/com/airbnb/deeplinkdispatch/sample/handler/SampleKotlinDeeplinkHandler.kt b/sample/src/main/java/com/airbnb/deeplinkdispatch/sample/handler/SampleKotlinDeeplinkHandler.kt index 89731cd2..73e10451 100644 --- a/sample/src/main/java/com/airbnb/deeplinkdispatch/sample/handler/SampleKotlinDeeplinkHandler.kt +++ b/sample/src/main/java/com/airbnb/deeplinkdispatch/sample/handler/SampleKotlinDeeplinkHandler.kt @@ -18,7 +18,7 @@ object SampleKotlinDeepLinkHandler : context: Context, deepLinkArgs: TestKotlinDeepLinkHandlerDeepLinkArgs, ) { - /** + /* * From here any internal/3rd party navigation framework can be called the provided args. */ Log.d(TAG, "SampleKotlinDeepLinkHandler with $deepLinkArgs") @@ -34,7 +34,7 @@ object SamplePartialParamKotlinDeepLinkHandler : context: Context, deepLinkArgs: TestKotlinDeepLinkHandlerDeepLinkArgsMissingPathParamExtraQueryParam, ) { - /** + /* * From here any internal/3rd party navigation framework can be called the provided args. */ Log.d(TAG, "SamplePartialParamKotlinDeepLinkHandler with $deepLinkArgs") @@ -50,7 +50,7 @@ object SampleNoParamsKotlinDeepLinkHandler : context: Context, deepLinkArgs: Any, ) { - /** + /* * From here any internal/3rd party navigation framework can be called the provided args. */ Log.d(TAG, "SampleNoParamsKotlinDeepLinkHandler with $deepLinkArgs") @@ -64,7 +64,7 @@ abstract class DeepLinkHandlerThatIsUsedAsExtensionByAnotherClass : DeepLinkHand context: Context, deepLinkArgs: Any, ) { - /** + /* * From here any internal/3rd party navigation framework can be called the provided args. */ Log.d(TAG, "SampleNoParamsKotlinDeepLinkHandler with $deepLinkArgs") diff --git a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/AppDeepLinkDelegateTest.kt b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/AppDeepLinkDelegateTest.kt index 4563b52c..10df5e2c 100644 --- a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/AppDeepLinkDelegateTest.kt +++ b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/AppDeepLinkDelegateTest.kt @@ -17,9 +17,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.RuntimeEnvironment -import org.robolectric.annotation.Config -@Config(sdk = [21]) @RunWith(RobolectricTestRunner::class) class AppDeepLinkDelegateTest { // Demo test to find duplicate URLs across all modules diff --git a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/AutoGenIntentFiltersTest.java b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/AutoGenIntentFiltersTest.java index 93724a78..09f4e7bc 100644 --- a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/AutoGenIntentFiltersTest.java +++ b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/AutoGenIntentFiltersTest.java @@ -31,7 +31,7 @@ import java.util.HashMap; import java.util.Map; -@Config(sdk = 21, shadows = {ShadowTaskStackBuilder.class}) +@Config(shadows = {ShadowTaskStackBuilder.class}) @RunWith(RobolectricTestRunner.class) public class AutoGenIntentFiltersTest { diff --git a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/ConfigurablePathSegmentTest.kt b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/ConfigurablePathSegmentTest.kt index 44b59f0c..b1297e64 100644 --- a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/ConfigurablePathSegmentTest.kt +++ b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/ConfigurablePathSegmentTest.kt @@ -10,9 +10,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.RuntimeEnvironment -import org.robolectric.annotation.Config -@Config(sdk = [21]) @RunWith(RobolectricTestRunner::class) class ConfigurablePathSegmentTest { private val configurablePathSegmentReplacementsAllEmpty = diff --git a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/CustomPrefixesActivityTest.java b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/CustomPrefixesActivityTest.java index 908e066a..ad1d303e 100644 --- a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/CustomPrefixesActivityTest.java +++ b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/CustomPrefixesActivityTest.java @@ -13,7 +13,6 @@ import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowActivity; import java.util.List; @@ -23,7 +22,6 @@ import static org.hamcrest.core.IsEqual.equalTo; import static org.robolectric.Shadows.shadowOf; -@Config(sdk = 21) @RunWith(RobolectricTestRunner.class) public class CustomPrefixesActivityTest { @Test public void testAppDeepLinkIntent() { diff --git a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/DeepLinkHandlerTest.kt b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/DeepLinkHandlerTest.kt index 7fe4d0a6..e31a1d35 100644 --- a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/DeepLinkHandlerTest.kt +++ b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/DeepLinkHandlerTest.kt @@ -19,9 +19,7 @@ import org.junit.runner.RunWith import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner import org.robolectric.Shadows -import org.robolectric.annotation.Config -@Config(sdk = [21]) @RunWith(RobolectricTestRunner::class) class DeepLinkHandlerTest { @Test diff --git a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/DeepLinkHandlerTypeConversionTest.kt b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/DeepLinkHandlerTypeConversionTest.kt index b44c2a36..a1df2991 100644 --- a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/DeepLinkHandlerTypeConversionTest.kt +++ b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/DeepLinkHandlerTypeConversionTest.kt @@ -16,10 +16,8 @@ import org.junit.runner.RunWith import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner import org.robolectric.Shadows.shadowOf -import org.robolectric.annotation.Config import java.lang.NumberFormatException -@Config(sdk = [21]) @RunWith(RobolectricTestRunner::class) class DeepLinkHandlerTypeConversionTest { @Test diff --git a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/MainActivityTest.java b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/MainActivityTest.java index 8a0cb76d..dcaf27ac 100644 --- a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/MainActivityTest.java +++ b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/MainActivityTest.java @@ -33,7 +33,7 @@ import androidx.core.app.TaskStackBuilder; -@Config(sdk = 21, shadows = {ShadowTaskStackBuilder.class}) +@Config(shadows = {ShadowTaskStackBuilder.class}) @RunWith(RobolectricTestRunner.class) public class MainActivityTest { @Test diff --git a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/SecondActivityTest.java b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/SecondActivityTest.java index 72b2fb87..4df39fac 100644 --- a/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/SecondActivityTest.java +++ b/sample/src/test/java/com/airbnb/deeplinkdispatch/sample/SecondActivityTest.java @@ -10,14 +10,12 @@ import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowActivity; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; import static org.robolectric.Shadows.shadowOf; -@Config(sdk = 21) @RunWith(RobolectricTestRunner.class) public class SecondActivityTest { @Test public void testIntent() { diff --git a/sample/src/test/resources/robolectric.properties b/sample/src/test/resources/robolectric.properties new file mode 100644 index 00000000..6fdd0f21 --- /dev/null +++ b/sample/src/test/resources/robolectric.properties @@ -0,0 +1,2 @@ +# Exercise the existing sample target SDK; newer Robolectric releases no longer support API 21. +sdk=35