Merge branch 'android:main' into main

pull/2128/head
AmirMohammad Yazdanmanesh 7 months ago committed by GitHub
commit d4473ed621
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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** **Ensure tests pass and code is formatted correctly**
- Run local tests on the `DemoDebug` variant by running `./gradlew testDemoDebug` - 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** **Add a description**

@ -48,7 +48,7 @@ jobs:
run: ./gradlew :build-logic:convention:check run: ./gradlew :build-logic:convention:check
- name: Check spotless - name: Check spotless
run: ./gradlew spotlessCheck --init-script gradle/init.gradle.kts --no-configuration-cache run: ./gradlew spotlessCheck
- name: Check Dependency Guard - name: Check Dependency Guard
id: dependencyguard_verify id: dependencyguard_verify

@ -4,7 +4,6 @@
<option name="executionName" /> <option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" /> <option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="--init-script=gradle/init.gradle.kts" />
<option name="taskDescriptions"> <option name="taskDescriptions">
<list /> <list />
</option> </option>

@ -26,7 +26,7 @@ The main Android app lives in the `app/` folder. Feature modules live in `featur
The app and Android libraries have two product flavors: `demo` and `prod`, and two build types: `debug` and `release`. The app and Android libraries have two product flavors: `demo` and `prod`, and two build types: `debug` and `release`.
- Build: `./gradlew assemble{Variant}`. Typically `assembleDemoDebug`. - Build: `./gradlew assemble{Variant}`. Typically `assembleDemoDebug`.
- Fix linting/formatting: `./gradlew --init-script gradle/init.gradle.kts spotlessApply` - Fix linting/formatting: `./gradlew spotlessApply`
- Run local tests: `./gradlew {variant}Test` - Run local tests: `./gradlew {variant}Test`
- Run single test: `./gradlew {variant}Test --tests "com.example.myapp.MyTestClass"` - Run single test: `./gradlew {variant}Test --tests "com.example.myapp.MyTestClass"`
- Run local screenshot tests: `./gradlew verifyRoborazziDemoDebug` - Run local screenshot tests: `./gradlew verifyRoborazziDemoDebug`

@ -112,10 +112,10 @@ androidx.window:window-core:1.4.0
androidx.window:window:1.4.0 androidx.window:window:1.4.0
com.google.accompanist:accompanist-drawablepainter:0.32.0 com.google.accompanist:accompanist-drawablepainter:0.32.0
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger-lint-aar:2.57.2 com.google.dagger:dagger-lint-aar:2.59
com.google.dagger:dagger:2.57.2 com.google.dagger:dagger:2.59
com.google.dagger:hilt-android:2.57.2 com.google.dagger:hilt-android:2.59
com.google.dagger:hilt-core:2.57.2 com.google.dagger:hilt-core:2.59
com.google.guava:listenablefuture:1.0 com.google.guava:listenablefuture:1.0
com.squareup.okhttp3:okhttp:4.12.0 com.squareup.okhttp3:okhttp:4.12.0
com.squareup.okio:okio-jvm:3.9.0 com.squareup.okio:okio-jvm:3.9.0
@ -126,10 +126,10 @@ io.coil-kt:coil-compose:2.7.0
io.coil-kt:coil:2.7.0 io.coil-kt:coil:2.7.0
jakarta.inject:jakarta.inject-api:2.0.1 jakarta.inject:jakarta.inject-api:2.0.1
javax.inject:javax.inject:1 javax.inject:javax.inject:1
org.jetbrains.kotlin:kotlin-stdlib-common:2.2.21 org.jetbrains.kotlin:kotlin-stdlib-common:2.3.0
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0
org.jetbrains.kotlin:kotlin-stdlib:2.2.21 org.jetbrains.kotlin:kotlin-stdlib:2.3.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0

@ -36,4 +36,4 @@
</activity> </activity>
</application> </application>
</manifest> </manifest>

@ -18,4 +18,4 @@
<background android:drawable="@drawable/ic_launcher_background"/> <background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" /> <monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>

@ -18,4 +18,4 @@
<background android:drawable="@drawable/ic_launcher_background"/> <background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" /> <monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>

@ -16,4 +16,4 @@
--> -->
<resources> <resources>
<string name="app_name">NiA Catalog</string> <string name="app_name">NiA Catalog</string>
</resources> </resources>

@ -16,4 +16,4 @@
--> -->
<resources> <resources>
<style name="Theme.NiaCatalog" parent="android:Theme.Material.Light.NoActionBar" /> <style name="Theme.NiaCatalog" parent="android:Theme.Material.Light.NoActionBar" />
</resources> </resources>

@ -54,7 +54,7 @@ graph TB
:core:data[data]:::android-library :core:data[data]:::android-library
:core:database[database]:::android-library :core:database[database]:::android-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:designsystem[designsystem]:::android-library :core:designsystem[designsystem]:::android-library
:core:domain[domain]:::android-library :core:domain[domain]:::android-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library

@ -189,10 +189,10 @@ com.google.android.gms:play-services-oss-licenses:17.1.0
com.google.android.gms:play-services-stats:17.0.2 com.google.android.gms:play-services-stats:17.0.2
com.google.android.gms:play-services-tasks:18.2.0 com.google.android.gms:play-services-tasks:18.2.0
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger-lint-aar:2.57.2 com.google.dagger:dagger-lint-aar:2.59
com.google.dagger:dagger:2.57.2 com.google.dagger:dagger:2.59
com.google.dagger:hilt-android:2.57.2 com.google.dagger:hilt-android:2.59
com.google.dagger:hilt-core:2.57.2 com.google.dagger:hilt-core:2.59
com.google.errorprone:error_prone_annotations:2.26.0 com.google.errorprone:error_prone_annotations:2.26.0
com.google.firebase:firebase-abt:21.1.1 com.google.firebase:firebase-abt:21.1.1
com.google.firebase:firebase-analytics:22.1.2 com.google.firebase:firebase-analytics:22.1.2
@ -235,10 +235,10 @@ io.coil-kt:coil:2.7.0
jakarta.inject:jakarta.inject-api:2.0.1 jakarta.inject:jakarta.inject-api:2.0.1
javax.inject:javax.inject:1 javax.inject:javax.inject:1
org.checkerframework:checker-qual:3.12.0 org.checkerframework:checker-qual:3.12.0
org.jetbrains.kotlin:kotlin-stdlib-common:2.2.21 org.jetbrains.kotlin:kotlin-stdlib-common:2.3.0
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0
org.jetbrains.kotlin:kotlin-stdlib:2.2.21 org.jetbrains.kotlin:kotlin-stdlib:2.3.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.1
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.1
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.1

@ -18,7 +18,7 @@ package com.google.samples.apps.nowinandroid.util
import android.util.Log import android.util.Log
import androidx.profileinstaller.ProfileVerifier import androidx.profileinstaller.ProfileVerifier
import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope import com.google.samples.apps.nowinandroid.core.common.network.di.ApplicationScope
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.guava.await import kotlinx.coroutines.guava.await
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

@ -18,4 +18,4 @@
<background android:drawable="@drawable/ic_launcher_background"/> <background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" /> <monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>

@ -18,4 +18,4 @@
<background android:drawable="@drawable/ic_launcher_background"/> <background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" /> <monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>

@ -23,4 +23,4 @@
android:name="firebase_analytics_collection_deactivated" android:name="firebase_analytics_collection_deactivated"
android:value="false" /> android:value="false" />
</application> </application>
</manifest> </manifest>

@ -54,7 +54,7 @@ graph TB
:core:data[data]:::android-library :core:data[data]:::android-library
:core:database[database]:::android-library :core:database[database]:::android-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:designsystem[designsystem]:::android-library :core:designsystem[designsystem]:::android-library
:core:domain[domain]:::android-library :core:domain[domain]:::android-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library

@ -37,7 +37,7 @@ kotlin {
} }
dependencies { dependencies {
compileOnly(libs.android.gradleApiPlugin) compileOnly(libs.android.gradlePlugin)
compileOnly(libs.android.tools.common) compileOnly(libs.android.tools.common)
compileOnly(libs.compose.gradlePlugin) compileOnly(libs.compose.gradlePlugin)
compileOnly(libs.firebase.crashlytics.gradlePlugin) compileOnly(libs.firebase.crashlytics.gradlePlugin)
@ -45,6 +45,7 @@ dependencies {
compileOnly(libs.kotlin.gradlePlugin) compileOnly(libs.kotlin.gradlePlugin)
compileOnly(libs.ksp.gradlePlugin) compileOnly(libs.ksp.gradlePlugin)
compileOnly(libs.room.gradlePlugin) compileOnly(libs.room.gradlePlugin)
compileOnly(libs.spotless.gradlePlugin)
implementation(libs.truth) implementation(libs.truth)
lintChecks(libs.androidx.lint.gradle) lintChecks(libs.androidx.lint.gradle)
} }

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.api.dsl.ApplicationExtension import com.android.build.api.dsl.ApplicationExtension

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.api.dsl.ApplicationExtension 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.configureGradleManagedDevices
import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configureKotlinAndroid
import com.google.samples.apps.nowinandroid.configurePrintApksTask import com.google.samples.apps.nowinandroid.configurePrintApksTask
import com.google.samples.apps.nowinandroid.configureSpotlessForAndroid
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.apply
@ -29,14 +30,12 @@ class AndroidApplicationConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
apply(plugin = "com.android.application") apply(plugin = "com.android.application")
apply(plugin = "org.jetbrains.kotlin.android")
apply(plugin = "nowinandroid.android.lint") apply(plugin = "nowinandroid.android.lint")
apply(plugin = "com.dropbox.dependency-guard") apply(plugin = "com.dropbox.dependency-guard")
extensions.configure<ApplicationExtension> { extensions.configure<ApplicationExtension> {
configureKotlinAndroid(this) configureKotlinAndroid(this)
defaultConfig.targetSdk = 36 defaultConfig.targetSdk = 36
@Suppress("UnstableApiUsage")
testOptions.animationsDisabled = true testOptions.animationsDisabled = true
configureGradleManagedDevices(this) configureGradleManagedDevices(this)
} }
@ -44,6 +43,7 @@ class AndroidApplicationConventionPlugin : Plugin<Project> {
configurePrintApksTask(this) configurePrintApksTask(this)
configureBadgingTasks(this) configureBadgingTasks(this)
} }
configureSpotlessForAndroid()
} }
} }
} }

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.api.dsl.ApplicationExtension import com.android.build.api.dsl.ApplicationExtension
@ -41,7 +41,7 @@ class AndroidApplicationFirebaseConventionPlugin : Plugin<Project> {
datastore-proto brings in protobuf dependencies. These are the source of truth datastore-proto brings in protobuf dependencies. These are the source of truth
for Now in Android. for Now in Android.
That's why the duplicate classes from below dependencies are excluded. That's why the duplicate classes from below dependencies are excluded.
*/ */
exclude(group = "com.google.protobuf", module = "protobuf-javalite") exclude(group = "com.google.protobuf", module = "protobuf-javalite")
exclude(group = "com.google.firebase", module = "protolite-well-known-types") exclude(group = "com.google.firebase", module = "protolite-well-known-types")
} }

@ -1,17 +1,17 @@
/* /*
* Copyright 2023 The Android Open Source Project * Copyright 2023 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.api.dsl.ApplicationExtension import com.android.build.api.dsl.ApplicationExtension
@ -28,4 +28,4 @@ class AndroidApplicationFlavorsConventionPlugin : Plugin<Project> {
} }
} }
} }
} }

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.api.dsl.ApplicationExtension import com.android.build.api.dsl.ApplicationExtension
@ -21,20 +21,16 @@ import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.getByType
import org.gradle.testing.jacoco.plugins.JacocoPlugin
class AndroidApplicationJacocoConventionPlugin : Plugin<Project> { class AndroidApplicationJacocoConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
apply(plugin = "jacoco") apply<JacocoPlugin>()
configureJacoco(
val androidExtension = extensions.getByType<ApplicationExtension>() commonExtension = extensions.getByType<ApplicationExtension>(),
androidComponentsExtension = extensions.getByType<ApplicationAndroidComponentsExtension>(),
androidExtension.buildTypes.configureEach { )
enableAndroidTestCoverage = true
enableUnitTestCoverage = true
}
configureJacoco(extensions.getByType<ApplicationAndroidComponentsExtension>())
} }
} }
} }

@ -1,20 +1,19 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.google.samples.apps.nowinandroid.libs
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.apply

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.api.dsl.LibraryExtension import com.android.build.api.dsl.LibraryExtension

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.api.dsl.LibraryExtension import com.android.build.api.dsl.LibraryExtension

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.api.dsl.LibraryExtension import com.android.build.api.dsl.LibraryExtension
@ -20,6 +20,7 @@ import com.google.samples.apps.nowinandroid.configureFlavors
import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.google.samples.apps.nowinandroid.configureGradleManagedDevices
import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configureKotlinAndroid
import com.google.samples.apps.nowinandroid.configurePrintApksTask import com.google.samples.apps.nowinandroid.configurePrintApksTask
import com.google.samples.apps.nowinandroid.configureSpotlessForAndroid
import com.google.samples.apps.nowinandroid.disableUnnecessaryAndroidTests import com.google.samples.apps.nowinandroid.disableUnnecessaryAndroidTests
import com.google.samples.apps.nowinandroid.libs import com.google.samples.apps.nowinandroid.libs
import org.gradle.api.Plugin import org.gradle.api.Plugin
@ -32,14 +33,12 @@ class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
apply(plugin = "com.android.library") apply(plugin = "com.android.library")
apply(plugin = "org.jetbrains.kotlin.android")
apply(plugin = "nowinandroid.android.lint") apply(plugin = "nowinandroid.android.lint")
extensions.configure<LibraryExtension> { extensions.configure<LibraryExtension> {
configureKotlinAndroid(this) configureKotlinAndroid(this)
testOptions.targetSdk = 36 testOptions.targetSdk = 36
lint.targetSdk = 36 lint.targetSdk = 36
defaultConfig.targetSdk = 36
defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testOptions.animationsDisabled = true testOptions.animationsDisabled = true
configureFlavors(this) configureFlavors(this)
@ -54,9 +53,11 @@ class AndroidLibraryConventionPlugin : Plugin<Project> {
configurePrintApksTask(this) configurePrintApksTask(this)
disableUnnecessaryAndroidTests(target) disableUnnecessaryAndroidTests(target)
} }
configureSpotlessForAndroid()
dependencies { dependencies {
"androidTestImplementation"(libs.findLibrary("kotlin.test").get()) "androidTestImplementation"(libs.findLibrary("kotlin.test").get())
"testImplementation"(libs.findLibrary("kotlin.test").get()) "testImplementation"(libs.findLibrary("kotlin.test").get())
"testImplementation"(libs.findLibrary("junit").get())
"implementation"(libs.findLibrary("androidx.tracing.ktx").get()) "implementation"(libs.findLibrary("androidx.tracing.ktx").get())
} }

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.api.dsl.LibraryExtension import com.android.build.api.dsl.LibraryExtension
@ -21,20 +21,16 @@ import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.getByType
import org.gradle.testing.jacoco.plugins.JacocoPlugin
class AndroidLibraryJacocoConventionPlugin : Plugin<Project> { class AndroidLibraryJacocoConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
apply(plugin = "jacoco") apply<JacocoPlugin>()
configureJacoco(
val androidExtension = extensions.getByType<LibraryExtension>() commonExtension = extensions.getByType<LibraryExtension>(),
androidComponentsExtension = extensions.getByType<LibraryAndroidComponentsExtension>(),
androidExtension.buildTypes.configureEach { )
enableAndroidTestCoverage = true
enableUnitTestCoverage = true
}
configureJacoco(extensions.getByType<LibraryAndroidComponentsExtension>())
} }
} }
} }

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.api.dsl.ApplicationExtension import com.android.build.api.dsl.ApplicationExtension

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import androidx.room.gradle.RoomExtension import androidx.room.gradle.RoomExtension

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.api.dsl.TestExtension import com.android.build.api.dsl.TestExtension
@ -26,7 +26,6 @@ class AndroidTestConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
apply(plugin = "com.android.test") apply(plugin = "com.android.test")
apply(plugin = "org.jetbrains.kotlin.android")
extensions.configure<TestExtension> { extensions.configure<TestExtension> {
configureKotlinAndroid(this) configureKotlinAndroid(this)

@ -1,17 +1,17 @@
/* /*
* Copyright 2023 The Android Open Source Project * Copyright 2023 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.android.build.gradle.api.AndroidBasePlugin import com.android.build.gradle.api.AndroidBasePlugin
@ -28,6 +28,7 @@ class HiltConventionPlugin : Plugin<Project> {
dependencies { dependencies {
"ksp"(libs.findLibrary("hilt.compiler").get()) "ksp"(libs.findLibrary("hilt.compiler").get())
"ksp"(libs.findLibrary("kotlin.metadata").get())
} }
// Add support for Jvm Module, base on org.jetbrains.kotlin.jvm // Add support for Jvm Module, base on org.jetbrains.kotlin.jvm

@ -1,20 +1,21 @@
/* /*
* Copyright 2023 The Android Open Source Project * Copyright 2023 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import com.google.samples.apps.nowinandroid.configureKotlinJvm import com.google.samples.apps.nowinandroid.configureKotlinJvm
import com.google.samples.apps.nowinandroid.configureSpotlessForJvm
import com.google.samples.apps.nowinandroid.libs import com.google.samples.apps.nowinandroid.libs
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
@ -28,6 +29,7 @@ class JvmLibraryConventionPlugin : Plugin<Project> {
apply(plugin = "nowinandroid.android.lint") apply(plugin = "nowinandroid.android.lint")
configureKotlinJvm() configureKotlinJvm()
configureSpotlessForJvm()
dependencies { dependencies {
"testImplementation"(libs.findLibrary("kotlin.test").get()) "testImplementation"(libs.findLibrary("kotlin.test").get())
} }

@ -15,6 +15,7 @@
*/ */
import com.google.samples.apps.nowinandroid.configureGraphTasks import com.google.samples.apps.nowinandroid.configureGraphTasks
import com.google.samples.apps.nowinandroid.configureSpotlessForRootProject
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
@ -22,5 +23,6 @@ class RootPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
require(target.path == ":") require(target.path == ":")
target.subprojects { configureGraphTasks() } target.subprojects { configureGraphTasks() }
target.configureSpotlessForRootProject()
} }
} }

@ -27,10 +27,10 @@ import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginE
* Configure Compose-specific options * Configure Compose-specific options
*/ */
internal fun Project.configureAndroidCompose( internal fun Project.configureAndroidCompose(
commonExtension: CommonExtension<*, *, *, *, *, *>, commonExtension: CommonExtension,
) { ) {
commonExtension.apply { commonExtension.apply {
buildFeatures { buildFeatures.apply {
compose = true compose = true
} }
@ -46,6 +46,7 @@ internal fun Project.configureAndroidCompose(
extensions.configure<ComposeCompilerGradlePluginExtension> { extensions.configure<ComposeCompilerGradlePluginExtension> {
fun Provider<String>.onlyIfTrue() = flatMap { provider { it.takeIf(String::toBoolean) } } fun Provider<String>.onlyIfTrue() = flatMap { provider { it.takeIf(String::toBoolean) } }
fun Provider<*>.relativeToRootProject(dir: String) = map { fun Provider<*>.relativeToRootProject(dir: String) = map {
@Suppress("UnstableApiUsage")
isolated.rootProject.projectDirectory isolated.rootProject.projectDirectory
.dir("build") .dir("build")
.dir(projectDir.toRelativeString(rootDir)) .dir(projectDir.toRelativeString(rootDir))
@ -59,6 +60,7 @@ internal fun Project.configureAndroidCompose(
.relativeToRootProject("compose-reports") .relativeToRootProject("compose-reports")
.let(reportsDestination::set) .let(reportsDestination::set)
@Suppress("UnstableApiUsage")
stabilityConfigurationFiles stabilityConfigurationFiles
.add(isolated.rootProject.projectDirectory.file("compose_compiler_config.conf")) .add(isolated.rootProject.projectDirectory.file("compose_compiler_config.conf"))
} }

@ -1,17 +1,17 @@
/* /*
* Copyright 2023 The Android Open Source Project * Copyright 2023 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid package com.google.samples.apps.nowinandroid
@ -30,6 +30,6 @@ import org.gradle.api.Project
internal fun LibraryAndroidComponentsExtension.disableUnnecessaryAndroidTests( internal fun LibraryAndroidComponentsExtension.disableUnnecessaryAndroidTests(
project: Project, project: Project,
) = beforeVariants { ) = beforeVariants {
it.androidTest.enable = it.androidTest.enable it.androidTest.enable = it.androidTest.enable &&
&& project.projectDir.resolve("src/androidTest").exists() project.projectDir.resolve("src/androidTest").exists()
} }

@ -1,17 +1,17 @@
/* /*
* Copyright 2023 The Android Open Source Project * Copyright 2023 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid package com.google.samples.apps.nowinandroid
@ -123,7 +123,6 @@ fun Project.configureBadgingTasks(
badging = project.layout.buildDirectory.file( badging = project.layout.buildDirectory.file(
"outputs/apk_from_bundle/${variant.name}/${variant.name}-badging.txt", "outputs/apk_from_bundle/${variant.name}/${variant.name}-badging.txt",
) )
} }
val updateBadgingTaskName = "update${capitalizedVariantName}Badging" val updateBadgingTaskName = "update${capitalizedVariantName}Badging"
@ -141,7 +140,6 @@ fun Project.configureBadgingTasks(
this.updateBadgingTaskName = updateBadgingTaskName this.updateBadgingTaskName = updateBadgingTaskName
output = project.layout.buildDirectory.dir("intermediates/$checkBadgingTaskName") output = project.layout.buildDirectory.dir("intermediates/$checkBadgingTaskName")
} }
} }
} }

@ -1,17 +1,17 @@
/* /*
* Copyright 2023 The Android Open Source Project * Copyright 2023 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid package com.google.samples.apps.nowinandroid
@ -25,7 +25,7 @@ import org.gradle.kotlin.dsl.invoke
* Configure project for Gradle managed devices * Configure project for Gradle managed devices
*/ */
internal fun configureGradleManagedDevices( internal fun configureGradleManagedDevices(
commonExtension: CommonExtension<*, *, *, *, *, *>, commonExtension: CommonExtension,
) { ) {
val pixel4 = DeviceConfig("Pixel 4", 30, "aosp-atd") val pixel4 = DeviceConfig("Pixel 4", 30, "aosp-atd")
val pixel6 = DeviceConfig("Pixel 6", 31, "aosp") val pixel6 = DeviceConfig("Pixel 6", 31, "aosp")
@ -34,9 +34,10 @@ internal fun configureGradleManagedDevices(
val allDevices = listOf(pixel4, pixel6, pixelC) val allDevices = listOf(pixel4, pixel6, pixelC)
val ciDevices = listOf(pixel4, pixelC) val ciDevices = listOf(pixel4, pixelC)
commonExtension.testOptions { commonExtension.testOptions.apply {
@Suppress("UnstableApiUsage")
managedDevices { managedDevices {
devices { allDevices {
allDevices.forEach { deviceConfig -> allDevices.forEach { deviceConfig ->
maybeCreate(deviceConfig.taskName, ManagedVirtualDevice::class.java).apply { maybeCreate(deviceConfig.taskName, ManagedVirtualDevice::class.java).apply {
device = deviceConfig.device device = deviceConfig.device
@ -48,7 +49,7 @@ internal fun configureGradleManagedDevices(
groups { groups {
maybeCreate("ci").apply { maybeCreate("ci").apply {
ciDevices.forEach { deviceConfig -> ciDevices.forEach { deviceConfig ->
targetDevices.add(devices[deviceConfig.taskName]) targetDevices.add(localDevices[deviceConfig.taskName])
} }
} }
} }

@ -17,6 +17,7 @@
package com.google.samples.apps.nowinandroid package com.google.samples.apps.nowinandroid
import com.android.build.api.artifact.ScopedArtifact import com.android.build.api.artifact.ScopedArtifact
import com.android.build.api.dsl.CommonExtension
import com.android.build.api.variant.AndroidComponentsExtension import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.variant.ScopedArtifacts import com.android.build.api.variant.ScopedArtifacts
import com.android.build.api.variant.SourceDirectories import com.android.build.api.variant.SourceDirectories
@ -59,8 +60,15 @@ private fun String.capitalize() = replaceFirstChar {
* tests on CI using a different Github Action or an external device farm. * tests on CI using a different Github Action or an external device farm.
*/ */
internal fun Project.configureJacoco( internal fun Project.configureJacoco(
commonExtension: CommonExtension,
androidComponentsExtension: AndroidComponentsExtension<*, *, *>, androidComponentsExtension: AndroidComponentsExtension<*, *, *>,
) { ) {
// Configure only the debug build, otherwise it will force the debuggable flag on release buildTypes as well
commonExtension.buildTypes.named("debug") {
enableAndroidTestCoverage = true
enableUnitTestCoverage = true
}
configure<JacocoPluginExtension> { configure<JacocoPluginExtension> {
toolVersion = libs.findVersion("jacoco").get().toString() toolVersion = libs.findVersion("jacoco").get().toString()
} }
@ -76,7 +84,6 @@ internal fun Project.configureJacoco(
"create${variant.name.capitalize()}CombinedCoverageReport", "create${variant.name.capitalize()}CombinedCoverageReport",
JacocoReport::class, JacocoReport::class,
) { ) {
classDirectories.setFrom( classDirectories.setFrom(
allJars, allJars,
allDirectories.map { dirs -> allDirectories.map { dirs ->
@ -97,7 +104,7 @@ internal fun Project.configureJacoco(
sourceDirectories.setFrom( sourceDirectories.setFrom(
files( files(
variant.sources.java.toFilePaths(), variant.sources.java.toFilePaths(),
variant.sources.kotlin.toFilePaths() variant.sources.kotlin.toFilePaths(),
), ),
) )
@ -110,7 +117,6 @@ internal fun Project.configureJacoco(
) )
} }
variant.artifacts.forScope(ScopedArtifacts.Scope.PROJECT) variant.artifacts.forScope(ScopedArtifacts.Scope.PROJECT)
.use(reportTask) .use(reportTask)
.toGet( .toGet(

@ -27,22 +27,21 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
/** /**
* Configure base Kotlin with Android options * Configure base Kotlin with Android options
*/ */
internal fun Project.configureKotlinAndroid( internal fun Project.configureKotlinAndroid(
commonExtension: CommonExtension<*, *, *, *, *, *>, commonExtension: CommonExtension,
) { ) {
commonExtension.apply { commonExtension.apply {
compileSdk = 36 compileSdk = 36
defaultConfig { defaultConfig.apply {
minSdk = 23 minSdk = 23
} }
compileOptions { compileOptions.apply {
// Up to Java 11 APIs are available through desugaring // Up to Java 11 APIs are available through desugaring
// https://developer.android.com/studio/write/java11-minimal-support-table // https://developer.android.com/studio/write/java11-minimal-support-table
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
@ -86,9 +85,6 @@ private inline fun <reified T : KotlinBaseExtension> Project.configureKotlin() =
is KotlinJvmProjectExtension -> compilerOptions is KotlinJvmProjectExtension -> compilerOptions
else -> TODO("Unsupported project extension $this ${T::class}") else -> TODO("Unsupported project extension $this ${T::class}")
}.apply { }.apply {
// TODO: move remove languageVersion and coreLibrariesVersion after upgrading to AGP 9.0
languageVersion.set(KotlinVersion.KOTLIN_2_2)
coreLibrariesVersion = "2.2.21"
jvmTarget = JvmTarget.JVM_11 jvmTarget = JvmTarget.JVM_11
allWarningsAsErrors = warningsAsErrors allWarningsAsErrors = warningsAsErrors
freeCompilerArgs.add( freeCompilerArgs.add(
@ -107,7 +103,7 @@ private inline fun <reified T : KotlinBaseExtension> Project.configureKotlin() =
* The binary signature changes. The error on the declaration is no longer reported. * The binary signature changes. The error on the declaration is no longer reported.
* '-Xconsistent-data-class-copy-visibility' compiler flag and ConsistentCopyVisibility annotation are now unnecessary. * '-Xconsistent-data-class-copy-visibility' compiler flag and ConsistentCopyVisibility annotation are now unnecessary.
*/ */
"-Xconsistent-data-class-copy-visibility" "-Xconsistent-data-class-copy-visibility",
) )
} }
} }

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid package com.google.samples.apps.nowinandroid

@ -1,13 +1,30 @@
/*
* 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 package com.google.samples.apps.nowinandroid
import com.android.build.api.dsl.ApplicationExtension import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.ApplicationProductFlavor import com.android.build.api.dsl.ApplicationProductFlavor
import com.android.build.api.dsl.CommonExtension import com.android.build.api.dsl.CommonExtension
import com.android.build.api.dsl.ProductFlavor import com.android.build.api.dsl.ProductFlavor
import org.gradle.kotlin.dsl.invoke
@Suppress("EnumEntryName") @Suppress("EnumEntryName")
enum class FlavorDimension { enum class FlavorDimension {
contentType contentType,
} }
// The content for the app can either come from local static data which is useful for demo // The content for the app can either come from local static data which is useful for demo
@ -20,7 +37,7 @@ enum class NiaFlavor(val dimension: FlavorDimension, val applicationIdSuffix: St
} }
fun configureFlavors( fun configureFlavors(
commonExtension: CommonExtension<*, *, *, *, *, *>, commonExtension: CommonExtension,
flavorConfigurationBlock: ProductFlavor.(flavor: NiaFlavor) -> Unit = {}, flavorConfigurationBlock: ProductFlavor.(flavor: NiaFlavor) -> Unit = {},
) { ) {
commonExtension.apply { commonExtension.apply {
@ -33,7 +50,7 @@ fun configureFlavors(
register(niaFlavor.name) { register(niaFlavor.name) {
dimension = niaFlavor.dimension.name dimension = niaFlavor.dimension.name
flavorConfigurationBlock(this, niaFlavor) flavorConfigurationBlock(this, niaFlavor)
if (this@apply is ApplicationExtension && this is ApplicationProductFlavor) { if (commonExtension is ApplicationExtension && this is ApplicationProductFlavor) {
if (niaFlavor.applicationIdSuffix != null) { if (niaFlavor.applicationIdSuffix != null) {
applicationIdSuffix = niaFlavor.applicationIdSuffix applicationIdSuffix = niaFlavor.applicationIdSuffix
} }

@ -1,17 +1,17 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid package com.google.samples.apps.nowinandroid
@ -49,7 +49,9 @@ internal fun Project.configurePrintApksTask(extension: AndroidComponentsExtensio
javaSources.zip(kotlinSources) { javaDirs, kotlinDirs -> javaSources.zip(kotlinSources) { javaDirs, kotlinDirs ->
javaDirs + kotlinDirs javaDirs + kotlinDirs
} }
} else javaSources ?: kotlinSources } else {
javaSources ?: kotlinSources
}
if (artifact != null && testSources != null) { if (artifact != null && testSources != null) {
tasks.register( tasks.register(
@ -96,8 +98,9 @@ internal abstract class PrintApkLocationTask : DefaultTask() {
val builtArtifacts = builtArtifactsLoader.get().load(apkFolder.get()) val builtArtifacts = builtArtifactsLoader.get().load(apkFolder.get())
?: throw RuntimeException("Cannot load APKs") ?: throw RuntimeException("Cannot load APKs")
if (builtArtifacts.elements.size != 1) if (builtArtifacts.elements.size != 1) {
throw RuntimeException("Expected one APK !") throw RuntimeException("Expected one APK !")
}
val apk = File(builtArtifacts.elements.single().outputFile).toPath() val apk = File(builtArtifacts.elements.single().outputFile).toPath()
println(apk) println(apk)
} }

@ -1,17 +1,17 @@
/* /*
* Copyright 2023 The Android Open Source Project * Copyright 2023 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid package com.google.samples.apps.nowinandroid

@ -0,0 +1,80 @@
/*
* 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.diffplug.gradle.spotless.SpotlessExtension
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure
internal fun Project.configureSpotlessForAndroid() {
configureSpotlessCommon()
extensions.configure<SpotlessExtension> {
format("xml") {
target("src/**/*.xml")
// Look for the first XML tag that isn't a comment (<!--) or the xml declaration (<?xml)
licenseHeaderFile(rootDir.resolve("spotless/copyright.xml"), "(<[^!?])")
endWithNewline()
}
}
}
internal fun Project.configureSpotlessForJvm() {
configureSpotlessCommon()
}
internal fun Project.configureSpotlessForRootProject() {
apply(plugin = "com.diffplug.spotless")
extensions.configure<SpotlessExtension> {
kotlin {
target("build-logic/convention/src/**/*.kt")
ktlint(libs.findVersion("ktlint").get().requiredVersion).editorConfigOverride(
mapOf("android" to "true"),
)
licenseHeaderFile(rootDir.resolve("spotless/copyright.kt"))
endWithNewline()
}
format("kts") {
target("*.kts")
target("build-logic/*.kts")
target("build-logic/convention/*.kts")
// Look for the first line that doesn't have a block comment (assumed to be the license)
licenseHeaderFile(rootDir.resolve("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)")
endWithNewline()
}
}
}
private fun Project.configureSpotlessCommon() {
apply(plugin = "com.diffplug.spotless")
extensions.configure<SpotlessExtension> {
kotlin {
target("src/**/*.kt")
ktlint(libs.findVersion("ktlint").get().requiredVersion).editorConfigOverride(
mapOf("android" to "true"),
)
licenseHeaderFile(rootDir.resolve("spotless/copyright.kt"))
endWithNewline()
}
format("kts") {
target("*.kts")
// Look for the first line that doesn't have a block comment (assumed to be the license)
licenseHeaderFile(rootDir.resolve("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)")
endWithNewline()
}
}
}

@ -38,5 +38,6 @@ plugins {
alias(libs.plugins.roborazzi) apply false alias(libs.plugins.roborazzi) apply false
alias(libs.plugins.google.osslicenses) apply false alias(libs.plugins.google.osslicenses) apply false
alias(libs.plugins.room) apply false alias(libs.plugins.room) apply false
alias(libs.plugins.spotless) apply false
alias(libs.plugins.nowinandroid.root) alias(libs.plugins.nowinandroid.root)
} }

@ -14,4 +14,4 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest /> <manifest />

@ -22,4 +22,4 @@ dependencies {
implementation(libs.kotlinx.coroutines.core) implementation(libs.kotlinx.coroutines.core)
testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.turbine) testImplementation(libs.turbine)
} }

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.network package com.google.samples.apps.nowinandroid.core.common.network
import javax.inject.Qualifier import javax.inject.Qualifier
import kotlin.annotation.AnnotationRetention.RUNTIME import kotlin.annotation.AnnotationRetention.RUNTIME

@ -1,5 +1,5 @@
/* /*
* Copyright 2023 The Android Open Source Project * Copyright 2026 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,10 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.network.di package com.google.samples.apps.nowinandroid.core.common.network.di
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.common.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.Default import com.google.samples.apps.nowinandroid.core.common.network.NiaDispatchers.Default
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn

@ -1,5 +1,5 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2026 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,11 +14,11 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.network.di package com.google.samples.apps.nowinandroid.core.common.network.di
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.common.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.Default import com.google.samples.apps.nowinandroid.core.common.network.NiaDispatchers.Default
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.common.network.NiaDispatchers.IO
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.result package com.google.samples.apps.nowinandroid.core.common.result
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch

@ -1,5 +1,5 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2026 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.result package com.google.samples.apps.nowinandroid.core.common.result
import app.cash.turbine.test import app.cash.turbine.test
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
@ -40,6 +40,7 @@ class ResultKtTest {
"Test Done", "Test Done",
errorResult.exception.message, errorResult.exception.message,
) )
Result.Loading, Result.Loading,
is Result.Success, is Result.Success,
-> throw IllegalStateException( -> throw IllegalStateException(

@ -19,7 +19,7 @@ graph TB
:core:data-test[data-test]:::android-library :core:data-test[data-test]:::android-library
:core:database[database]:::android-library :core:database[database]:::android-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library
:core:network[network]:::android-library :core:network[network]:::android-library
:core:notifications[notifications]:::android-library :core:notifications[notifications]:::android-library

@ -16,13 +16,13 @@
package com.google.samples.apps.nowinandroid.core.data.test.repository package com.google.samples.apps.nowinandroid.core.data.test.repository
import com.google.samples.apps.nowinandroid.core.common.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.common.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.data.Synchronizer import com.google.samples.apps.nowinandroid.core.data.Synchronizer
import com.google.samples.apps.nowinandroid.core.data.model.asExternalModel import com.google.samples.apps.nowinandroid.core.data.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.demo.DemoNiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.demo.DemoNiaNetworkDataSource
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow

@ -16,11 +16,11 @@
package com.google.samples.apps.nowinandroid.core.data.test.repository package com.google.samples.apps.nowinandroid.core.data.test.repository
import com.google.samples.apps.nowinandroid.core.common.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.common.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.data.Synchronizer import com.google.samples.apps.nowinandroid.core.data.Synchronizer
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.demo.DemoNiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.demo.DemoNiaNetworkDataSource
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow

@ -18,7 +18,7 @@ graph TB
:core:data[data]:::android-library :core:data[data]:::android-library
:core:database[database]:::android-library :core:database[database]:::android-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library
:core:network[network]:::android-library :core:network[network]:::android-library
:core:notifications[notifications]:::android-library :core:notifications[notifications]:::android-library

@ -16,4 +16,4 @@
--> -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest> </manifest>

@ -16,6 +16,8 @@
package com.google.samples.apps.nowinandroid.core.data.repository package com.google.samples.apps.nowinandroid.core.data.repository
import com.google.samples.apps.nowinandroid.core.common.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.common.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao
import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceFtsDao import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceFtsDao
import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao
@ -24,8 +26,6 @@ import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsRes
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.database.model.asFtsEntity import com.google.samples.apps.nowinandroid.core.database.model.asFtsEntity
import com.google.samples.apps.nowinandroid.core.model.data.SearchResult import com.google.samples.apps.nowinandroid.core.model.data.SearchResult
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine

@ -25,8 +25,8 @@ import android.net.NetworkRequest
import android.net.NetworkRequest.Builder import android.net.NetworkRequest.Builder
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.tracing.trace import androidx.tracing.trace
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.common.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.common.network.NiaDispatchers.IO
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose

@ -23,9 +23,9 @@ import android.content.IntentFilter
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.Build.VERSION_CODES import android.os.Build.VERSION_CODES
import androidx.tracing.trace import androidx.tracing.trace
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.common.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.common.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope import com.google.samples.apps.nowinandroid.core.common.network.di.ApplicationScope
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope

@ -14,4 +14,4 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest /> <manifest />

@ -13,7 +13,7 @@ config:
graph TB graph TB
subgraph :core subgraph :core
direction TB direction TB
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
end end
classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;

@ -15,23 +15,19 @@
*/ */
plugins { plugins {
alias(libs.plugins.nowinandroid.android.library) alias(libs.plugins.nowinandroid.jvm.library)
alias(libs.plugins.protobuf) alias(libs.plugins.protobuf)
} }
android {
namespace = "com.google.samples.apps.nowinandroid.core.datastore.proto"
}
// Setup protobuf configuration, generating lite Java and Kotlin classes // Setup protobuf configuration, generating lite Java and Kotlin classes
protobuf { protobuf {
protoc { protoc {
artifact = libs.protobuf.protoc.get().toString() artifact = libs.protobuf.protoc.get().toString()
} }
generateProtoTasks { generateProtoTasks {
all().forEach { task -> all().configureEach {
task.builtins { builtins {
register("java") { named("java") {
option("lite") option("lite")
} }
register("kotlin") { register("kotlin") {
@ -42,14 +38,6 @@ protobuf {
} }
} }
androidComponents.beforeVariants {
android.sourceSets.register(it.name) {
val buildDir = layout.buildDirectory.get().asFile
java.srcDir(buildDir.resolve("generated/source/proto/${it.name}/java"))
kotlin.srcDir(buildDir.resolve("generated/source/proto/${it.name}/kotlin"))
}
}
dependencies { dependencies {
api(libs.protobuf.kotlin.lite) api(libs.protobuf.kotlin.lite)
} }

@ -15,7 +15,7 @@ graph TB
direction TB direction TB
:core:common[common]:::jvm-library :core:common[common]:::jvm-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:datastore-test[datastore-test]:::android-library :core:datastore-test[datastore-test]:::android-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library
end end

@ -14,4 +14,4 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest /> <manifest />

@ -15,7 +15,7 @@ graph TB
direction TB direction TB
:core:common[common]:::jvm-library :core:common[common]:::jvm-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library
end end

@ -14,4 +14,4 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest /> <manifest />

@ -20,12 +20,12 @@ import android.content.Context
import androidx.datastore.core.DataStore import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory import androidx.datastore.core.DataStoreFactory
import androidx.datastore.dataStoreFile import androidx.datastore.dataStoreFile
import com.google.samples.apps.nowinandroid.core.common.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.common.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.common.network.di.ApplicationScope
import com.google.samples.apps.nowinandroid.core.datastore.IntToStringIdsMigration import com.google.samples.apps.nowinandroid.core.datastore.IntToStringIdsMigration
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferences import com.google.samples.apps.nowinandroid.core.datastore.UserPreferences
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferencesSerializer import com.google.samples.apps.nowinandroid.core.datastore.UserPreferencesSerializer
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn

@ -14,4 +14,4 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest /> <manifest />

@ -18,7 +18,7 @@ graph TB
:core:data[data]:::android-library :core:data[data]:::android-library
:core:database[database]:::android-library :core:database[database]:::android-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:domain[domain]:::android-library :core:domain[domain]:::android-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library
:core:network[network]:::android-library :core:network[network]:::android-library

@ -30,4 +30,4 @@ dependencies {
implementation(libs.javax.inject) implementation(libs.javax.inject)
testImplementation(projects.core.testing) testImplementation(projects.core.testing)
} }

@ -14,4 +14,4 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest /> <manifest />

@ -17,4 +17,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
</manifest> </manifest>

@ -19,8 +19,8 @@ package com.google.samples.apps.nowinandroid.core.network.demo
import JvmUnitTestDemoAssetManager import JvmUnitTestDemoAssetManager
import android.os.Build.VERSION.SDK_INT import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.M import android.os.Build.VERSION_CODES.M
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.common.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.common.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource

@ -28,4 +28,4 @@
android:pathData="M17.6,11.48 L19.44,8.3a0.63,0.63 0,0 0,-1.09 -0.63l-1.88,3.24a11.43,11.43 0,0 0,-8.94 0L5.65,7.67a0.63,0.63 0,0 0,-1.09 0.63L6.4,11.48A10.81,10.81 0,0 0,1 20L23,20A10.81,10.81 0,0 0,17.6 11.48ZM7,17.25A1.25,1.25 0,1 1,8.25 16,1.25 1.25,0 0,1 7,17.25ZM17,17.25A1.25,1.25 0,1 1,18.25 16,1.25 1.25,0 0,1 17,17.25Z" android:pathData="M17.6,11.48 L19.44,8.3a0.63,0.63 0,0 0,-1.09 -0.63l-1.88,3.24a11.43,11.43 0,0 0,-8.94 0L5.65,7.67a0.63,0.63 0,0 0,-1.09 0.63L6.4,11.48A10.81,10.81 0,0 0,1 20L23,20A10.81,10.81 0,0 0,17.6 11.48ZM7,17.25A1.25,1.25 0,1 1,8.25 16,1.25 1.25,0 0,1 7,17.25ZM17,17.25A1.25,1.25 0,1 1,18.25 16,1.25 1.25,0 0,1 17,17.25Z"
android:fillColor="#FF000000"/> android:fillColor="#FF000000"/>
</group> </group>
</vector> </vector>

@ -21,4 +21,4 @@
Theme_Material_Light_NoActionBar is the base theme used by the production app. Theme_Material_Light_NoActionBar is the base theme used by the production app.
--> -->
<application android:theme="@android:style/Theme.Material.NoActionBar" /> <application android:theme="@android:style/Theme.Material.NoActionBar" />
</manifest> </manifest>

@ -18,7 +18,7 @@ graph TB
:core:data[data]:::android-library :core:data[data]:::android-library
:core:database[database]:::android-library :core:database[database]:::android-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library
:core:network[network]:::android-library :core:network[network]:::android-library
:core:notifications[notifications]:::android-library :core:notifications[notifications]:::android-library

@ -14,4 +14,4 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest /> <manifest />

@ -16,10 +16,10 @@
package com.google.samples.apps.nowinandroid.core.testing.di package com.google.samples.apps.nowinandroid.core.testing.di
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.common.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.Default import com.google.samples.apps.nowinandroid.core.common.network.NiaDispatchers.Default
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.common.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.di.DispatchersModule import com.google.samples.apps.nowinandroid.core.common.network.di.DispatchersModule
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent

@ -14,4 +14,4 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest /> <manifest />

@ -20,4 +20,4 @@ plugins {
android { android {
namespace = "com.google.samples.apps.nowinandroid.feature.bookmarks.api" namespace = "com.google.samples.apps.nowinandroid.feature.bookmarks.api"
} }

@ -30,7 +30,7 @@ graph TB
:core:data[data]:::android-library :core:data[data]:::android-library
:core:database[database]:::android-library :core:database[database]:::android-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:designsystem[designsystem]:::android-library :core:designsystem[designsystem]:::android-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library
:core:navigation[navigation]:::android-library :core:navigation[navigation]:::android-library

@ -14,4 +14,4 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest /> <manifest />

@ -14,4 +14,4 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest /> <manifest />

@ -21,4 +21,4 @@
<string name="feature_foryou_api_navigate_up">Navigate up</string> <string name="feature_foryou_api_navigate_up">Navigate up</string>
<string name="feature_foryou_api_onboarding_guidance_title">What are you interested in?</string> <string name="feature_foryou_api_onboarding_guidance_title">What are you interested in?</string>
<string name="feature_foryou_api_onboarding_guidance_subtitle">Updates from topics you follow will appear here. Follow some things to get started.</string> <string name="feature_foryou_api_onboarding_guidance_subtitle">Updates from topics you follow will appear here. Follow some things to get started.</string>
</resources> </resources>

@ -30,7 +30,7 @@ graph TB
:core:data[data]:::android-library :core:data[data]:::android-library
:core:database[database]:::android-library :core:database[database]:::android-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:designsystem[designsystem]:::android-library :core:designsystem[designsystem]:::android-library
:core:domain[domain]:::android-library :core:domain[domain]:::android-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library

@ -40,4 +40,4 @@ dependencies {
androidTestImplementation(libs.bundles.androidx.compose.ui.test) androidTestImplementation(libs.bundles.androidx.compose.ui.test)
androidTestImplementation(projects.core.testing) androidTestImplementation(projects.core.testing)
} }

@ -20,4 +20,4 @@ plugins {
android { android {
namespace = "com.google.samples.apps.nowinandroid.feature.interests.api" namespace = "com.google.samples.apps.nowinandroid.feature.interests.api"
} }

@ -14,4 +14,4 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest /> <manifest />

@ -52,4 +52,4 @@
android:strokeColor="#8C4190" android:strokeColor="#8C4190"
android:strokeLineCap="round" android:strokeLineCap="round"
android:strokeWidth="2" /> android:strokeWidth="2" />
</vector> </vector>

@ -25,7 +25,7 @@ graph TB
:core:data[data]:::android-library :core:data[data]:::android-library
:core:database[database]:::android-library :core:database[database]:::android-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:domain[domain]:::android-library :core:domain[domain]:::android-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library
:core:navigation[navigation]:::android-library :core:navigation[navigation]:::android-library

@ -25,4 +25,3 @@ android {
dependencies { dependencies {
implementation(projects.core.domain) implementation(projects.core.domain)
} }

@ -34,7 +34,7 @@ graph TB
:core:data[data]:::android-library :core:data[data]:::android-library
:core:database[database]:::android-library :core:database[database]:::android-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:designsystem[designsystem]:::android-library :core:designsystem[designsystem]:::android-library
:core:domain[domain]:::android-library :core:domain[domain]:::android-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library

@ -34,4 +34,4 @@ dependencies {
androidTestImplementation(libs.bundles.androidx.compose.ui.test) androidTestImplementation(libs.bundles.androidx.compose.ui.test)
androidTestImplementation(projects.core.testing) androidTestImplementation(projects.core.testing)
} }

@ -25,7 +25,7 @@ graph TB
:core:data[data]:::android-library :core:data[data]:::android-library
:core:database[database]:::android-library :core:database[database]:::android-library
:core:datastore[datastore]:::android-library :core:datastore[datastore]:::android-library
:core:datastore-proto[datastore-proto]:::android-library :core:datastore-proto[datastore-proto]:::jvm-library
:core:designsystem[designsystem]:::android-library :core:designsystem[designsystem]:::android-library
:core:model[model]:::jvm-library :core:model[model]:::jvm-library
:core:network[network]:::android-library :core:network[network]:::android-library

@ -22,4 +22,4 @@ plugins {
android { android {
namespace = "com.google.samples.apps.nowinandroid.feature.topic.api" namespace = "com.google.samples.apps.nowinandroid.feature.topic.api"
} }

@ -35,4 +35,4 @@ dependencies {
androidTestImplementation(libs.bundles.androidx.compose.ui.test) androidTestImplementation(libs.bundles.androidx.compose.ui.test)
androidTestImplementation(projects.core.testing) androidTestImplementation(projects.core.testing)
} }

@ -18,6 +18,8 @@ package com.google.samples.apps.nowinandroid.feature.topic.impl
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.core.common.result.Result
import com.google.samples.apps.nowinandroid.core.common.result.asResult
import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
@ -25,8 +27,6 @@ import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourc
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import com.google.samples.apps.nowinandroid.core.result.Result
import com.google.samples.apps.nowinandroid.core.result.asResult
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject

@ -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.4.0"
initscript {
val spotlessVersion = "6.25.0"
repositories {
mavenCentral()
}
dependencies {
classpath("com.diffplug.spotless:spotless-plugin-gradle:$spotlessVersion")
}
}
rootProject {
subprojects {
apply<com.diffplug.gradle.spotless.SpotlessPlugin>()
extensions.configure<com.diffplug.gradle.spotless.SpotlessExtension> {
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 (<!--) or the xml declaration (<?xml)
licenseHeaderFile(rootProject.file("spotless/copyright.xml"), "(<[^!?])")
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save