diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 265ff2ba5..efe4ef29a 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -10,7 +10,7 @@ Thanks for submitting a pull request. To accept your pull request we need you do **Ensure tests pass and code is formatted correctly** - Run local tests on the `DemoDebug` variant by running `./gradlew testDemoDebug` -- Fix code formatting: `./gradlew --init-script gradle/init.gradle.kts spotlessApply` +- Fix code formatting: `./gradlew spotlessApply` **Add a description** diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index 076fd2acc..643ad0b92 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -48,7 +48,35 @@ jobs: run: ./gradlew :build-logic:convention:check - name: Check spotless - run: ./gradlew spotlessCheck --init-script gradle/init.gradle.kts --no-configuration-cache + run: ./gradlew spotlessCheck + + - name: Update Graphs + id: graphs_update + continue-on-error: true + run: ./gradlew graphUpdate + + - name: Check Graphs + id: graphs_verify + run: | + if ! git diff --quiet "**/README.md"; then + echo "::error::Module graph updates detected. Please run './gradlew graphUpdate' locally and commit the changes." + exit 1 + fi + + - name: Prevent updating graphs if this is a fork + id: checkfork_graphs + continue-on-error: false + if: steps.graphs_verify.outcome == 'failure' && github.event.pull_request.head.repo.full_name != github.repository + run: | + echo "::error::Module graph updates detected, please update graphs with: ./gradlew graphUpdate" && exit 1 + + - name: Push updated graphs if available + uses: stefanzweifel/git-auto-commit-action@v5 + if: steps.graphs_update.outcome == 'success' && steps.graphs_verify.outcome == 'failure' && github.event_name == 'pull_request' + with: + file_pattern: '**/README.md' + disable_globbing: true + commit_message: "Updates module graphs" - name: Check Dependency Guard id: dependencyguard_verify diff --git a/.run/spotlessApply.run.xml b/.run/spotlessApply.run.xml index 5c4dac833..c79311274 100644 --- a/.run/spotlessApply.run.xml +++ b/.run/spotlessApply.run.xml @@ -4,7 +4,7 @@ diff --git a/AGENTS.md b/AGENTS.md index 84ae649c1..8ba1d47b8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,7 +11,7 @@ Now in Android (KMP edition) — a Kotlin Multiplatform fork of Google's Now in ```bash # Format code (must pass before merge) ./gradlew spotlessApply -./gradlew spotlessCheck --init-script gradle/init.gradle.kts --no-configuration-cache +./gradlew spotlessCheck # Build ./gradlew :app:assemble # main app (all variants) @@ -43,6 +43,9 @@ Now in Android (KMP edition) — a Kotlin Multiplatform fork of Google's Now in # Build-logic check ./gradlew :build-logic:convention:check +# Module graphs +./gradlew graphUpdate # update README.md module graphs + # Badging check ./gradlew :app:checkReleaseBadging ``` diff --git a/app-nia-catalog/build.gradle.kts b/app-nia-catalog/build.gradle.kts index abaf5b1eb..fc1404d7d 100644 --- a/app-nia-catalog/build.gradle.kts +++ b/app-nia-catalog/build.gradle.kts @@ -26,6 +26,7 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose) + alias(libs.plugins.spotless) } kotlin { @@ -97,8 +98,8 @@ android { versionCode = 1 versionName = "0.0.1" // X.Y.Z; X = Major, Y = minor, Z = Patch level minSdk = 24 - targetSdk = 34 - compileSdk = 35 + targetSdk = 36 + compileSdk = 36 // The UI catalog does not depend on content from the app, however, it depends on modules // which do, so we must specify a default value for the contentType dimension. missingDimensionStrategy("contentType", "demo") @@ -147,10 +148,24 @@ compose.desktop { } } -compose.experimental { - -} - dependencyGuard { configuration("releaseRuntimeClasspath") } + +spotless { + kotlin { + target("src/**/*.kt") + ktlint(libs.versions.ktlint.get()) + .editorConfigOverride(mapOf("android" to "true")) + licenseHeaderFile(rootProject.file("spotless/copyright.kt")) + } + format("kts") { + target("*.kts") + targetExclude("**/build/**/*.kts") + licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)") + } + format("xml") { + target("src/**/*.xml") + licenseHeaderFile(rootProject.file("spotless/copyright.xml"), "(<[^!?])") + } +} diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt index c48e02b95..8cae66b47 100644 --- a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt +++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt @@ -11,83 +11,100 @@ androidx.autofill:autofill:1.0.0 androidx.collection:collection-jvm:1.5.0 androidx.collection:collection-ktx:1.5.0 androidx.collection:collection:1.5.0 -androidx.compose.animation:animation-android:1.8.0 -androidx.compose.animation:animation-core-android:1.8.0 -androidx.compose.animation:animation-core:1.8.0 -androidx.compose.animation:animation:1.8.0 -androidx.compose.foundation:foundation-android:1.8.0 -androidx.compose.foundation:foundation-layout-android:1.8.0 -androidx.compose.foundation:foundation-layout:1.8.0 -androidx.compose.foundation:foundation:1.8.0 +androidx.compose.animation:animation-android:1.10.0 +androidx.compose.animation:animation-core-android:1.10.0 +androidx.compose.animation:animation-core:1.10.0 +androidx.compose.animation:animation:1.10.0 +androidx.compose.foundation:foundation-android:1.10.0 +androidx.compose.foundation:foundation-layout-android:1.10.0 +androidx.compose.foundation:foundation-layout:1.10.0 +androidx.compose.foundation:foundation:1.10.0 androidx.compose.material3.adaptive:adaptive-android:1.1.0 androidx.compose.material3.adaptive:adaptive-layout-android:1.1.0 androidx.compose.material3.adaptive:adaptive-layout:1.1.0 androidx.compose.material3.adaptive:adaptive:1.1.0 -androidx.compose.material3:material3-adaptive-navigation-suite-android:1.3.2 -androidx.compose.material3:material3-adaptive-navigation-suite:1.3.2 -androidx.compose.material3:material3-android:1.3.2 -androidx.compose.material3:material3:1.3.2 -androidx.compose.material:material-android:1.8.0 +androidx.compose.material3:material3-adaptive-navigation-suite-android:1.4.0 +androidx.compose.material3:material3-adaptive-navigation-suite:1.4.0 +androidx.compose.material3:material3-android:1.4.0 +androidx.compose.material3:material3:1.4.0 +androidx.compose.material:material-android:1.10.0 androidx.compose.material:material-icons-core-android:1.7.6 androidx.compose.material:material-icons-core:1.7.6 androidx.compose.material:material-icons-extended-android:1.7.6 androidx.compose.material:material-icons-extended:1.7.6 -androidx.compose.material:material-ripple-android:1.8.0 -androidx.compose.material:material-ripple:1.8.0 -androidx.compose.material:material:1.8.0 -androidx.compose.runtime:runtime-android:1.8.1 -androidx.compose.runtime:runtime-saveable-android:1.8.1 -androidx.compose.runtime:runtime-saveable:1.8.1 -androidx.compose.runtime:runtime:1.8.1 -androidx.compose.ui:ui-android:1.8.1 -androidx.compose.ui:ui-geometry-android:1.8.1 -androidx.compose.ui:ui-geometry:1.8.1 -androidx.compose.ui:ui-graphics-android:1.8.1 -androidx.compose.ui:ui-graphics:1.8.1 -androidx.compose.ui:ui-text-android:1.8.1 -androidx.compose.ui:ui-text:1.8.1 -androidx.compose.ui:ui-tooling-preview-android:1.8.1 -androidx.compose.ui:ui-tooling-preview:1.8.1 -androidx.compose.ui:ui-unit-android:1.8.1 -androidx.compose.ui:ui-unit:1.8.1 -androidx.compose.ui:ui-util-android:1.8.1 -androidx.compose.ui:ui-util:1.8.1 -androidx.compose.ui:ui:1.8.1 +androidx.compose.material:material-ripple-android:1.10.0 +androidx.compose.material:material-ripple:1.10.0 +androidx.compose.material:material:1.10.0 +androidx.compose.runtime:runtime-android:1.10.0 +androidx.compose.runtime:runtime-annotation-android:1.10.0 +androidx.compose.runtime:runtime-annotation:1.10.0 +androidx.compose.runtime:runtime-retain-android:1.10.0 +androidx.compose.runtime:runtime-retain:1.10.0 +androidx.compose.runtime:runtime-saveable-android:1.10.0 +androidx.compose.runtime:runtime-saveable:1.10.0 +androidx.compose.runtime:runtime:1.10.0 +androidx.compose.ui:ui-android:1.10.0 +androidx.compose.ui:ui-geometry-android:1.10.0 +androidx.compose.ui:ui-geometry:1.10.0 +androidx.compose.ui:ui-graphics-android:1.10.0 +androidx.compose.ui:ui-graphics:1.10.0 +androidx.compose.ui:ui-text-android:1.10.0 +androidx.compose.ui:ui-text:1.10.0 +androidx.compose.ui:ui-tooling-preview-android:1.10.0 +androidx.compose.ui:ui-tooling-preview:1.10.0 +androidx.compose.ui:ui-unit-android:1.10.0 +androidx.compose.ui:ui-unit:1.10.0 +androidx.compose.ui:ui-util-android:1.10.0 +androidx.compose.ui:ui-util:1.10.0 +androidx.compose.ui:ui:1.10.0 androidx.concurrent:concurrent-futures:1.1.0 -androidx.core:core-ktx:1.15.0 -androidx.core:core:1.15.0 +androidx.core:core-ktx:1.16.0 +androidx.core:core-viewtree:1.0.0 +androidx.core:core:1.16.0 androidx.customview:customview-poolingcontainer:1.0.0 +androidx.documentfile:documentfile:1.0.0 +androidx.dynamicanimation:dynamicanimation:1.0.0 androidx.emoji2:emoji2:1.4.0 androidx.exifinterface:exifinterface:1.4.1 androidx.graphics:graphics-path:1.0.1 androidx.interpolator:interpolator:1.0.0 -androidx.lifecycle:lifecycle-common-java8:2.8.7 -androidx.lifecycle:lifecycle-common-jvm:2.8.7 -androidx.lifecycle:lifecycle-common:2.8.7 -androidx.lifecycle:lifecycle-livedata-core:2.8.7 -androidx.lifecycle:lifecycle-process:2.8.7 -androidx.lifecycle:lifecycle-runtime-android:2.8.7 -androidx.lifecycle:lifecycle-runtime-compose-android:2.8.7 -androidx.lifecycle:lifecycle-runtime-compose:2.8.7 -androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.7 -androidx.lifecycle:lifecycle-runtime-ktx:2.8.7 -androidx.lifecycle:lifecycle-runtime:2.8.7 -androidx.lifecycle:lifecycle-viewmodel-android:2.8.7 -androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.7 -androidx.lifecycle:lifecycle-viewmodel:2.8.7 +androidx.legacy:legacy-support-core-utils:1.0.0 +androidx.lifecycle:lifecycle-common-java8:2.9.4 +androidx.lifecycle:lifecycle-common-jvm:2.9.4 +androidx.lifecycle:lifecycle-common:2.9.4 +androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.4 +androidx.lifecycle:lifecycle-livedata-core:2.9.4 +androidx.lifecycle:lifecycle-livedata:2.9.4 +androidx.lifecycle:lifecycle-process:2.9.4 +androidx.lifecycle:lifecycle-runtime-android:2.9.4 +androidx.lifecycle:lifecycle-runtime-compose-android:2.9.4 +androidx.lifecycle:lifecycle-runtime-compose:2.9.4 +androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.4 +androidx.lifecycle:lifecycle-runtime-ktx:2.9.4 +androidx.lifecycle:lifecycle-runtime:2.9.4 +androidx.lifecycle:lifecycle-viewmodel-android:2.9.4 +androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.4 +androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.4 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4 +androidx.lifecycle:lifecycle-viewmodel:2.9.4 +androidx.loader:loader:1.0.0 +androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 +androidx.print:print:1.0.0 androidx.profileinstaller:profileinstaller:1.4.1 -androidx.savedstate:savedstate-ktx:1.2.1 -androidx.savedstate:savedstate:1.2.1 +androidx.savedstate:savedstate-android:1.3.3 +androidx.savedstate:savedstate-compose-android:1.3.3 +androidx.savedstate:savedstate-compose:1.3.3 +androidx.savedstate:savedstate-ktx:1.3.3 +androidx.savedstate:savedstate:1.3.3 androidx.startup:startup-runtime:1.1.1 androidx.tracing:tracing:1.2.0 +androidx.transition:transition:1.6.0 androidx.vectordrawable:vectordrawable-animated:1.1.0 androidx.vectordrawable:vectordrawable:1.1.0 androidx.versionedparcelable:versionedparcelable:1.1.1 -androidx.window.extensions.core:core:1.0.0 -androidx.window:window-core-android:1.3.0 -androidx.window:window-core:1.3.0 -androidx.window:window:1.3.0 +androidx.window:window-core-android:1.5.0 +androidx.window:window-core:1.5.0 +androidx.window:window:1.5.0 com.google.accompanist:accompanist-drawablepainter:0.37.3 com.google.guava:listenablefuture:1.0 com.squareup.okio:okio-jvm:3.11.0 @@ -96,43 +113,49 @@ io.coil-kt.coil3:coil-compose-core-android:3.2.0 io.coil-kt.coil3:coil-compose-core:3.2.0 io.coil-kt.coil3:coil-core-android:3.2.0 io.coil-kt.coil3:coil-core:3.2.0 -org.jetbrains.androidx.lifecycle:lifecycle-common:2.8.4 -org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.8.4 -org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.8.4 -org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.8.4 -org.jetbrains.androidx.window:window-core:1.3.1 -org.jetbrains.compose.animation:animation-core:1.8.0 -org.jetbrains.compose.animation:animation:1.8.0 -org.jetbrains.compose.annotation-internal:annotation:1.8.0 -org.jetbrains.compose.collection-internal:collection:1.8.0 -org.jetbrains.compose.components:components-resources-android:1.8.0 -org.jetbrains.compose.components:components-resources:1.8.0 -org.jetbrains.compose.components:components-ui-tooling-preview-android:1.8.0 -org.jetbrains.compose.components:components-ui-tooling-preview:1.8.0 -org.jetbrains.compose.foundation:foundation-layout:1.8.0 -org.jetbrains.compose.foundation:foundation:1.8.0 +org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.6 +org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.6 +org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.6 +org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.6 +org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.6 +org.jetbrains.androidx.savedstate:savedstate-compose:1.3.6 +org.jetbrains.androidx.savedstate:savedstate:1.3.6 +org.jetbrains.androidx.window:window-core:1.4.0 +org.jetbrains.compose.animation:animation-core:1.10.0 +org.jetbrains.compose.animation:animation:1.10.0 +org.jetbrains.compose.annotation-internal:annotation:1.10.0 +org.jetbrains.compose.collection-internal:collection:1.10.0 +org.jetbrains.compose.components:components-resources-android:1.10.0 +org.jetbrains.compose.components:components-resources:1.10.0 +org.jetbrains.compose.components:components-ui-tooling-preview-android:1.10.0 +org.jetbrains.compose.components:components-ui-tooling-preview:1.10.0 +org.jetbrains.compose.foundation:foundation-layout:1.10.0 +org.jetbrains.compose.foundation:foundation:1.10.0 org.jetbrains.compose.material3.adaptive:adaptive-layout:1.1.0 -org.jetbrains.compose.material3.adaptive:adaptive:1.1.0 -org.jetbrains.compose.material3:material3-adaptive-navigation-suite:1.8.0 -org.jetbrains.compose.material3:material3:1.8.0 +org.jetbrains.compose.material3.adaptive:adaptive:1.1.2 +org.jetbrains.compose.material3:material3-adaptive-navigation-suite:1.9.0 +org.jetbrains.compose.material3:material3:1.9.0 org.jetbrains.compose.material:material-icons-core:1.7.3 org.jetbrains.compose.material:material-icons-extended:1.7.3 -org.jetbrains.compose.material:material-ripple:1.8.0 -org.jetbrains.compose.material:material:1.8.0 -org.jetbrains.compose.runtime:runtime-saveable:1.8.0 -org.jetbrains.compose.runtime:runtime:1.8.0 -org.jetbrains.compose.ui:ui-backhandler-android:1.8.0 -org.jetbrains.compose.ui:ui-backhandler:1.8.0 -org.jetbrains.compose.ui:ui-geometry:1.8.0 -org.jetbrains.compose.ui:ui-graphics:1.8.0 -org.jetbrains.compose.ui:ui-text:1.8.0 -org.jetbrains.compose.ui:ui-unit:1.8.0 -org.jetbrains.compose.ui:ui-util:1.8.0 -org.jetbrains.compose.ui:ui:1.8.0 -org.jetbrains.kotlin:kotlin-stdlib:2.1.20 +org.jetbrains.compose.material:material-ripple:1.10.0 +org.jetbrains.compose.material:material:1.10.0 +org.jetbrains.compose.runtime:runtime-saveable:1.10.0 +org.jetbrains.compose.runtime:runtime:1.10.0 +org.jetbrains.compose.ui:ui-backhandler-android:1.9.1 +org.jetbrains.compose.ui:ui-backhandler:1.9.1 +org.jetbrains.compose.ui:ui-geometry:1.10.0 +org.jetbrains.compose.ui:ui-graphics:1.10.0 +org.jetbrains.compose.ui:ui-text:1.10.0 +org.jetbrains.compose.ui:ui-unit:1.10.0 +org.jetbrains.compose.ui:ui-util:1.10.0 +org.jetbrains.compose.ui:ui:1.10.0 +org.jetbrains.kotlin:kotlin-stdlib:2.3.0 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2 +org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3 +org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.3 +org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3 org.jetbrains:annotations:23.0.0 org.jspecify:jspecify:1.0.0 diff --git a/app/release-badging.txt b/app/release-badging.txt index 5c1511f5b..95054fd52 100644 --- a/app/release-badging.txt +++ b/app/release-badging.txt @@ -1,6 +1,6 @@ -package: name='com.google.samples.apps.nowinandroid' versionCode='8' versionName='0.1.2' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15' -minSdkVersion:'21' -targetSdkVersion:'34' +package: name='com.google.samples.apps.nowinandroid' versionCode='8' versionName='0.1.2' platformBuildVersionName='16' platformBuildVersionCode='36' compileSdkVersion='36' compileSdkVersionCodename='16' +minSdkVersion:'23' +targetSdkVersion:'36' uses-permission: name='android.permission.INTERNET' uses-permission: name='android.permission.ACCESS_NETWORK_STATE' uses-permission: name='android.permission.POST_NOTIFICATIONS' diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index 104cf1e6a..42d4d57b3 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -42,6 +42,7 @@ dependencies { compileOnly(libs.compose.gradlePlugin) compileOnly(libs.kotlin.gradlePlugin) compileOnly(libs.ksp.gradlePlugin) + compileOnly(libs.spotless.gradlePlugin) implementation(libs.truth) lintChecks(libs.androidx.lint.gradle) } @@ -99,5 +100,9 @@ gradlePlugin { id = "nowinandroid.di.koin" implementationClass = "KoinConventionPlugin" } + register("root") { + id = "nowinandroid.root" + implementationClass = "RootConventionPlugin" + } } } diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt index b0eece41d..c16b87a7e 100644 --- a/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt @@ -1,17 +1,17 @@ /* * Copyright 2022 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ import com.android.build.api.dsl.ApplicationExtension diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt index d249e4cbf..6f7db9186 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt @@ -1,17 +1,17 @@ /* * Copyright 2022 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ import com.android.build.api.dsl.LibraryExtension diff --git a/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt index 884d6f076..c701983b1 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt @@ -1,17 +1,17 @@ /* * Copyright 2022 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ import com.android.build.api.dsl.ApplicationExtension diff --git a/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt index 49c2eecec..e87b43290 100644 --- a/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt @@ -1,22 +1,23 @@ /* * Copyright 2022 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ import com.android.build.api.dsl.TestExtension import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.google.samples.apps.nowinandroid.configureKotlinAndroid +import com.google.samples.apps.nowinandroid.configureSpotlessForAndroid import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.apply @@ -27,10 +28,11 @@ class AndroidTestConventionPlugin : Plugin { with(target) { apply(plugin = "com.android.test") apply(plugin = "org.jetbrains.kotlin.android") + configureSpotlessForAndroid() extensions.configure { configureKotlinAndroid(this) - defaultConfig.targetSdk = 35 + defaultConfig.targetSdk = 36 configureGradleManagedDevices(this) } } diff --git a/build-logic/convention/src/main/kotlin/CmpApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/CmpApplicationConventionPlugin.kt index aee833ee7..7cbafa706 100644 --- a/build-logic/convention/src/main/kotlin/CmpApplicationConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/CmpApplicationConventionPlugin.kt @@ -1,17 +1,17 @@ /* - * Copyright 2024 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ import com.android.build.api.dsl.ApplicationExtension @@ -20,6 +20,7 @@ import com.google.samples.apps.nowinandroid.configureBadgingTasks import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configurePrintApksTask +import com.google.samples.apps.nowinandroid.configureSpotlessForAndroid import com.google.samples.apps.nowinandroid.libs import org.gradle.api.JavaVersion import org.gradle.api.Plugin @@ -44,9 +45,10 @@ class CmpApplicationConventionPlugin : Plugin { // apply("com.dropbox.dependency-guard") } configureComposeMultiplatformApp() + configureSpotlessForAndroid() extensions.configure { configureKotlinAndroid(this) - defaultConfig.targetSdk = 34 + defaultConfig.targetSdk = 36 @Suppress("UnstableApiUsage") testOptions.animationsDisabled = true configureGradleManagedDevices(this) @@ -68,7 +70,6 @@ class CmpApplicationConventionPlugin : Plugin { "androidMainImplementation"(libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) "androidMainImplementation"(libs.findLibrary("androidx.tracing.ktx").get()) } - } } } diff --git a/build-logic/convention/src/main/kotlin/CmpFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/CmpFeatureConventionPlugin.kt index 674feab48..e60bc0aa0 100644 --- a/build-logic/convention/src/main/kotlin/CmpFeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/CmpFeatureConventionPlugin.kt @@ -1,17 +1,17 @@ /* - * Copyright 2024 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ import com.android.build.gradle.LibraryExtension @@ -21,7 +21,6 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies -import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension // Convention plugin for the Compose Multiplatform feature module class CmpFeatureConventionPlugin : Plugin { @@ -62,7 +61,6 @@ class CmpFeatureConventionPlugin : Plugin { "androidInstrumentedTestImplementation"(libs.findLibrary("androidx.test.junit").get()) "androidInstrumentedTestImplementation"(libs.findLibrary("androidx.test.runner").get()) } - } } } diff --git a/build-logic/convention/src/main/kotlin/JvmLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/JvmLibraryConventionPlugin.kt index a1477891d..f2be5c8ac 100644 --- a/build-logic/convention/src/main/kotlin/JvmLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/JvmLibraryConventionPlugin.kt @@ -1,20 +1,21 @@ /* * Copyright 2023 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ import com.google.samples.apps.nowinandroid.configureKotlinJvm +import com.google.samples.apps.nowinandroid.configureSpotlessForJvm import com.google.samples.apps.nowinandroid.libs import org.gradle.api.Plugin import org.gradle.api.Project @@ -28,6 +29,7 @@ class JvmLibraryConventionPlugin : Plugin { apply(plugin = "nowinandroid.android.lint") configureKotlinJvm() + configureSpotlessForJvm() dependencies { "testImplementation"(libs.findLibrary("kotlin.test").get()) } diff --git a/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt index 2c9a33b29..789d6a06b 100644 --- a/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt @@ -18,21 +18,23 @@ import com.android.build.gradle.LibraryExtension import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configureKotlinMultiplatform +import com.google.samples.apps.nowinandroid.configureSpotlessForAndroid import com.google.samples.apps.nowinandroid.libs import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies -class KmpLibraryConventionPlugin: Plugin { +class KmpLibraryConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { plugins.apply("com.android.library") plugins.apply("org.jetbrains.kotlin.multiplatform") configureKotlinMultiplatform() + configureSpotlessForAndroid() extensions.configure { configureKotlinAndroid(this) - defaultConfig.targetSdk = 34 + defaultConfig.targetSdk = 36 configureGradleManagedDevices(this) // The resource prefix is derived from the module name, // so resources inside ":core:module1" must be prefixed with "core_module1_" diff --git a/build-logic/convention/src/main/kotlin/KoinConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KoinConventionPlugin.kt index 81022eeda..61894022a 100644 --- a/build-logic/convention/src/main/kotlin/KoinConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KoinConventionPlugin.kt @@ -21,7 +21,7 @@ import org.gradle.kotlin.dsl.dependencies import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask -class KoinConventionPlugin: Plugin { +class KoinConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { @@ -49,7 +49,16 @@ class KoinConventionPlugin: Plugin { } project.tasks.withType(KotlinCompilationTask::class.java).configureEach { - if(name != "kspCommonMainKotlinMetadata") { + if (name != "kspCommonMainKotlinMetadata") { + dependsOn("kspCommonMainKotlinMetadata") + } + } + // KSP2 uses KspAATask which isn't a KotlinCompilationTask but still + // needs to depend on kspCommonMainKotlinMetadata for the shared source set. + project.tasks.configureEach { + if (name.startsWith("ksp") && name != "kspCommonMainKotlinMetadata" && + name.contains("Kotlin") + ) { dependsOn("kspCommonMainKotlinMetadata") } } diff --git a/build-logic/convention/src/main/kotlin/KotlinInjectConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KotlinInjectConventionPlugin.kt index cd151a5b2..a31042b22 100644 --- a/build-logic/convention/src/main/kotlin/KotlinInjectConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KotlinInjectConventionPlugin.kt @@ -19,7 +19,7 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.dependencies -class KotlinInjectConventionPlugin: Plugin { +class KotlinInjectConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { @@ -42,4 +42,4 @@ class KotlinInjectConventionPlugin: Plugin { } } } -} \ No newline at end of file +} diff --git a/build-logic/convention/src/main/kotlin/RootConventionPlugin.kt b/build-logic/convention/src/main/kotlin/RootConventionPlugin.kt new file mode 100644 index 000000000..ca8f77144 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/RootConventionPlugin.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. + */ + +import com.google.samples.apps.nowinandroid.configureGraphTasks +import org.gradle.api.Plugin +import org.gradle.api.Project + +class RootConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + configureGraphTasks() + } + } +} diff --git a/build-logic/convention/src/main/kotlin/SqlDelightConventionPlugin.kt b/build-logic/convention/src/main/kotlin/SqlDelightConventionPlugin.kt index 29bc3bf54..688d8502a 100644 --- a/build-logic/convention/src/main/kotlin/SqlDelightConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/SqlDelightConventionPlugin.kt @@ -17,10 +17,10 @@ import org.gradle.api.Plugin import org.gradle.api.Project -class SqlDelightConventionPlugin: Plugin { +class SqlDelightConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { pluginManager.apply("app.cash.sqldelight") } } -} \ No newline at end of file +} diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidInstrumentedTests.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidInstrumentedTests.kt index c51dac5c9..9531e9caf 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidInstrumentedTests.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidInstrumentedTests.kt @@ -1,17 +1,17 @@ /* * Copyright 2023 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ package com.google.samples.apps.nowinandroid @@ -30,6 +30,6 @@ import org.gradle.api.Project internal fun LibraryAndroidComponentsExtension.disableUnnecessaryAndroidTests( project: Project, ) = beforeVariants { - it.androidTest.enable = it.androidTest.enable - && project.projectDir.resolve("src/androidTest").exists() + it.androidTest.enable = it.androidTest.enable && + project.projectDir.resolve("src/androidTest").exists() } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt index e747325ee..803ee00e7 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt @@ -1,17 +1,17 @@ /* * Copyright 2023 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ package com.google.samples.apps.nowinandroid @@ -123,7 +123,6 @@ fun Project.configureBadgingTasks( badging = project.layout.buildDirectory.file( "outputs/apk_from_bundle/${variant.name}/${variant.name}-badging.txt", ) - } val updateBadgingTaskName = "update${capitalizedVariantName}Badging" @@ -141,7 +140,6 @@ fun Project.configureBadgingTasks( this.updateBadgingTaskName = updateBadgingTaskName output = project.layout.buildDirectory.dir("intermediates/$checkBadgingTaskName") - } } } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/GradleManagedDevices.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/GradleManagedDevices.kt index bb4252577..2cadff20d 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/GradleManagedDevices.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/GradleManagedDevices.kt @@ -1,17 +1,17 @@ /* * Copyright 2023 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ package com.google.samples.apps.nowinandroid diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Graph.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Graph.kt new file mode 100644 index 000000000..9d0c1db35 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Graph.kt @@ -0,0 +1,320 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. + */ + +package com.google.samples.apps.nowinandroid + +import org.gradle.api.Project +import org.gradle.api.artifacts.ProjectDependency +import java.io.File + +/** + * Module types for the KMP fork of Now in Android. + * Maps to convention plugin IDs. + */ +enum class PluginType( + val id: String, + val displayName: String, + val color: String, +) { + CmpApplication( + id = "nowinandroid.cmp.application", + displayName = "cmp-application", + color = "#CAFFBF", + ), + CmpFeature( + id = "nowinandroid.cmp.feature", + displayName = "cmp-feature", + color = "#FFD6A5", + ), + KmpLibrary( + id = "nowinandroid.kmp.library", + displayName = "kmp-library", + color = "#9BF6FF", + ), + JvmLibrary( + id = "nowinandroid.jvm.library", + displayName = "jvm-library", + color = "#BDB2FF", + ), + AndroidTest( + id = "nowinandroid.android.test", + displayName = "android-test", + color = "#A0C4FF", + ), +} + +/** + * Edge types representing different dependency configurations. + */ +enum class EdgeType(val mermaidStyle: String) { + Api("-->"), + Implementation("-.->"), +} + +data class GraphEdge( + val from: String, + val to: String, + val type: EdgeType, + val label: String? = null, +) + +private val supportedConfigurations = listOf( + "commonMainApi" to EdgeType.Api, + "commonMainImplementation" to EdgeType.Implementation, + "api" to EdgeType.Api, + "implementation" to EdgeType.Implementation, + "baselineProfile" to EdgeType.Implementation, + "testedApks" to EdgeType.Implementation, +) + +private val labeledConfigurations = setOf("baselineProfile", "testedApks") + +/** + * Detects the plugin type of a project based on which convention plugins are applied. + */ +fun Project.pluginType(): PluginType? { + return PluginType.entries.firstOrNull { pluginManager.hasPlugin(it.id) } +} + +/** + * Collects all project dependency edges for the given project. + */ +fun Project.collectEdges(): List { + val edges = mutableListOf() + for ((configName, edgeType) in supportedConfigurations) { + val config = configurations.findByName(configName) ?: continue + config.dependencies.filterIsInstance().forEach { dep -> + val label = if (configName in labeledConfigurations) configName else null + val depPath = dep.path + edges.add(GraphEdge(path, depPath, edgeType, label)) + } + } + return edges +} + +/** + * Generates the Mermaid graph text for a given root project, showing all + * modules that are reachable from the specified project. + */ +fun generateMermaidGraph( + rootProjectPath: String, + allEdges: Map>, + allPluginTypes: Map, + ignoredProjects: Set = emptySet(), +): String { + // Find all reachable modules from the root + val reachable = mutableSetOf(rootProjectPath) + val queue = ArrayDeque() + queue.add(rootProjectPath) + while (queue.isNotEmpty()) { + val current = queue.removeFirst() + allEdges[current]?.forEach { edge -> + if (edge.to !in reachable && edge.to !in ignoredProjects) { + reachable.add(edge.to) + queue.add(edge.to) + } + } + } + + val relevantEdges = allEdges.values.flatten() + .filter { it.from in reachable && it.to in reachable } + .filter { it.from !in ignoredProjects && it.to !in ignoredProjects } + .sortedWith(compareBy({ it.from }, { it.to })) + + val modules = reachable.filter { it !in ignoredProjects }.sorted() + + // Group modules by top-level parent for subgraph generation + val grouped = modules.groupBy { path -> + val parts = path.removePrefix(":").split(":") + if (parts.size > 1) ":${parts.first()}" else null + } + + val sb = StringBuilder() + sb.appendLine("```mermaid") + sb.appendLine("---") + sb.appendLine("config:") + sb.appendLine(" layout: elk") + sb.appendLine(" elk:") + sb.appendLine(" nodePlacementStrategy: SIMPLE") + sb.appendLine("---") + sb.appendLine("graph TB") + + // Render subgraphs for grouped modules + for ((group, members) in grouped.toSortedMap(nullsLast(compareBy { it }))) { + if (group != null && members.size > 1) { + sb.appendLine(" subgraph $group") + sb.appendLine(" direction TB") + for (member in members.sorted()) { + val shortName = member.split(":").last() + val pluginType = allPluginTypes[member] + val classDef = pluginType?.displayName ?: "unknown" + sb.appendLine(" $member[$shortName]:::$classDef") + } + sb.appendLine(" end") + } + } + + // Render ungrouped modules (top-level modules) + for ((group, members) in grouped.toSortedMap(nullsLast(compareBy { it }))) { + if (group == null || members.size == 1) { + for (member in members.sorted()) { + val shortName = member.split(":").last() + val pluginType = allPluginTypes[member] + val classDef = pluginType?.displayName ?: "unknown" + sb.appendLine(" $member[$shortName]:::$classDef") + } + } + } + + sb.appendLine() + + // Render edges + for (edge in relevantEdges) { + val labelPart = if (edge.label != null) "|${edge.label}| " else "" + sb.appendLine(" ${edge.from} ${edge.type.mermaidStyle}$labelPart ${edge.to}") + } + + sb.appendLine() + + // Render classDef styles + val usedTypes = modules.mapNotNull { allPluginTypes[it] }.toSet() + for (type in PluginType.entries) { + sb.appendLine("classDef ${type.displayName} fill:${type.color},stroke:#000,stroke-width:2px,color:#000;") + } + sb.appendLine("classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000;") + + sb.appendLine("```") + return sb.toString() +} + +/** + * Generates the legend section for the graph. + */ +fun generateLegend(): String { + val sb = StringBuilder() + sb.appendLine("
Graph legend") + sb.appendLine() + sb.appendLine("```mermaid") + sb.appendLine("graph TB") + + for (type in PluginType.entries) { + sb.appendLine(" ${type.displayName}[${type.displayName}]:::${type.displayName}") + } + + sb.appendLine() + sb.appendLine(" cmp-application -.-> cmp-feature") + sb.appendLine(" kmp-library --> jvm-library") + sb.appendLine() + + for (type in PluginType.entries) { + sb.appendLine("classDef ${type.displayName} fill:${type.color},stroke:#000,stroke-width:2px,color:#000;") + } + sb.appendLine("classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000;") + sb.appendLine("```") + sb.appendLine() + sb.appendLine("
") + return sb.toString() +} + +/** + * Updates a README.md file, replacing content between graph region markers. + */ +fun updateReadmeGraph(readmeFile: File, graphContent: String) { + if (!readmeFile.exists()) return + val content = readmeFile.readText() + val startMarker = "" + val endMarker = "" + + val startIdx = content.indexOf(startMarker) + val endIdx = content.indexOf(endMarker) + + if (startIdx == -1 || endIdx == -1) return + + val newContent = buildString { + append(content.substring(0, startIdx + startMarker.length)) + appendLine() + append(graphContent) + appendLine(generateLegend()) + append(content.substring(endIdx)) + } + + readmeFile.writeText(newContent) +} + +/** + * Registers the `graphDump` and `graphUpdate` tasks on the root project. + */ +fun Project.configureGraphTasks() { + val ignoredProjects = providers.gradleProperty("graph.ignoredProjects") + .orElse("") + .map { it.split(",").map(String::trim).filter(String::isNotEmpty).toSet() } + + tasks.register("graphDump") { + group = "documentation" + description = "Dumps the module dependency graph as Mermaid text" + doLast { + val allEdges = mutableMapOf>() + val allPluginTypes = mutableMapOf() + + subprojects.forEach { sub -> + allEdges[sub.path] = sub.collectEdges() + allPluginTypes[sub.path] = sub.pluginType() + } + + subprojects.forEach { sub -> + val readmeFile = sub.file("README.md") + if (readmeFile.exists() && readmeFile.readText().contains("")) { + val graph = generateMermaidGraph( + rootProjectPath = sub.path, + allEdges = allEdges, + allPluginTypes = allPluginTypes, + ignoredProjects = ignoredProjects.get(), + ) + println("=== ${sub.path} ===") + println(graph) + } + } + } + } + + tasks.register("graphUpdate") { + group = "documentation" + description = "Updates README.md files with module dependency graphs" + doLast { + val allEdges = mutableMapOf>() + val allPluginTypes = mutableMapOf() + + subprojects.forEach { sub -> + allEdges[sub.path] = sub.collectEdges() + allPluginTypes[sub.path] = sub.pluginType() + } + + subprojects.forEach { sub -> + val readmeFile = sub.file("README.md") + if (readmeFile.exists() && readmeFile.readText().contains("")) { + val graph = generateMermaidGraph( + rootProjectPath = sub.path, + allEdges = allEdges, + allPluginTypes = allPluginTypes, + ignoredProjects = ignoredProjects.get(), + ) + updateReadmeGraph(readmeFile, graph) + println("Updated: ${readmeFile.relativeTo(rootDir)}") + } + } + } + } +} diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Jacoco.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Jacoco.kt index ed1ea4254..3d8facd42 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Jacoco.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Jacoco.kt @@ -76,7 +76,6 @@ internal fun Project.configureJacoco( "create${variant.name.capitalize()}CombinedCoverageReport", JacocoReport::class, ) { - classDirectories.setFrom( allJars, allDirectories.map { dirs -> @@ -97,7 +96,7 @@ internal fun Project.configureJacoco( sourceDirectories.setFrom( files( variant.sources.java.toFilePaths(), - variant.sources.kotlin.toFilePaths() + variant.sources.kotlin.toFilePaths(), ), ) @@ -110,7 +109,6 @@ internal fun Project.configureJacoco( ) } - variant.artifacts.forScope(ScopedArtifacts.Scope.PROJECT) .use(reportTask) .toGet( diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt index 0fce68329..89fbccfcd 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt @@ -20,15 +20,13 @@ import com.android.build.api.dsl.CommonExtension import org.gradle.api.JavaVersion import org.gradle.api.Project import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.testing.Test import org.gradle.kotlin.dsl.assign import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.provideDelegate import org.gradle.kotlin.dsl.withType import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompile /** @@ -38,10 +36,10 @@ internal fun Project.configureKotlinAndroid( commonExtension: CommonExtension<*, *, *, *, *, *>, ) { commonExtension.apply { - compileSdk = 35 + compileSdk = 36 defaultConfig { - minSdk = 21 + minSdk = 23 } compileOptions { @@ -53,6 +51,13 @@ internal fun Project.configureKotlinAndroid( } } + // Gradle 9 defaults failOnNoDiscoveredTests to true. KMP modules may declare + // commonTest dependencies without having Android-specific test files, so disable + // the check to avoid false failures. + tasks.withType().configureEach { + failOnNoDiscoveredTests = false + } + configureKotlin() dependencies { @@ -71,6 +76,10 @@ internal fun Project.configureKotlinJvm() { targetCompatibility = JavaVersion.VERSION_11 } + tasks.withType().configureEach { + failOnNoDiscoveredTests = false + } + configureKotlin() } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt index 45dd97f6f..79a7241b3 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt @@ -21,12 +21,8 @@ import org.gradle.api.Project import org.gradle.api.tasks.compile.JavaCompile import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.withType -import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.jetbrains.kotlin.konan.target.HostManager /** * A plugin that applies the Kotlin Multiplatform plugin and configures it for the project. diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaBuildType.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaBuildType.kt index e4f40840d..f4d6f038b 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaBuildType.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaBuildType.kt @@ -1,17 +1,17 @@ /* * Copyright 2022 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ package com.google.samples.apps.nowinandroid diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt index f57e634cc..975c11616 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. + */ + package com.google.samples.apps.nowinandroid import com.android.build.api.dsl.ApplicationExtension @@ -7,7 +23,7 @@ import com.android.build.api.dsl.ProductFlavor @Suppress("EnumEntryName") enum class FlavorDimension { - contentType + contentType, } // The content for the app can either come from local static data which is useful for demo @@ -24,12 +40,12 @@ fun configureFlavors( flavorConfigurationBlock: ProductFlavor.(flavor: NiaFlavor) -> Unit = {}, ) { commonExtension.apply { - FlavorDimension.values().forEach { flavorDimension -> + FlavorDimension.entries.forEach { flavorDimension -> flavorDimensions += flavorDimension.name } productFlavors { - NiaFlavor.values().forEach { niaFlavor -> + NiaFlavor.entries.forEach { niaFlavor -> register(niaFlavor.name) { dimension = niaFlavor.dimension.name flavorConfigurationBlock(this, niaFlavor) diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/PrintTestApks.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/PrintTestApks.kt index 271fc51b7..e2a96eedf 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/PrintTestApks.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/PrintTestApks.kt @@ -1,17 +1,17 @@ /* * Copyright 2022 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ package com.google.samples.apps.nowinandroid @@ -49,7 +49,9 @@ internal fun Project.configurePrintApksTask(extension: AndroidComponentsExtensio javaSources.zip(kotlinSources) { javaDirs, kotlinDirs -> javaDirs + kotlinDirs } - } else javaSources ?: kotlinSources + } else { + javaSources ?: kotlinSources + } if (artifact != null && testSources != null) { tasks.register( @@ -96,8 +98,9 @@ internal abstract class PrintApkLocationTask : DefaultTask() { val builtArtifacts = builtArtifactsLoader.get().load(apkFolder.get()) ?: throw RuntimeException("Cannot load APKs") - if (builtArtifacts.elements.size != 1) + if (builtArtifacts.elements.size != 1) { throw RuntimeException("Expected one APK !") + } val apk = File(builtArtifacts.elements.single().outputFile).toPath() println(apk) } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/ProjectExtensions.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/ProjectExtensions.kt index e45d7f2e1..8bf6e1dfe 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/ProjectExtensions.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/ProjectExtensions.kt @@ -1,17 +1,17 @@ /* * Copyright 2023 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. */ package com.google.samples.apps.nowinandroid diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Spotless.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Spotless.kt new file mode 100644 index 000000000..2d8c70aca --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Spotless.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. + */ + +package com.google.samples.apps.nowinandroid + +import com.diffplug.gradle.spotless.SpotlessExtension +import org.gradle.api.Project +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.configure + +/** + * Configures Spotless for Android/KMP modules (Kotlin + KTS + XML). + * + * Uses `target("src/ **")` instead of `target("** /") + targetExclude("build/")` to work around + * [spotless#2717](https://github.com/diffplug/spotless/issues/2717). + */ +fun Project.configureSpotlessForAndroid() { + configureSpotlessCommon() + val rootDir = isolated.rootProject.projectDirectory + extensions.configure { + format("xml") { + target("src/**/*.xml") + licenseHeaderFile(rootDir.file("spotless/copyright.xml").asFile, "(<[^!?])") + } + } +} + +/** + * Configures Spotless for JVM-only modules (Kotlin + KTS, no XML). + */ +fun Project.configureSpotlessForJvm() { + configureSpotlessCommon() +} + +private fun Project.configureSpotlessCommon() { + apply(plugin = "com.diffplug.spotless") + val rootDir = isolated.rootProject.projectDirectory + extensions.configure { + kotlin { + target("src/**/*.kt") + ktlint(libs.findVersion("ktlint").get().toString()) + .editorConfigOverride(mapOf("android" to "true")) + licenseHeaderFile(rootDir.file("spotless/copyright.kt").asFile) + } + format("kts") { + target("*.kts") + targetExclude("**/build/**/*.kts") + licenseHeaderFile(rootDir.file("spotless/copyright.kts").asFile, "(^(?![\\/ ]\\*).*$)") + } + } +} diff --git a/build.gradle.kts b/build.gradle.kts index 83c079a8e..7fd51b1f7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -54,9 +54,25 @@ plugins { alias(libs.plugins.roborazzi) apply false alias(libs.plugins.secrets) apply false alias(libs.plugins.module.graph) apply true // Plugin applied to allow module graph generation + alias(libs.plugins.nowinandroid.root) + alias(libs.plugins.spotless) alias(libs.plugins.jetbrains.compose) apply false alias(libs.plugins.kotlin.multiplatform) apply false alias(libs.plugins.sqldelight.gradle.plugin) apply false alias(libs.plugins.ktrofit) apply false alias(libs.plugins.buildkonfig) apply false } + +spotless { + kotlin { + target("build-logic/convention/src/**/*.kt") + ktlint(libs.versions.ktlint.get()) + .editorConfigOverride(mapOf("android" to "true")) + licenseHeaderFile(rootProject.file("spotless/copyright.kt")) + } + format("kts") { + target("*.kts", "build-logic/**/*.kts") + targetExclude("**/build/**/*.kts") + licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)") + } +} diff --git a/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt index 9af93d54c..6f9d401ad 100644 --- a/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt +++ b/core/data/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt @@ -23,8 +23,6 @@ import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkRequest import android.net.NetworkRequest.Builder -import android.os.Build.VERSION -import android.os.Build.VERSION_CODES import androidx.core.content.getSystemService import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.channels.awaitClose @@ -79,13 +77,8 @@ internal class ConnectivityManagerNetworkMonitor( .flowOn(ioDispatcher) .conflate() - @Suppress("DEPRECATION") - private fun ConnectivityManager.isCurrentlyConnected() = when { - VERSION.SDK_INT >= VERSION_CODES.M -> - activeNetwork - ?.let(::getNetworkCapabilities) - ?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - - else -> activeNetworkInfo?.isConnected - } ?: false + private fun ConnectivityManager.isCurrentlyConnected(): Boolean { + val networkCapabilities = getNetworkCapabilities(activeNetwork) ?: return false + return networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + } } diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index 74cb07430..92df27483 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -27,6 +27,10 @@ plugins { id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin") } +ktorfit { + compilerPluginVersion.set("2.3.3") +} + android { namespace = "com.google.samples.apps.nowinandroid.core.network" testOptions { diff --git a/gradle/init.gradle.kts b/gradle/init.gradle.kts deleted file mode 100644 index cb3d34e48..000000000 --- a/gradle/init.gradle.kts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. - */ - -val ktlintVersion = "1.5.0" - -initscript { - val spotlessVersion = "7.0.2" - - repositories { - mavenCentral() - } - - dependencies { - classpath("com.diffplug.spotless:spotless-plugin-gradle:$spotlessVersion") - } -} - -rootProject { - subprojects { - apply() - extensions.configure { - kotlin { - target("**/*.kt") - targetExclude("**/build/**/*.kt") - ktlint(ktlintVersion).editorConfigOverride( - mapOf( - "android" to "true", - ), - ) - licenseHeaderFile(rootProject.file("spotless/copyright.kt")) - } - format("kts") { - target("**/*.kts") - targetExclude("**/build/**/*.kts") - // Look for the first line that doesn't have a block comment (assumed to be the license) - licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)") - } - format("xml") { - target("**/*.xml") - targetExclude("**/build/**/*.xml") - // Look for the first XML tag that isn't a comment (