diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml
index 001140a87..db326c380 100644
--- a/.github/workflows/Build.yaml
+++ b/.github/workflows/Build.yaml
@@ -17,6 +17,7 @@ jobs:
permissions:
contents: write
+ pull-requests: write
timeout-minutes: 60
@@ -100,12 +101,13 @@ jobs:
commit_message: "🤖 Updates screenshots"
# Run local tests after screenshot tests to avoid wrong UP-TO-DATE. TODO: Ignore screenshots.
- - name: Run local tests
+ - name: Run local tests and create report
if: always()
run: ./gradlew testDemoDebug :lint:test
# Replace task exclusions with `-Pandroidx.baselineprofile.skipgeneration` when
# https://android-review.googlesource.com/c/platform/frameworks/support/+/2602790 landed in a
# release build
+
- name: Build all build type and flavor permutations
run: ./gradlew :app:assemble :benchmarks:assemble
-x pixel6Api33ProdNonMinifiedReleaseAndroidTest
@@ -119,11 +121,11 @@ jobs:
name: APKs
path: '**/build/outputs/apk/**/*.apk'
- - name: Upload test results (XML)
+ - name: Upload JVM local results (XML)
if: always()
uses: actions/upload-artifact@v4
with:
- name: test-results
+ name: local-test-results
path: '**/build/test-results/test*UnitTest/**.xml'
- name: Check lint
@@ -180,10 +182,7 @@ jobs:
- name: Setup Gradle
uses: gradle/gradle-build-action@v3
- - name: Build projects before running emulator
- run: ./gradlew packageDemoDebug packageDemoDebugAndroidTest
-
- - name: Run instrumentation tests
+ - name: Build projects and run instrumentation tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
@@ -193,9 +192,41 @@ jobs:
heap-size: 600M
script: ./gradlew connectedDemoDebugAndroidTest --daemon
+ - name: Run local tests (including Roborazzi) for the combined coverage report (only API 30)
+ if: matrix.api-level == 30
+ # There is no need to verify Roborazzi tests to generate coverage.
+ run: ./gradlew testDemoDebugUnitTest -Proborazzi.test.verify=false # Add Prod if we ever add JVM tests for prod
+
+ # Add `createProdDebugUnitTestCoverageReport` if we ever add JVM tests for prod
+ - name: Generate coverage reports for Debug variants (only API 30)
+ if: matrix.api-level == 30
+ run: ./gradlew createDemoDebugCombinedCoverageReport
+
- name: Upload test reports
if: always()
uses: actions/upload-artifact@v4
with:
name: test-reports-${{ matrix.api-level }}
path: '**/build/reports/androidTests'
+
+ - name: Display local test coverage (only API 30)
+ if: matrix.api-level == 30
+ id: jacoco
+ uses: madrapps/jacoco-report@v1.6.1
+ with:
+ title: Combined test coverage report
+ min-coverage-overall: 40
+ min-coverage-changed-files: 60
+ paths: |
+ ${{ github.workspace }}/**/build/reports/jacoco/**/*Report.xml
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Upload local coverage reports (XML + HTML) (only API 30)
+ if: matrix.api-level == 30
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-reports
+ if-no-files-found: error
+ compression-level: 1
+ overwrite: false
+ path: '**/build/reports/jacoco/'
diff --git a/README.md b/README.md
index be1270b16..b80090c29 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,9 @@
-Now in Android App
+Now in Android App - Kotlin Multiplatform Edition
==================
+Welcome to the Kotlin Multiplatform edition of Now in Android, a community-driven initiative to bring the best of Android development content to developers across platforms. This repository is a branch of the original Now in Android project, reimagined and re-engineered using Kotlin Multiplatform technology to make the project truly cross-platform.
**Learn how this app was designed and built in the [design case study](https://goo.gle/nia-figma), [architecture learning journey](docs/ArchitectureLearningJourney.md) and [modularization learning journey](docs/ModularizationLearningJourney.md).**
diff --git a/app-nia-catalog/README.md b/app-nia-catalog/README.md
index edbbb5e46..cf6d05f4f 100644
--- a/app-nia-catalog/README.md
+++ b/app-nia-catalog/README.md
@@ -1,3 +1,3 @@
# :app-nia-catalog module
-
-
+## Dependency graph
+
diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt
index d5a487997..4ae6a91b4 100644
--- a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt
+++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt
@@ -1,6 +1,6 @@
-androidx.activity:activity-compose:1.8.0
-androidx.activity:activity-ktx:1.8.0
-androidx.activity:activity:1.8.0
+androidx.activity:activity-compose:1.8.2
+androidx.activity:activity-ktx:1.8.2
+androidx.activity:activity:1.8.2
androidx.annotation:annotation-experimental:1.4.0
androidx.annotation:annotation-jvm:1.7.1
androidx.annotation:annotation:1.7.1
@@ -12,41 +12,41 @@ androidx.browser:browser:1.8.0
androidx.collection:collection-jvm:1.4.0
androidx.collection:collection-ktx:1.4.0
androidx.collection:collection:1.4.0
-androidx.compose.animation:animation-android:1.6.2
-androidx.compose.animation:animation-core-android:1.6.2
-androidx.compose.animation:animation-core:1.6.2
-androidx.compose.animation:animation:1.6.2
-androidx.compose.foundation:foundation-android:1.6.2
-androidx.compose.foundation:foundation-layout-android:1.6.2
-androidx.compose.foundation:foundation-layout:1.6.2
-androidx.compose.foundation:foundation:1.6.2
-androidx.compose.material3:material3-android:1.2.0
-androidx.compose.material3:material3:1.2.0
-androidx.compose.material:material-icons-core-android:1.6.2
-androidx.compose.material:material-icons-core:1.6.2
-androidx.compose.material:material-icons-extended-android:1.6.2
-androidx.compose.material:material-icons-extended:1.6.2
-androidx.compose.material:material-ripple-android:1.6.2
-androidx.compose.material:material-ripple:1.6.2
-androidx.compose.runtime:runtime-android:1.6.2
-androidx.compose.runtime:runtime-saveable-android:1.6.2
-androidx.compose.runtime:runtime-saveable:1.6.2
-androidx.compose.runtime:runtime:1.6.2
-androidx.compose.ui:ui-android:1.6.2
-androidx.compose.ui:ui-geometry-android:1.6.2
-androidx.compose.ui:ui-geometry:1.6.2
-androidx.compose.ui:ui-graphics-android:1.6.2
-androidx.compose.ui:ui-graphics:1.6.2
-androidx.compose.ui:ui-text-android:1.6.2
-androidx.compose.ui:ui-text:1.6.2
-androidx.compose.ui:ui-tooling-preview-android:1.6.2
-androidx.compose.ui:ui-tooling-preview:1.6.2
-androidx.compose.ui:ui-unit-android:1.6.2
-androidx.compose.ui:ui-unit:1.6.2
-androidx.compose.ui:ui-util-android:1.6.2
-androidx.compose.ui:ui-util:1.6.2
-androidx.compose.ui:ui:1.6.2
-androidx.compose:compose-bom:2024.02.01
+androidx.compose.animation:animation-android:1.6.3
+androidx.compose.animation:animation-core-android:1.6.3
+androidx.compose.animation:animation-core:1.6.3
+androidx.compose.animation:animation:1.6.3
+androidx.compose.foundation:foundation-android:1.6.3
+androidx.compose.foundation:foundation-layout-android:1.6.3
+androidx.compose.foundation:foundation-layout:1.6.3
+androidx.compose.foundation:foundation:1.6.3
+androidx.compose.material3:material3-android:1.2.1
+androidx.compose.material3:material3:1.2.1
+androidx.compose.material:material-icons-core-android:1.6.3
+androidx.compose.material:material-icons-core:1.6.3
+androidx.compose.material:material-icons-extended-android:1.6.3
+androidx.compose.material:material-icons-extended:1.6.3
+androidx.compose.material:material-ripple-android:1.6.3
+androidx.compose.material:material-ripple:1.6.3
+androidx.compose.runtime:runtime-android:1.6.3
+androidx.compose.runtime:runtime-saveable-android:1.6.3
+androidx.compose.runtime:runtime-saveable:1.6.3
+androidx.compose.runtime:runtime:1.6.3
+androidx.compose.ui:ui-android:1.6.3
+androidx.compose.ui:ui-geometry-android:1.6.3
+androidx.compose.ui:ui-geometry:1.6.3
+androidx.compose.ui:ui-graphics-android:1.6.3
+androidx.compose.ui:ui-graphics:1.6.3
+androidx.compose.ui:ui-text-android:1.6.3
+androidx.compose.ui:ui-text:1.6.3
+androidx.compose.ui:ui-tooling-preview-android:1.6.3
+androidx.compose.ui:ui-tooling-preview:1.6.3
+androidx.compose.ui:ui-unit-android:1.6.3
+androidx.compose.ui:ui-unit:1.6.3
+androidx.compose.ui:ui-util-android:1.6.3
+androidx.compose.ui:ui-util:1.6.3
+androidx.compose.ui:ui:1.6.3
+androidx.compose:compose-bom:2024.02.02
androidx.concurrent:concurrent-futures:1.1.0
androidx.core:core-ktx:1.12.0
androidx.core:core:1.12.0
@@ -94,10 +94,10 @@ io.coil-kt:coil-compose-base:2.6.0
io.coil-kt:coil-compose:2.6.0
io.coil-kt:coil:2.6.0
javax.inject:javax.inject:1
-org.jetbrains.kotlin:kotlin-stdlib-common:1.9.22
+org.jetbrains.kotlin:kotlin-stdlib-common:1.9.23
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0
-org.jetbrains.kotlin:kotlin-stdlib:1.9.22
+org.jetbrains.kotlin:kotlin-stdlib:1.9.23
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3
diff --git a/app/README.md b/app/README.md
index 9f151c245..a3fb4572a 100644
--- a/app/README.md
+++ b/app/README.md
@@ -1,3 +1,3 @@
# :app module
-
-
+## Dependency graph
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index d3621f1e3..6908104a7 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -21,7 +21,6 @@ plugins {
alias(libs.plugins.nowinandroid.android.application.flavors)
alias(libs.plugins.nowinandroid.android.application.jacoco)
alias(libs.plugins.nowinandroid.android.hilt)
- id("jacoco")
alias(libs.plugins.nowinandroid.android.application.firebase)
id("com.google.android.gms.oss-licenses-plugin")
alias(libs.plugins.baselineprofile)
@@ -90,6 +89,8 @@ dependencies {
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.compose.material3.adaptive)
+ implementation(libs.androidx.compose.material3.adaptive.layout)
+ implementation(libs.androidx.compose.material3.adaptive.navigation)
implementation(libs.androidx.compose.material3.windowSizeClass)
implementation(libs.androidx.compose.runtime.tracing)
implementation(libs.androidx.core.ktx)
diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt
index b7eecc088..cc80b19ce 100644
--- a/app/dependencies/prodReleaseRuntimeClasspath.txt
+++ b/app/dependencies/prodReleaseRuntimeClasspath.txt
@@ -1,6 +1,6 @@
-androidx.activity:activity-compose:1.8.0
-androidx.activity:activity-ktx:1.8.0
-androidx.activity:activity:1.8.0
+androidx.activity:activity-compose:1.8.2
+androidx.activity:activity-ktx:1.8.2
+androidx.activity:activity:1.8.2
androidx.annotation:annotation-experimental:1.4.0
androidx.annotation:annotation-jvm:1.7.1
androidx.annotation:annotation:1.7.1
@@ -13,43 +13,43 @@ androidx.browser:browser:1.8.0
androidx.collection:collection-jvm:1.4.0
androidx.collection:collection-ktx:1.4.0
androidx.collection:collection:1.4.0
-androidx.compose.animation:animation-android:1.6.1
-androidx.compose.animation:animation-core-android:1.6.1
-androidx.compose.animation:animation-core:1.6.1
-androidx.compose.animation:animation:1.6.1
-androidx.compose.foundation:foundation-android:1.6.1
-androidx.compose.foundation:foundation-layout-android:1.6.1
-androidx.compose.foundation:foundation-layout:1.6.1
-androidx.compose.foundation:foundation:1.6.1
+androidx.compose.animation:animation-android:1.6.2
+androidx.compose.animation:animation-core-android:1.6.2
+androidx.compose.animation:animation-core:1.6.2
+androidx.compose.animation:animation:1.6.2
+androidx.compose.foundation:foundation-android:1.6.2
+androidx.compose.foundation:foundation-layout-android:1.6.2
+androidx.compose.foundation:foundation-layout:1.6.2
+androidx.compose.foundation:foundation:1.6.2
androidx.compose.material3:material3-android:1.2.0
androidx.compose.material3:material3-window-size-class-android:1.2.0
androidx.compose.material3:material3-window-size-class:1.2.0
androidx.compose.material3:material3:1.2.0
-androidx.compose.material:material-icons-core-android:1.6.1
-androidx.compose.material:material-icons-core:1.6.1
-androidx.compose.material:material-icons-extended-android:1.6.1
-androidx.compose.material:material-icons-extended:1.6.1
-androidx.compose.material:material-ripple-android:1.6.1
-androidx.compose.material:material-ripple:1.6.1
-androidx.compose.runtime:runtime-android:1.6.1
-androidx.compose.runtime:runtime-saveable-android:1.6.1
-androidx.compose.runtime:runtime-saveable:1.6.1
-androidx.compose.runtime:runtime:1.6.1
-androidx.compose.ui:ui-android:1.6.1
-androidx.compose.ui:ui-geometry-android:1.6.1
-androidx.compose.ui:ui-geometry:1.6.1
-androidx.compose.ui:ui-graphics-android:1.6.1
-androidx.compose.ui:ui-graphics:1.6.1
-androidx.compose.ui:ui-text-android:1.6.1
-androidx.compose.ui:ui-text:1.6.1
-androidx.compose.ui:ui-tooling-preview-android:1.6.1
-androidx.compose.ui:ui-tooling-preview:1.6.1
-androidx.compose.ui:ui-unit-android:1.6.1
-androidx.compose.ui:ui-unit:1.6.1
-androidx.compose.ui:ui-util-android:1.6.1
-androidx.compose.ui:ui-util:1.6.1
-androidx.compose.ui:ui:1.6.1
-androidx.compose:compose-bom:2024.02.00
+androidx.compose.material:material-icons-core-android:1.6.2
+androidx.compose.material:material-icons-core:1.6.2
+androidx.compose.material:material-icons-extended-android:1.6.2
+androidx.compose.material:material-icons-extended:1.6.2
+androidx.compose.material:material-ripple-android:1.6.2
+androidx.compose.material:material-ripple:1.6.2
+androidx.compose.runtime:runtime-android:1.6.2
+androidx.compose.runtime:runtime-saveable-android:1.6.2
+androidx.compose.runtime:runtime-saveable:1.6.2
+androidx.compose.runtime:runtime:1.6.2
+androidx.compose.ui:ui-android:1.6.2
+androidx.compose.ui:ui-geometry-android:1.6.2
+androidx.compose.ui:ui-geometry:1.6.2
+androidx.compose.ui:ui-graphics-android:1.6.2
+androidx.compose.ui:ui-graphics:1.6.2
+androidx.compose.ui:ui-text-android:1.6.2
+androidx.compose.ui:ui-text:1.6.2
+androidx.compose.ui:ui-tooling-preview-android:1.6.2
+androidx.compose.ui:ui-tooling-preview:1.6.2
+androidx.compose.ui:ui-unit-android:1.6.2
+androidx.compose.ui:ui-unit:1.6.2
+androidx.compose.ui:ui-util-android:1.6.2
+androidx.compose.ui:ui-util:1.6.2
+androidx.compose.ui:ui:1.6.2
+androidx.compose:compose-bom:2024.02.01
androidx.concurrent:concurrent-futures:1.1.0
androidx.core:core-ktx:1.12.0
androidx.core:core-splashscreen:1.0.1
@@ -128,20 +128,20 @@ app.cash.sqldelight:runtime-jvm:2.0.1
app.cash.sqldelight:runtime:2.0.1
com.caverock:androidsvg-aar:1.4
com.google.accompanist:accompanist-drawablepainter:0.32.0
-com.google.accompanist:accompanist-permissions:0.32.0
-com.google.android.datatransport:transport-api:3.0.0
+com.google.accompanist:accompanist-permissions:0.34.0
+com.google.android.datatransport:transport-api:3.1.0
com.google.android.datatransport:transport-backend-cct:3.1.9
com.google.android.datatransport:transport-runtime:3.1.9
com.google.android.gms:play-services-ads-identifier:18.0.0
com.google.android.gms:play-services-base:18.0.1
com.google.android.gms:play-services-basement:18.1.0
-com.google.android.gms:play-services-cloud-messaging:17.0.1
-com.google.android.gms:play-services-measurement-api:21.4.0
-com.google.android.gms:play-services-measurement-base:21.4.0
-com.google.android.gms:play-services-measurement-impl:21.4.0
-com.google.android.gms:play-services-measurement-sdk-api:21.4.0
-com.google.android.gms:play-services-measurement-sdk:21.4.0
-com.google.android.gms:play-services-measurement:21.4.0
+com.google.android.gms:play-services-cloud-messaging:17.1.0
+com.google.android.gms:play-services-measurement-api:21.5.1
+com.google.android.gms:play-services-measurement-base:21.5.1
+com.google.android.gms:play-services-measurement-impl:21.5.1
+com.google.android.gms:play-services-measurement-sdk-api:21.5.1
+com.google.android.gms:play-services-measurement-sdk:21.5.1
+com.google.android.gms:play-services-measurement:21.5.1
com.google.android.gms:play-services-oss-licenses:17.0.1
com.google.android.gms:play-services-stats:17.0.2
com.google.android.gms:play-services-tasks:18.0.2
@@ -152,16 +152,17 @@ com.google.dagger:hilt-android:2.50
com.google.dagger:hilt-core:2.50
com.google.errorprone:error_prone_annotations:2.11.0
com.google.firebase:firebase-abt:21.1.1
-com.google.firebase:firebase-analytics-ktx:21.4.0
-com.google.firebase:firebase-analytics:21.4.0
+com.google.firebase:firebase-analytics-ktx:21.5.1
+com.google.firebase:firebase-analytics:21.5.1
com.google.firebase:firebase-annotations:16.2.0
-com.google.firebase:firebase-bom:32.4.0
+com.google.firebase:firebase-bom:32.7.3
com.google.firebase:firebase-common-ktx:20.4.2
com.google.firebase:firebase-common:20.4.2
com.google.firebase:firebase-components:17.1.5
-com.google.firebase:firebase-config:21.5.0
-com.google.firebase:firebase-crashlytics-ktx:18.5.0
-com.google.firebase:firebase-crashlytics:18.5.0
+com.google.firebase:firebase-config-interop:16.0.1
+com.google.firebase:firebase-config:21.6.2
+com.google.firebase:firebase-crashlytics-ktx:18.6.2
+com.google.firebase:firebase-crashlytics:18.6.2
com.google.firebase:firebase-datatransport:18.1.8
com.google.firebase:firebase-encoders-json:18.0.1
com.google.firebase:firebase-encoders-proto:16.0.0
@@ -170,19 +171,27 @@ com.google.firebase:firebase-iid-interop:17.1.0
com.google.firebase:firebase-installations-interop:17.1.1
com.google.firebase:firebase-installations:17.2.0
com.google.firebase:firebase-measurement-connector:19.0.0
-com.google.firebase:firebase-messaging-ktx:23.3.0
-com.google.firebase:firebase-messaging:23.3.0
-com.google.firebase:firebase-perf-ktx:20.5.0
-com.google.firebase:firebase-perf:20.5.0
-com.google.firebase:firebase-sessions:1.1.0
+com.google.firebase:firebase-messaging-ktx:23.4.1
+com.google.firebase:firebase-messaging:23.4.1
+com.google.firebase:firebase-perf-ktx:20.5.2
+com.google.firebase:firebase-perf:20.5.2
+com.google.firebase:firebase-sessions:1.2.2
com.google.firebase:protolite-well-known-types:18.0.0
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:31.1-android
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.3
-com.google.protobuf:protobuf-javalite:3.25.2
-com.google.protobuf:protobuf-kotlin-lite:3.25.2
+com.google.protobuf:protobuf-javalite:3.25.3
+com.google.protobuf:protobuf-kotlin-lite:3.25.3
com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0
+com.russhwolf:multiplatform-settings-android:1.1.1
+com.russhwolf:multiplatform-settings-coroutines-android:1.1.1
+com.russhwolf:multiplatform-settings-coroutines:1.1.1
+com.russhwolf:multiplatform-settings-no-arg-android:1.1.1
+com.russhwolf:multiplatform-settings-no-arg:1.1.1
+com.russhwolf:multiplatform-settings-serialization-android:1.1.1
+com.russhwolf:multiplatform-settings-serialization:1.1.1
+com.russhwolf:multiplatform-settings:1.1.1
com.squareup.okhttp3:logging-interceptor:4.12.0
com.squareup.okhttp3:okhttp:4.12.0
com.squareup.okio:okio-jvm:3.8.0
@@ -199,10 +208,10 @@ javax.inject:javax.inject:1
me.tatarka.inject:kotlin-inject-runtime-jvm:0.6.3
me.tatarka.inject:kotlin-inject-runtime:0.6.3
org.checkerframework:checker-qual:3.12.0
-org.jetbrains.kotlin:kotlin-stdlib-common:1.9.22
+org.jetbrains.kotlin:kotlin-stdlib-common:1.9.23
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0
-org.jetbrains.kotlin:kotlin-stdlib:1.9.22
+org.jetbrains.kotlin:kotlin-stdlib:1.9.23
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.0
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0
diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt
index b2eabe2ed..eaed15a4e 100644
--- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt
+++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt
@@ -17,7 +17,6 @@
package com.google.samples.apps.nowinandroid.ui
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
@@ -78,7 +77,6 @@ import com.google.samples.apps.nowinandroid.feature.settings.R as settingsR
@OptIn(
ExperimentalMaterial3Api::class,
- ExperimentalLayoutApi::class,
ExperimentalComposeUiApi::class,
)
@Composable
diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt
index 98327923f..335f83371 100644
--- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt
+++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt
@@ -17,14 +17,12 @@
package com.google.samples.apps.nowinandroid.ui.interests2pane
import androidx.activity.compose.BackHandler
-import androidx.compose.foundation.layout.Box
-import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
-import androidx.compose.material3.adaptive.ListDetailPaneScaffold
-import androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole
-import androidx.compose.material3.adaptive.PaneAdaptedValue
-import androidx.compose.material3.adaptive.ThreePaneScaffoldNavigator
-import androidx.compose.material3.adaptive.rememberListDetailPaneScaffoldNavigator
+import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
+import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
+import androidx.compose.material3.adaptive.layout.PaneAdaptedValue
+import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
+import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -39,6 +37,7 @@ import androidx.navigation.navArgument
import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute
import com.google.samples.apps.nowinandroid.feature.interests.navigation.INTERESTS_ROUTE
import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_ARG
+import com.google.samples.apps.nowinandroid.feature.topic.TopicDetailPlaceholder
import com.google.samples.apps.nowinandroid.feature.topic.navigation.TOPIC_ROUTE
import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic
import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicScreen
@@ -93,7 +92,8 @@ internal fun InterestsListDetailScreen(
}
ListDetailPaneScaffold(
- scaffoldState = listDetailNavigator.scaffoldState,
+ value = listDetailNavigator.scaffoldValue,
+ directive = listDetailNavigator.scaffoldDirective,
listPane = {
InterestsRoute(
onTopicClick = ::onTopicClickShowDetailPane,
@@ -112,9 +112,7 @@ internal fun InterestsListDetailScreen(
onTopicClick = ::onTopicClickShowDetailPane,
)
composable(route = TOPIC_ROUTE) {
- Box {
- Text("Placeholder")
- }
+ TopicDetailPlaceholder()
}
}
},
@@ -129,8 +127,8 @@ internal fun InterestsListDetailScreen(
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private fun ThreePaneScaffoldNavigator.isListPaneVisible(): Boolean =
- scaffoldState.scaffoldValue[ListDetailPaneScaffoldRole.List] == PaneAdaptedValue.Expanded
+ scaffoldValue[ListDetailPaneScaffoldRole.List] == PaneAdaptedValue.Expanded
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private fun ThreePaneScaffoldNavigator.isDetailPaneVisible(): Boolean =
- scaffoldState.scaffoldValue[ListDetailPaneScaffoldRole.Detail] == PaneAdaptedValue.Expanded
+ scaffoldValue[ListDetailPaneScaffoldRole.Detail] == PaneAdaptedValue.Expanded
diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt
index 4c3acc520..ac385b0d9 100644
--- a/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt
@@ -15,6 +15,7 @@
*/
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
+import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import com.google.samples.apps.nowinandroid.configureJacoco
import org.gradle.api.Plugin
import org.gradle.api.Project
@@ -23,13 +24,15 @@ import org.gradle.kotlin.dsl.getByType
class AndroidApplicationJacocoConventionPlugin : Plugin {
override fun apply(target: Project) {
with(target) {
- with(pluginManager) {
- apply("org.gradle.jacoco")
- apply("com.android.application")
+ pluginManager.apply("jacoco")
+ val androidExtension = extensions.getByType()
+
+ androidExtension.buildTypes.configureEach {
+ enableAndroidTestCoverage = true
+ enableUnitTestCoverage = true
}
- val extension = extensions.getByType()
- configureJacoco(extension)
+
+ configureJacoco(extensions.getByType())
}
}
-
-}
\ No newline at end of file
+}
diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt
index 86ca091c3..6f2ff60c5 100644
--- a/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import com.android.build.api.dsl.LibraryExtension
+import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.api.variant.LibraryAndroidComponentsExtension
import com.google.samples.apps.nowinandroid.configureJacoco
import org.gradle.api.Plugin
@@ -23,13 +25,15 @@ import org.gradle.kotlin.dsl.getByType
class AndroidLibraryJacocoConventionPlugin : Plugin {
override fun apply(target: Project) {
with(target) {
- with(pluginManager) {
- apply("org.gradle.jacoco")
- apply("com.android.library")
+ pluginManager.apply("jacoco")
+ val androidExtension = extensions.getByType()
+
+ androidExtension.buildTypes.configureEach {
+ enableAndroidTestCoverage = true
+ enableUnitTestCoverage = true
}
- val extension = extensions.getByType()
- configureJacoco(extension)
+
+ configureJacoco(extensions.getByType())
}
}
-
-}
\ No newline at end of file
+}
diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Jacoco.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Jacoco.kt
index 596c4f579..7820a978e 100644
--- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Jacoco.kt
+++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Jacoco.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,13 @@
package com.google.samples.apps.nowinandroid
+import com.android.build.api.artifact.ScopedArtifact
import com.android.build.api.variant.AndroidComponentsExtension
+import com.android.build.api.variant.ScopedArtifacts
import org.gradle.api.Project
+import org.gradle.api.file.Directory
+import org.gradle.api.file.RegularFile
+import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.register
@@ -32,13 +37,24 @@ private val coverageExclusions = listOf(
"**/R.class",
"**/R\$*.class",
"**/BuildConfig.*",
- "**/Manifest*.*"
+ "**/Manifest*.*",
+ "**/*_Hilt*.class",
+ "**/Hilt_*.class",
)
private fun String.capitalize() = replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
}
+/**
+ * Creates a new task that generates a combined coverage report with data from local and
+ * instrumented tests.
+ *
+ * `create{variant}CombinedCoverageReport`
+ *
+ * Note that coverage data must exist before running the task. This allows us to run device
+ * tests on CI using a different Github Action or an external device farm.
+ */
internal fun Project.configureJacoco(
androidComponentsExtension: AndroidComponentsExtension<*, *, *>,
) {
@@ -46,37 +62,53 @@ internal fun Project.configureJacoco(
toolVersion = libs.findVersion("jacoco").get().toString()
}
- val jacocoTestReport = tasks.create("jacocoTestReport")
-
androidComponentsExtension.onVariants { variant ->
- val testTaskName = "test${variant.name.capitalize()}UnitTest"
+ val myObjFactory = project.objects
val buildDir = layout.buildDirectory.get().asFile
- val reportTask = tasks.register("jacoco${testTaskName.capitalize()}Report", JacocoReport::class) {
- dependsOn(testTaskName)
+ val allJars: ListProperty = myObjFactory.listProperty(RegularFile::class.java)
+ val allDirectories: ListProperty = myObjFactory.listProperty(Directory::class.java)
+ val reportTask =
+ tasks.register("create${variant.name.capitalize()}CombinedCoverageReport", JacocoReport::class) {
- reports {
- xml.required.set(true)
- html.required.set(true)
- }
-
- classDirectories.setFrom(
- fileTree("$buildDir/tmp/kotlin-classes/${variant.name}") {
- exclude(coverageExclusions)
+ classDirectories.setFrom(
+ allJars,
+ allDirectories.map { dirs ->
+ dirs.map { dir ->
+ myObjFactory.fileTree().setDir(dir).exclude(coverageExclusions)
+ }
+ }
+ )
+ reports {
+ xml.required.set(true)
+ html.required.set(true)
}
- )
- sourceDirectories.setFrom(files("$projectDir/src/main/java", "$projectDir/src/main/kotlin"))
- executionData.setFrom(file("$buildDir/jacoco/$testTaskName.exec"))
- }
+ // TODO: This is missing files in src/debug/, src/prod, src/demo, src/demoDebug...
+ sourceDirectories.setFrom(files("$projectDir/src/main/java", "$projectDir/src/main/kotlin"))
+
+ executionData.setFrom(
+ project.fileTree("$buildDir/outputs/unit_test_code_coverage/${variant.name}UnitTest")
+ .matching { include("**/*.exec") },
+
+ project.fileTree("$buildDir/outputs/code_coverage/${variant.name}AndroidTest")
+ .matching { include("**/*.ec") }
+ )
+ }
+
- jacocoTestReport.dependsOn(reportTask)
+ variant.artifacts.forScope(ScopedArtifacts.Scope.PROJECT)
+ .use(reportTask)
+ .toGet(
+ ScopedArtifact.CLASSES,
+ { _ -> allJars },
+ { _ -> allDirectories },
+ )
}
tasks.withType().configureEach {
configure {
// Required for JaCoCo + Robolectric
// https://github.com/robolectric/robolectric/issues/2230
- // TODO: Consider removing if not we don't add Robolectric
isIncludeNoLocationClasses = true
// Required for JDK 11 with the above
diff --git a/build.gradle.kts b/build.gradle.kts
index 0f84b5118..0fedb0580 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -28,9 +28,10 @@ buildscript {
}
classpath(libs.buildkonfig.gradlePlugin)
}
+
}
-// Lists all plugins used throughout the project without applying them.
+// Lists all plugins used throughout the project
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
@@ -47,9 +48,20 @@ plugins {
alias(libs.plugins.roborazzi) apply false
alias(libs.plugins.secrets) apply false
alias(libs.plugins.room) apply false
+ alias(libs.plugins.module.graph) apply true // Plugin applied to allow module graph generation
alias(libs.plugins.jetbrains.compose) apply false
alias(libs.plugins.kotlin.multiplatform) apply false
alias(libs.plugins.sqldelight.gradle.plugin) apply false
alias(libs.plugins.ktrofit) apply false
alias(libs.plugins.buildkonfig) apply false
}
+
+// Task to print all the module paths in the project e.g. :core:data
+// Used by module graph generator script
+tasks.register("printModulePaths") {
+ subprojects {
+ if (subprojects.size == 0) {
+ println(this.path)
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/analytics/README.md b/core/analytics/README.md
new file mode 100644
index 000000000..d2bcd1ea7
--- /dev/null
+++ b/core/analytics/README.md
@@ -0,0 +1,3 @@
+# :core:analytics module
+## Dependency graph
+
diff --git a/core/common/README.md b/core/common/README.md
index ade22c076..96558bcc6 100644
--- a/core/common/README.md
+++ b/core/common/README.md
@@ -1,3 +1,3 @@
# :core:common module
-
-
+## Dependency graph
+
diff --git a/core/data-test/README.md b/core/data-test/README.md
index 4f623e629..977ee10e4 100644
--- a/core/data-test/README.md
+++ b/core/data-test/README.md
@@ -1,3 +1,3 @@
# :core:data-test module
-
-
+## Dependency graph
+
diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt
index 5ceff4dd0..070c7ed38 100644
--- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt
+++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt
@@ -25,7 +25,7 @@ import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
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.fake.FakeNiaNetworkDataSource
+import com.google.samples.apps.nowinandroid.core.network.demo.DemoNiaNetworkDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -41,7 +41,7 @@ import javax.inject.Inject
*/
internal class FakeNewsRepository @Inject constructor(
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
- private val datasource: FakeNiaNetworkDataSource,
+ private val datasource: DemoNiaNetworkDataSource,
) : NewsRepository {
override fun getNewsResources(
diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeTopicsRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeTopicsRepository.kt
index f8ebca29e..0b81dd309 100644
--- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeTopicsRepository.kt
+++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeTopicsRepository.kt
@@ -21,7 +21,7 @@ import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepositor
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.fake.FakeNiaNetworkDataSource
+import com.google.samples.apps.nowinandroid.core.network.demo.DemoNiaNetworkDataSource
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
@@ -38,7 +38,7 @@ import javax.inject.Inject
*/
internal class FakeTopicsRepository @Inject constructor(
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
- private val datasource: FakeNiaNetworkDataSource,
+ private val datasource: DemoNiaNetworkDataSource,
) : TopicsRepository {
override fun getTopics(): Flow> = flow {
emit(
diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeUserDataRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeUserDataRepository.kt
index cdd23429f..4871baad9 100644
--- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeUserDataRepository.kt
+++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeUserDataRepository.kt
@@ -43,7 +43,7 @@ internal class FakeUserDataRepository @Inject constructor(
override suspend fun setTopicIdFollowed(followedTopicId: String, followed: Boolean) =
niaPreferencesDataSource.setTopicIdFollowed(followedTopicId, followed)
- override suspend fun updateNewsResourceBookmark(newsResourceId: String, bookmarked: Boolean) {
+ override suspend fun setNewsResourceBookmarked(newsResourceId: String, bookmarked: Boolean) {
niaPreferencesDataSource.setNewsResourceBookmarked(newsResourceId, bookmarked)
}
diff --git a/core/data/README.md b/core/data/README.md
index 905d74615..5d30f1638 100644
--- a/core/data/README.md
+++ b/core/data/README.md
@@ -1,3 +1,3 @@
# :core:data module
-
-
+## Dependency graph
+
diff --git a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt
index 9ee73b11a..341b02f59 100644
--- a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt
+++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt
@@ -19,8 +19,6 @@ package com.google.samples.apps.nowinandroid.core.data
import co.touchlab.kermit.Logger
import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
import kotlin.coroutines.cancellation.CancellationException
/**
@@ -103,29 +101,3 @@ suspend fun Synchronizer.changeListSync(
versionUpdater(latestVersion)
}
}.isSuccess
-
-/**
- * Returns a [Flow] whose values are generated by [transform] function that process the most
- * recently emitted values by each flow.
- */
-fun combine(
- flow: Flow,
- flow2: Flow,
- flow3: Flow,
- flow4: Flow,
- flow5: Flow,
- flow6: Flow,
- transform: suspend (T1, T2, T3, T4, T5, T6) -> R,
-): Flow = combine(
- combine(flow, flow2, flow3, ::Triple),
- combine(flow4, flow5, flow6, ::Triple),
-) { t1, t2 ->
- transform(
- t1.first,
- t1.second,
- t1.third,
- t2.first,
- t2.second,
- t2.third,
- )
-}
diff --git a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt
index f5c65440e..68c6efb36 100644
--- a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt
+++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt
@@ -41,7 +41,7 @@ internal class OfflineFirstUserDataRepository(
analyticsHelper.logTopicFollowToggled(followedTopicId, followed)
}
- override suspend fun updateNewsResourceBookmark(newsResourceId: String, bookmarked: Boolean) {
+ override suspend fun setNewsResourceBookmarked(newsResourceId: String, bookmarked: Boolean) {
niaPreferencesDataSource.setNewsResourceBookmarked(newsResourceId, bookmarked)
analyticsHelper.logNewsResourceBookmarkToggled(
newsResourceId = newsResourceId,
diff --git a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt
index ff616c179..c5202b02b 100644
--- a/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt
+++ b/core/data/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt
@@ -41,7 +41,7 @@ interface UserDataRepository {
/**
* Updates the bookmarked status for a news resource
*/
- suspend fun updateNewsResourceBookmark(newsResourceId: String, bookmarked: Boolean)
+ suspend fun setNewsResourceBookmarked(newsResourceId: String, bookmarked: Boolean)
/**
* Updates the viewed status for a news resource
diff --git a/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt
index e62a5c4c1..271cb6a09 100644
--- a/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt
+++ b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt
@@ -136,7 +136,7 @@ class OfflineFirstUserDataRepositoryTest {
@Test
fun offlineFirstUserDataRepository_bookmark_news_resource_logic_delegates_to_nia_preferences() =
testScope.runTest {
- subject.updateNewsResourceBookmark(newsResourceId = "0", bookmarked = true)
+ subject.setNewsResourceBookmarked(newsResourceId = "0", bookmarked = true)
assertEquals(
setOf("0"),
@@ -145,7 +145,7 @@ class OfflineFirstUserDataRepositoryTest {
.first(),
)
- subject.updateNewsResourceBookmark(newsResourceId = "1", bookmarked = true)
+ subject.setNewsResourceBookmarked(newsResourceId = "1", bookmarked = true)
assertEquals(
setOf("0", "1"),
diff --git a/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt
index 1a81e84b5..a060289c3 100644
--- a/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt
+++ b/core/data/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt
@@ -17,7 +17,7 @@
package com.google.samples.apps.nowinandroid.core.data.testdoubles
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
-import com.google.samples.apps.nowinandroid.core.network.fake.FakeNiaNetworkDataSource
+import com.google.samples.apps.nowinandroid.core.network.demo.DemoNiaNetworkDataSource
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.NetworkTopic
@@ -37,7 +37,7 @@ enum class CollectionType {
@OptIn(ExperimentalCoroutinesApi::class)
class TestNiaNetworkDataSource : NiaNetworkDataSource {
- private val source = FakeNiaNetworkDataSource(
+ private val source = DemoNiaNetworkDataSource(
UnconfinedTestDispatcher(),
Json { ignoreUnknownKeys = true },
)
diff --git a/core/database/README.md b/core/database/README.md
index 5cf339aed..855eab53e 100644
--- a/core/database/README.md
+++ b/core/database/README.md
@@ -1,3 +1,3 @@
# :core:database module
-
-
+## Dependency graph
+
diff --git a/core/datastore-proto/README.md b/core/datastore-proto/README.md
new file mode 100644
index 000000000..19ed58239
--- /dev/null
+++ b/core/datastore-proto/README.md
@@ -0,0 +1,3 @@
+# :core:datastore-proto module
+## Dependency graph
+
diff --git a/core/datastore-test/README.md b/core/datastore-test/README.md
new file mode 100644
index 000000000..99cf13f1f
--- /dev/null
+++ b/core/datastore-test/README.md
@@ -0,0 +1,3 @@
+# :core:datastore-test module
+## Dependency graph
+
diff --git a/core/datastore/README.md b/core/datastore/README.md
index 56699a483..4785c5885 100644
--- a/core/datastore/README.md
+++ b/core/datastore/README.md
@@ -1,3 +1,3 @@
# :core:datastore module
-
-
+## Dependency graph
+
diff --git a/core/designsystem/README.md b/core/designsystem/README.md
index 52a793821..d1778cb14 100644
--- a/core/designsystem/README.md
+++ b/core/designsystem/README.md
@@ -1,3 +1,3 @@
# :core:designsystem module
-
-
+## Dependency graph
+
diff --git a/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt
index d838362c4..9a5f8bd03 100644
--- a/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt
+++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/FilterChipScreenshotTests.kt
@@ -45,7 +45,7 @@ import org.robolectric.annotation.LooperMode
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
-class FilterChipScreenshotTests() {
+class FilterChipScreenshotTests {
@get:Rule
val composeTestRule = createAndroidComposeRule()
diff --git a/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt
index 57916092b..efb6ca469 100644
--- a/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt
+++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/LoadingWheelScreenshotTests.kt
@@ -39,7 +39,7 @@ import org.robolectric.annotation.LooperMode
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
-class LoadingWheelScreenshotTests() {
+class LoadingWheelScreenshotTests {
@get:Rule
val composeTestRule = createAndroidComposeRule()
diff --git a/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt
index 31ade1f88..dabbe77ae 100644
--- a/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt
+++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/NavigationScreenshotTests.kt
@@ -46,7 +46,7 @@ import org.robolectric.annotation.LooperMode
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
-class NavigationScreenshotTests() {
+class NavigationScreenshotTests {
@get:Rule
val composeTestRule = createAndroidComposeRule()
diff --git a/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt
index f551770d3..4e83b0b75 100644
--- a/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt
+++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TabsScreenshotTests.kt
@@ -44,7 +44,7 @@ import org.robolectric.annotation.LooperMode
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
-class TabsScreenshotTests() {
+class TabsScreenshotTests {
@get:Rule
val composeTestRule = createAndroidComposeRule()
diff --git a/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt
index 9c73aff18..bf5c32c7b 100644
--- a/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt
+++ b/core/designsystem/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/TagScreenshotTests.kt
@@ -41,7 +41,7 @@ import org.robolectric.annotation.LooperMode
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
-class TagScreenshotTests() {
+class TagScreenshotTests {
@get:Rule
val composeTestRule = createAndroidComposeRule()
diff --git a/core/domain/README.md b/core/domain/README.md
new file mode 100644
index 000000000..cc6905846
--- /dev/null
+++ b/core/domain/README.md
@@ -0,0 +1,3 @@
+# :core:domain module
+## Dependency graph
+
diff --git a/core/model/README.md b/core/model/README.md
index 5279064f2..efd0eec76 100644
--- a/core/model/README.md
+++ b/core/model/README.md
@@ -1,3 +1,3 @@
# :core:model module
-
-
+## Dependency graph
+
diff --git a/core/network/README.md b/core/network/README.md
index cfd9fd369..516aa2d38 100644
--- a/core/network/README.md
+++ b/core/network/README.md
@@ -1,3 +1,3 @@
# :core:network module
-
-
+## Dependency graph
+
diff --git a/core/network/lint.xml b/core/network/lint.xml
index 59fd50bd5..07c0cae9e 100644
--- a/core/network/lint.xml
+++ b/core/network/lint.xml
@@ -20,6 +20,6 @@
java.lang.IllegalStateException: () -> kotlin.String at org.jetbrains.kotlin.asJava.classes.KtLightClassForFacadeImpl$Companion.createForFacadeNoCache
-->
-
+
diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSource.kt
similarity index 93%
rename from core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt
rename to core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSource.kt
index b4220c8f6..78a71a68a 100644
--- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSource.kt
+++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSource.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.samples.apps.nowinandroid.core.network.fake
+package com.google.samples.apps.nowinandroid.core.network.demo
import com.google.samples.apps.nowinandroid.core.di.IODispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
@@ -30,7 +30,7 @@ import me.tatarka.inject.annotations.Inject
/**
* [NiaNetworkDataSource] implementation that provides static news resources to aid development
*/
-class FakeNiaNetworkDataSource @Inject constructor(
+class DemoNiaNetworkDataSource @Inject constructor(
private val ioDispatcher: IODispatcher,
private val networkJson: Json,
) : NiaNetworkDataSource {
diff --git a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt
index a6aff8b95..07cc87ca3 100644
--- a/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt
+++ b/core/network/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/network/di/FlavoredNetworkModule.kt
@@ -17,9 +17,9 @@
package com.google.samples.apps.nowinandroid.core.network.di
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
-import com.google.samples.apps.nowinandroid.core.network.fake.FakeNiaNetworkDataSource
+import com.google.samples.apps.nowinandroid.core.network.demo.DemoNiaNetworkDataSource
internal interface FlavoredNetworkModule {
- fun binds(impl: FakeNiaNetworkDataSource): NiaNetworkDataSource
+ fun binds(impl: DemoNiaNetworkDataSource): NiaNetworkDataSource
}
diff --git a/core/network/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt b/core/network/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/FakeNiaNetworkDataSourceTest.kt
similarity index 92%
rename from core/network/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt
rename to core/network/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/FakeNiaNetworkDataSourceTest.kt
index c88755a6d..e7fda79db 100644
--- a/core/network/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/network/fake/FakeNiaNetworkDataSourceTest.kt
+++ b/core/network/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/FakeNiaNetworkDataSourceTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.samples.apps.nowinandroid.core.network.fake
+package com.google.samples.apps.nowinandroid.core.network.demo
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
@@ -28,15 +28,15 @@ import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
-class FakeNiaNetworkDataSourceTest {
+class DemoNiaNetworkDataSourceTest {
- private lateinit var subject: FakeNiaNetworkDataSource
+ private lateinit var subject: DemoNiaNetworkDataSource
private val testDispatcher = StandardTestDispatcher()
@BeforeTest
fun setUp() {
- subject = FakeNiaNetworkDataSource(
+ subject = DemoNiaNetworkDataSource(
ioDispatcher = testDispatcher,
networkJson = Json { ignoreUnknownKeys = true },
)
diff --git a/core/notifications/README.md b/core/notifications/README.md
new file mode 100644
index 000000000..8f5607bdf
--- /dev/null
+++ b/core/notifications/README.md
@@ -0,0 +1,3 @@
+# :core:notifications module
+## Dependency graph
+
diff --git a/core/screenshot-testing/README.md b/core/screenshot-testing/README.md
new file mode 100644
index 000000000..9bd4f1f9c
--- /dev/null
+++ b/core/screenshot-testing/README.md
@@ -0,0 +1,3 @@
+# :core:screenshot-testing module
+## Dependency graph
+
diff --git a/core/testing/README.md b/core/testing/README.md
index 8eea64ac9..5a35d379b 100644
--- a/core/testing/README.md
+++ b/core/testing/README.md
@@ -1,3 +1,3 @@
# :core:testing module
-
-
+## Dependency graph
+
diff --git a/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt
index 504e79217..be76112dc 100644
--- a/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt
+++ b/core/testing/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt
@@ -61,7 +61,7 @@ class TestUserDataRepository : UserDataRepository {
}
}
- override suspend fun updateNewsResourceBookmark(newsResourceId: String, bookmarked: Boolean) {
+ override suspend fun setNewsResourceBookmarked(newsResourceId: String, bookmarked: Boolean) {
currentUserData.let { current ->
val bookmarkedNews = if (bookmarked) {
current.bookmarkedNewsResources + newsResourceId
diff --git a/core/ui/README.md b/core/ui/README.md
index 88c3561f1..38e514d01 100644
--- a/core/ui/README.md
+++ b/core/ui/README.md
@@ -1,3 +1,3 @@
# :core:ui module
-
-
+## Dependency graph
+
diff --git a/core/ui/src/commonMain/composeResources/values/strings.xml b/core/ui/src/commonMain/composeResources/values/strings.xml
index 65a855fc9..ab76748ef 100644
--- a/core/ui/src/commonMain/composeResources/values/strings.xml
+++ b/core/ui/src/commonMain/composeResources/values/strings.xml
@@ -26,4 +26,7 @@
%1$s is followed
%1$s is not followed
+
+ Follow interest
+ Unfollow interest
diff --git a/docs/ArchitectureLearningJourney.md b/docs/ArchitectureLearningJourney.md
index 925858111..d98dadf1e 100644
--- a/docs/ArchitectureLearningJourney.md
+++ b/docs/ArchitectureLearningJourney.md
@@ -25,6 +25,8 @@ The app architecture has three layers: a [data layer](https://developer.android.
+> [!NOTE]
+> The official Android architecture is different from other architectures, such as "Clean Architecture". Concepts from other architectures may not apply here, or be applied in different ways. [More discussion here](https://github.com/android/nowinandroid/discussions/1273).
The architecture follows a reactive programming model with [unidirectional data flow](https://developer.android.com/jetpack/guide/ui-layer#udf). With the data layer at the bottom, the key concepts are:
diff --git a/docs/images/graphs/dep_graph_app.png b/docs/images/graphs/dep_graph_app.png
deleted file mode 100644
index dc1e0b7d7..000000000
Binary files a/docs/images/graphs/dep_graph_app.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_app.svg b/docs/images/graphs/dep_graph_app.svg
new file mode 100644
index 000000000..57a592a8e
--- /dev/null
+++ b/docs/images/graphs/dep_graph_app.svg
@@ -0,0 +1,463 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_app_nia_catalog.png b/docs/images/graphs/dep_graph_app_nia_catalog.png
deleted file mode 100644
index e2698f0b0..000000000
Binary files a/docs/images/graphs/dep_graph_app_nia_catalog.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_app_nia_catalog.svg b/docs/images/graphs/dep_graph_app_nia_catalog.svg
new file mode 100644
index 000000000..b58415cef
--- /dev/null
+++ b/docs/images/graphs/dep_graph_app_nia_catalog.svg
@@ -0,0 +1,73 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_benchmark.png b/docs/images/graphs/dep_graph_benchmark.png
deleted file mode 100644
index a724c2fca..000000000
Binary files a/docs/images/graphs/dep_graph_benchmark.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_analytics.svg b/docs/images/graphs/dep_graph_core_analytics.svg
new file mode 100644
index 000000000..ac21c0707
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_analytics.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_common.png b/docs/images/graphs/dep_graph_core_common.png
deleted file mode 100644
index 8e5628068..000000000
Binary files a/docs/images/graphs/dep_graph_core_common.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_common.svg b/docs/images/graphs/dep_graph_core_common.svg
new file mode 100644
index 000000000..c91f33853
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_common.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_data.png b/docs/images/graphs/dep_graph_core_data.png
deleted file mode 100644
index fc30029b7..000000000
Binary files a/docs/images/graphs/dep_graph_core_data.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_data.svg b/docs/images/graphs/dep_graph_core_data.svg
new file mode 100644
index 000000000..cacf03a1f
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_data.svg
@@ -0,0 +1,151 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_data_test.png b/docs/images/graphs/dep_graph_core_data_test.png
deleted file mode 100644
index c3762b600..000000000
Binary files a/docs/images/graphs/dep_graph_core_data_test.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_data_test.svg b/docs/images/graphs/dep_graph_core_data_test.svg
new file mode 100644
index 000000000..162c83f10
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_data_test.svg
@@ -0,0 +1,163 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_database.png b/docs/images/graphs/dep_graph_core_database.png
deleted file mode 100644
index dc3e65756..000000000
Binary files a/docs/images/graphs/dep_graph_core_database.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_database.svg b/docs/images/graphs/dep_graph_core_database.svg
new file mode 100644
index 000000000..9e907b96f
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_database.svg
@@ -0,0 +1,31 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_datastore.png b/docs/images/graphs/dep_graph_core_datastore.png
deleted file mode 100644
index 861c2498a..000000000
Binary files a/docs/images/graphs/dep_graph_core_datastore.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_datastore.svg b/docs/images/graphs/dep_graph_core_datastore.svg
new file mode 100644
index 000000000..cfcf78db2
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_datastore.svg
@@ -0,0 +1,55 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_datastore_proto.svg b/docs/images/graphs/dep_graph_core_datastore_proto.svg
new file mode 100644
index 000000000..d572d0ea7
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_datastore_proto.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_datastore_test.png b/docs/images/graphs/dep_graph_core_datastore_test.png
deleted file mode 100644
index efe51c7de..000000000
Binary files a/docs/images/graphs/dep_graph_core_datastore_test.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_datastore_test.svg b/docs/images/graphs/dep_graph_core_datastore_test.svg
new file mode 100644
index 000000000..ca8d3f84b
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_datastore_test.svg
@@ -0,0 +1,73 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_designsystem.png b/docs/images/graphs/dep_graph_core_designsystem.png
deleted file mode 100644
index 1d6002d2a..000000000
Binary files a/docs/images/graphs/dep_graph_core_designsystem.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_designsystem.svg b/docs/images/graphs/dep_graph_core_designsystem.svg
new file mode 100644
index 000000000..f46f075f0
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_designsystem.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_domain.svg b/docs/images/graphs/dep_graph_core_domain.svg
new file mode 100644
index 000000000..1c97b64e8
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_domain.svg
@@ -0,0 +1,169 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_model.png b/docs/images/graphs/dep_graph_core_model.png
deleted file mode 100644
index 205583afa..000000000
Binary files a/docs/images/graphs/dep_graph_core_model.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_model.svg b/docs/images/graphs/dep_graph_core_model.svg
new file mode 100644
index 000000000..290457d6c
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_model.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_navigation.png b/docs/images/graphs/dep_graph_core_navigation.png
deleted file mode 100644
index 5ceab49b8..000000000
Binary files a/docs/images/graphs/dep_graph_core_navigation.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_network.png b/docs/images/graphs/dep_graph_core_network.png
deleted file mode 100644
index 908715660..000000000
Binary files a/docs/images/graphs/dep_graph_core_network.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_network.svg b/docs/images/graphs/dep_graph_core_network.svg
new file mode 100644
index 000000000..ea804bcff
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_network.svg
@@ -0,0 +1,43 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_notifications.svg b/docs/images/graphs/dep_graph_core_notifications.svg
new file mode 100644
index 000000000..cf25ca32e
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_notifications.svg
@@ -0,0 +1,43 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_screenshot_testing.svg b/docs/images/graphs/dep_graph_core_screenshot_testing.svg
new file mode 100644
index 000000000..e9fc6d48a
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_screenshot_testing.svg
@@ -0,0 +1,43 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_testing.png b/docs/images/graphs/dep_graph_core_testing.png
deleted file mode 100644
index 162830ae7..000000000
Binary files a/docs/images/graphs/dep_graph_core_testing.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_testing.svg b/docs/images/graphs/dep_graph_core_testing.svg
new file mode 100644
index 000000000..29d367e4b
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_testing.svg
@@ -0,0 +1,199 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_core_ui.png b/docs/images/graphs/dep_graph_core_ui.png
deleted file mode 100644
index 31c9e6715..000000000
Binary files a/docs/images/graphs/dep_graph_core_ui.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_core_ui.svg b/docs/images/graphs/dep_graph_core_ui.svg
new file mode 100644
index 000000000..2eba46866
--- /dev/null
+++ b/docs/images/graphs/dep_graph_core_ui.svg
@@ -0,0 +1,55 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_feature_author.png b/docs/images/graphs/dep_graph_feature_author.png
deleted file mode 100644
index ddd1f03f6..000000000
Binary files a/docs/images/graphs/dep_graph_feature_author.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_feature_bookmarks.png b/docs/images/graphs/dep_graph_feature_bookmarks.png
deleted file mode 100644
index f07fe891e..000000000
Binary files a/docs/images/graphs/dep_graph_feature_bookmarks.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_feature_bookmarks.svg b/docs/images/graphs/dep_graph_feature_bookmarks.svg
new file mode 100644
index 000000000..cfbb86412
--- /dev/null
+++ b/docs/images/graphs/dep_graph_feature_bookmarks.svg
@@ -0,0 +1,205 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_feature_foryou.png b/docs/images/graphs/dep_graph_feature_foryou.png
deleted file mode 100644
index cf483f1b1..000000000
Binary files a/docs/images/graphs/dep_graph_feature_foryou.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_feature_foryou.svg b/docs/images/graphs/dep_graph_feature_foryou.svg
new file mode 100644
index 000000000..e196bc7da
--- /dev/null
+++ b/docs/images/graphs/dep_graph_feature_foryou.svg
@@ -0,0 +1,229 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_feature_interests.png b/docs/images/graphs/dep_graph_feature_interests.png
deleted file mode 100644
index 09c74f995..000000000
Binary files a/docs/images/graphs/dep_graph_feature_interests.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_feature_interests.svg b/docs/images/graphs/dep_graph_feature_interests.svg
new file mode 100644
index 000000000..3728cb4f6
--- /dev/null
+++ b/docs/images/graphs/dep_graph_feature_interests.svg
@@ -0,0 +1,229 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_feature_search.svg b/docs/images/graphs/dep_graph_feature_search.svg
new file mode 100644
index 000000000..24c90cb0c
--- /dev/null
+++ b/docs/images/graphs/dep_graph_feature_search.svg
@@ -0,0 +1,229 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_feature_settings.svg b/docs/images/graphs/dep_graph_feature_settings.svg
new file mode 100644
index 000000000..93826715a
--- /dev/null
+++ b/docs/images/graphs/dep_graph_feature_settings.svg
@@ -0,0 +1,205 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_feature_topic.png b/docs/images/graphs/dep_graph_feature_topic.png
deleted file mode 100644
index 8385d1ed6..000000000
Binary files a/docs/images/graphs/dep_graph_feature_topic.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_feature_topic.svg b/docs/images/graphs/dep_graph_feature_topic.svg
new file mode 100644
index 000000000..cbda3c225
--- /dev/null
+++ b/docs/images/graphs/dep_graph_feature_topic.svg
@@ -0,0 +1,205 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_lint.png b/docs/images/graphs/dep_graph_lint.png
deleted file mode 100644
index 176d1de5d..000000000
Binary files a/docs/images/graphs/dep_graph_lint.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_sync.png b/docs/images/graphs/dep_graph_sync.png
deleted file mode 100644
index 26b79b9bc..000000000
Binary files a/docs/images/graphs/dep_graph_sync.png and /dev/null differ
diff --git a/docs/images/graphs/dep_graph_sync_sync_test.svg b/docs/images/graphs/dep_graph_sync_sync_test.svg
new file mode 100644
index 000000000..1e0753393
--- /dev/null
+++ b/docs/images/graphs/dep_graph_sync_sync_test.svg
@@ -0,0 +1,187 @@
+
+
+
+
+
diff --git a/docs/images/graphs/dep_graph_sync_work.svg b/docs/images/graphs/dep_graph_sync_work.svg
new file mode 100644
index 000000000..6901b5761
--- /dev/null
+++ b/docs/images/graphs/dep_graph_sync_work.svg
@@ -0,0 +1,169 @@
+
+
+
+
+
diff --git a/feature/bookmarks/README.md b/feature/bookmarks/README.md
index e2b8c65ac..54cbf91d0 100644
--- a/feature/bookmarks/README.md
+++ b/feature/bookmarks/README.md
@@ -1,3 +1,3 @@
# :feature:bookmarks module
-
-
+## Dependency graph
+
diff --git a/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt b/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt
index 7b6cac76a..f93602485 100644
--- a/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt
+++ b/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt
@@ -58,7 +58,7 @@ class BookmarksViewModel @Inject constructor(
viewModelScope.launch {
shouldDisplayUndoBookmark = true
lastRemovedBookmarkId = newsResourceId
- userDataRepository.updateNewsResourceBookmark(newsResourceId, false)
+ userDataRepository.setNewsResourceBookmarked(newsResourceId, false)
}
}
@@ -71,7 +71,7 @@ class BookmarksViewModel @Inject constructor(
fun undoBookmarkRemoval() {
viewModelScope.launch {
lastRemovedBookmarkId?.let {
- userDataRepository.updateNewsResourceBookmark(it, true)
+ userDataRepository.setNewsResourceBookmarked(it, true)
}
}
clearUndoState()
diff --git a/feature/bookmarks/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt b/feature/bookmarks/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt
index 6469a684b..037e9db64 100644
--- a/feature/bookmarks/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt
+++ b/feature/bookmarks/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt
@@ -67,7 +67,7 @@ class BookmarksViewModelTest {
val collectJob = launch(UnconfinedTestDispatcher()) { viewModel.feedUiState.collect() }
newsRepository.sendNewsResources(newsResourcesTestData)
- userDataRepository.updateNewsResourceBookmark(newsResourcesTestData[0].id, true)
+ userDataRepository.setNewsResourceBookmarked(newsResourcesTestData[0].id, true)
val item = viewModel.feedUiState.value
assertIs(item)
assertEquals(item.feed.size, 1)
@@ -81,7 +81,7 @@ class BookmarksViewModelTest {
// Set the news resources to be used by this test
newsRepository.sendNewsResources(newsResourcesTestData)
// Start with the resource saved
- userDataRepository.updateNewsResourceBookmark(newsResourcesTestData[0].id, true)
+ userDataRepository.setNewsResourceBookmarked(newsResourcesTestData[0].id, true)
// Use viewModel to remove saved resource
viewModel.removeFromSavedResources(newsResourcesTestData[0].id)
// Verify list of saved resources is now empty
diff --git a/feature/foryou/README.md b/feature/foryou/README.md
index 1ca599859..0f08cb827 100644
--- a/feature/foryou/README.md
+++ b/feature/foryou/README.md
@@ -1,3 +1,3 @@
# :feature:foryou module
-
-
+## Dependency graph
+
diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt
index 2a4b6f4ec..85035a77a 100644
--- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt
+++ b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt
@@ -117,7 +117,7 @@ class ForYouViewModel @Inject constructor(
fun updateNewsResourceSaved(newsResourceId: String, isChecked: Boolean) {
viewModelScope.launch {
- userDataRepository.updateNewsResourceBookmark(newsResourceId, isChecked)
+ userDataRepository.setNewsResourceBookmarked(newsResourceId, isChecked)
}
}
diff --git a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt
index b75573975..2fbdf0a79 100644
--- a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt
+++ b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt
@@ -36,6 +36,7 @@ import com.google.samples.apps.nowinandroid.core.testing.util.TestSyncManager
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import com.google.samples.apps.nowinandroid.feature.foryou.navigation.LINKED_NEWS_RESOURCE_ID
import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
@@ -70,6 +71,7 @@ class ForYouViewModelTest {
topicsRepository = topicsRepository,
userDataRepository = userDataRepository,
)
+
private val savedStateHandle = SavedStateHandle()
private lateinit var viewModel: ForYouViewModel
@@ -504,6 +506,24 @@ class ForYouViewModelTest {
collectJob.cancel()
}
+
+ @Test
+ fun whenUpdateNewsResourceSavedIsCalled_bookmarkStateIsUpdated() = runTest {
+ val newsResourceId = "123"
+ viewModel.updateNewsResourceSaved(newsResourceId, true)
+
+ assertEquals(
+ expected = setOf(newsResourceId),
+ actual = userDataRepository.userData.first().bookmarkedNewsResources,
+ )
+
+ viewModel.updateNewsResourceSaved(newsResourceId, false)
+
+ assertEquals(
+ expected = emptySet(),
+ actual = userDataRepository.userData.first().bookmarkedNewsResources,
+ )
+ }
}
private val sampleTopics = listOf(
diff --git a/feature/interests/README.md b/feature/interests/README.md
index b7601ecbc..90a4fbc9c 100644
--- a/feature/interests/README.md
+++ b/feature/interests/README.md
@@ -1,3 +1,3 @@
# :feature:interests module
-
-
+## Dependency graph
+
diff --git a/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt b/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt
index 1584662b8..a441f5a9d 100644
--- a/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt
+++ b/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt
@@ -27,10 +27,11 @@ import androidx.compose.ui.test.onNodeWithText
import com.google.samples.apps.nowinandroid.core.testing.data.followableTopicTestData
import com.google.samples.apps.nowinandroid.feature.interests.InterestsScreen
import com.google.samples.apps.nowinandroid.feature.interests.InterestsUiState
-import com.google.samples.apps.nowinandroid.feature.interests.R
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import com.google.samples.apps.nowinandroid.core.ui.R as CoreUiR
+import com.google.samples.apps.nowinandroid.feature.interests.R as InterestsR
/**
* UI test for checking the correct behaviour of the Interests screen;
@@ -50,12 +51,12 @@ class InterestsScreenTest {
@Before
fun setup() {
composeTestRule.activity.apply {
- interestsLoading = getString(R.string.feature_interests_loading)
- interestsEmptyHeader = getString(R.string.feature_interests_empty_header)
+ interestsLoading = getString(InterestsR.string.feature_interests_loading)
+ interestsEmptyHeader = getString(InterestsR.string.feature_interests_empty_header)
interestsTopicCardFollowButton =
- getString(R.string.feature_interests_card_follow_button_content_desc)
+ getString(CoreUiR.string.core_ui_interests_card_follow_button_content_desc)
interestsTopicCardUnfollowButton =
- getString(R.string.feature_interests_card_unfollow_button_content_desc)
+ getString(CoreUiR.string.core_ui_interests_card_unfollow_button_content_desc)
}
}
diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt
deleted file mode 100644
index 6ac0340ee..000000000
--- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsItem.kt
+++ /dev/null
@@ -1,207 +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.
- */
-
-package com.google.samples.apps.nowinandroid.feature.interests
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.material3.Icon
-import androidx.compose.material3.ListItem
-import androidx.compose.material3.ListItemDefaults
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.semantics.selected
-import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import com.google.samples.apps.nowinandroid.core.designsystem.component.DynamicAsyncImage
-import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton
-import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
-import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
-import com.google.samples.apps.nowinandroid.feature.interests.R.string
-
-@Composable
-fun InterestsItem(
- name: String,
- following: Boolean,
- topicImageUrl: String,
- onClick: () -> Unit,
- onFollowButtonClick: (Boolean) -> Unit,
- modifier: Modifier = Modifier,
- iconModifier: Modifier = Modifier,
- description: String = "",
- isSelected: Boolean = false,
-) {
- ListItem(
- leadingContent = {
- InterestsIcon(topicImageUrl, iconModifier.size(64.dp))
- },
- headlineContent = {
- Text(text = name)
- },
- supportingContent = {
- Text(text = description)
- },
- trailingContent = {
- NiaIconToggleButton(
- checked = following,
- onCheckedChange = onFollowButtonClick,
- icon = {
- Icon(
- imageVector = NiaIcons.Add,
- contentDescription = stringResource(
- id = string.feature_interests_card_follow_button_content_desc,
- ),
- )
- },
- checkedIcon = {
- Icon(
- imageVector = NiaIcons.Check,
- contentDescription = stringResource(
- id = string.feature_interests_card_unfollow_button_content_desc,
- ),
- )
- },
- )
- },
- colors = ListItemDefaults.colors(
- containerColor = if (isSelected) {
- MaterialTheme.colorScheme.surfaceVariant
- } else {
- Color.Transparent
- },
- ),
- modifier = modifier
- .semantics(mergeDescendants = true) {
- selected = isSelected
- }
- .clickable(enabled = true, onClick = onClick),
- )
-}
-
-@Composable
-private fun InterestsIcon(topicImageUrl: String, modifier: Modifier = Modifier) {
- if (topicImageUrl.isEmpty()) {
- Icon(
- modifier = modifier
- .background(MaterialTheme.colorScheme.surface)
- .padding(4.dp),
- imageVector = NiaIcons.Person,
- // decorative image
- contentDescription = null,
- )
- } else {
- DynamicAsyncImage(
- imageUrl = topicImageUrl,
- contentDescription = null,
- modifier = modifier,
- )
- }
-}
-
-@Preview
-@Composable
-private fun InterestsCardPreview() {
- NiaTheme {
- Surface {
- InterestsItem(
- name = "Compose",
- description = "Description",
- following = false,
- topicImageUrl = "",
- onClick = { },
- onFollowButtonClick = { },
- )
- }
- }
-}
-
-@Preview
-@Composable
-private fun InterestsCardLongNamePreview() {
- NiaTheme {
- Surface {
- InterestsItem(
- name = "This is a very very very very long name",
- description = "Description",
- following = true,
- topicImageUrl = "",
- onClick = { },
- onFollowButtonClick = { },
- )
- }
- }
-}
-
-@Preview
-@Composable
-private fun InterestsCardLongDescriptionPreview() {
- NiaTheme {
- Surface {
- InterestsItem(
- name = "Compose",
- description = "This is a very very very very very very very " +
- "very very very long description",
- following = false,
- topicImageUrl = "",
- onClick = { },
- onFollowButtonClick = { },
- )
- }
- }
-}
-
-@Preview
-@Composable
-private fun InterestsCardWithEmptyDescriptionPreview() {
- NiaTheme {
- Surface {
- InterestsItem(
- name = "Compose",
- description = "",
- following = true,
- topicImageUrl = "",
- onClick = { },
- onFollowButtonClick = { },
- )
- }
- }
-}
-
-@Preview
-@Composable
-private fun InterestsCardSelectedPreview() {
- NiaTheme {
- Surface {
- InterestsItem(
- name = "Compose",
- description = "",
- following = true,
- topicImageUrl = "",
- onClick = { },
- onFollowButtonClick = { },
- isSelected = true,
- )
- }
- }
-}
diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt
index 4a48645c5..83058c12e 100644
--- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt
+++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt
@@ -39,6 +39,7 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollba
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.rememberDraggableScroller
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
+import com.google.samples.apps.nowinandroid.core.ui.InterestsItem
@Composable
fun TopicsTabContent(
diff --git a/feature/interests/src/main/res/values/strings.xml b/feature/interests/src/main/res/values/strings.xml
index 2dd1c18a9..8d5322859 100644
--- a/feature/interests/src/main/res/values/strings.xml
+++ b/feature/interests/src/main/res/values/strings.xml
@@ -18,6 +18,4 @@
Interests
Loading data
"No available data"
- Follow interest
- Unfollow interest
diff --git a/feature/search/README.md b/feature/search/README.md
new file mode 100644
index 000000000..e205970f0
--- /dev/null
+++ b/feature/search/README.md
@@ -0,0 +1,3 @@
+# :feature:search module
+## Dependency graph
+
diff --git a/feature/search/build.gradle.kts b/feature/search/build.gradle.kts
index 206f4c0f9..98052e9ab 100644
--- a/feature/search/build.gradle.kts
+++ b/feature/search/build.gradle.kts
@@ -27,9 +27,7 @@ android {
dependencies {
implementation(projects.core.data)
implementation(projects.core.domain)
- implementation(projects.feature.bookmarks)
- implementation(projects.feature.foryou)
- implementation(projects.feature.interests)
+ implementation(projects.core.ui)
testImplementation(projects.core.testing)
diff --git a/feature/search/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt b/feature/search/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt
index 8a0532e1b..a9e2fa98f 100644
--- a/feature/search/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt
+++ b/feature/search/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenTest.kt
@@ -35,10 +35,10 @@ import com.google.samples.apps.nowinandroid.core.model.data.UserData
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import com.google.samples.apps.nowinandroid.core.testing.data.followableTopicTestData
import com.google.samples.apps.nowinandroid.core.testing.data.newsResourcesTestData
+import com.google.samples.apps.nowinandroid.core.ui.R.string
import org.junit.Before
import org.junit.Rule
import org.junit.Test
-import com.google.samples.apps.nowinandroid.feature.interests.R as interestsR
/**
* UI test for checking the correct behaviour of the Search screen.
@@ -73,9 +73,9 @@ class SearchScreenTest {
clearSearchContentDesc = getString(R.string.feature_search_clear_search_text_content_desc)
clearRecentSearchesContentDesc = getString(R.string.feature_search_clear_recent_searches_content_desc)
followButtonContentDesc =
- getString(interestsR.string.feature_interests_card_follow_button_content_desc)
+ getString(string.core_ui_interests_card_follow_button_content_desc)
unfollowButtonContentDesc =
- getString(interestsR.string.feature_interests_card_unfollow_button_content_desc)
+ getString(string.core_ui_interests_card_unfollow_button_content_desc)
topicsString = getString(R.string.feature_search_topics)
updatesString = getString(R.string.feature_search_updates)
tryAnotherSearchString = getString(R.string.feature_search_try_another_search) +
diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt
index ca159c80b..86b1eb717 100644
--- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt
+++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt
@@ -55,7 +55,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
@@ -88,14 +87,11 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews
+import com.google.samples.apps.nowinandroid.core.ui.InterestsItem
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success
import com.google.samples.apps.nowinandroid.core.ui.R.string
import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent
import com.google.samples.apps.nowinandroid.core.ui.newsFeed
-import com.google.samples.apps.nowinandroid.feature.bookmarks.BookmarksViewModel
-import com.google.samples.apps.nowinandroid.feature.foryou.ForYouViewModel
-import com.google.samples.apps.nowinandroid.feature.interests.InterestsItem
-import com.google.samples.apps.nowinandroid.feature.interests.InterestsViewModel
import com.google.samples.apps.nowinandroid.feature.search.R as searchR
@Composable
@@ -104,10 +100,7 @@ internal fun SearchRoute(
onInterestsClick: () -> Unit,
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
- bookmarksViewModel: BookmarksViewModel = hiltViewModel(),
- interestsViewModel: InterestsViewModel = hiltViewModel(),
searchViewModel: SearchViewModel = hiltViewModel(),
- forYouViewModel: ForYouViewModel = hiltViewModel(),
) {
val recentSearchQueriesUiState by searchViewModel.recentSearchQueriesUiState.collectAsStateWithLifecycle()
val searchResultUiState by searchViewModel.searchResultUiState.collectAsStateWithLifecycle()
@@ -120,9 +113,9 @@ internal fun SearchRoute(
onSearchQueryChanged = searchViewModel::onSearchQueryChanged,
onSearchTriggered = searchViewModel::onSearchTriggered,
onClearRecentSearches = searchViewModel::clearRecentSearches,
- onNewsResourcesCheckedChanged = forYouViewModel::updateNewsResourceSaved,
- onNewsResourceViewed = { bookmarksViewModel.setNewsResourceViewed(it, true) },
- onFollowButtonClick = interestsViewModel::followTopic,
+ onNewsResourcesCheckedChanged = searchViewModel::setNewsResourceBookmarked,
+ onNewsResourceViewed = { searchViewModel.setNewsResourceViewed(it, true) },
+ onFollowButtonClick = searchViewModel::followTopic,
onBackClick = onBackClick,
onInterestsClick = onInterestsClick,
onTopicClick = onTopicClick,
@@ -470,7 +463,6 @@ private fun SearchToolbar(
}
}
-@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun SearchTextField(
searchQuery: String,
diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt
index 7c05f81c5..ad6ca6112 100644
--- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt
+++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt
@@ -23,6 +23,7 @@ import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent.Param
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper
import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository
+import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.domain.GetRecentSearchQueriesUseCase
import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsCountUseCase
import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase
@@ -44,6 +45,7 @@ class SearchViewModel @Inject constructor(
getSearchContentsCountUseCase: GetSearchContentsCountUseCase,
recentSearchQueriesUseCase: GetRecentSearchQueriesUseCase,
private val recentSearchRepository: RecentSearchRepository,
+ private val userDataRepository: UserDataRepository,
private val savedStateHandle: SavedStateHandle,
private val analyticsHelper: AnalyticsHelper,
) : ViewModel() {
@@ -111,6 +113,24 @@ class SearchViewModel @Inject constructor(
recentSearchRepository.clearRecentSearches()
}
}
+
+ fun setNewsResourceBookmarked(newsResourceId: String, isChecked: Boolean) {
+ viewModelScope.launch {
+ userDataRepository.setNewsResourceBookmarked(newsResourceId, isChecked)
+ }
+ }
+
+ fun followTopic(followedTopicId: String, followed: Boolean) {
+ viewModelScope.launch {
+ userDataRepository.setTopicIdFollowed(followedTopicId, followed)
+ }
+ }
+
+ fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) {
+ viewModelScope.launch {
+ userDataRepository.setNewsResourceViewed(newsResourceId, viewed)
+ }
+ }
}
private fun AnalyticsHelper.logEventSearchTriggered(query: String) =
diff --git a/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt b/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt
index da0d5654e..662afca7d 100644
--- a/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt
+++ b/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt
@@ -33,6 +33,7 @@ import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.E
import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.Loading
import com.google.samples.apps.nowinandroid.feature.search.SearchResultUiState.SearchNotReady
import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -60,6 +61,7 @@ class SearchViewModelTest {
private val recentSearchRepository = TestRecentSearchRepository()
private val getRecentQueryUseCase = GetRecentSearchQueriesUseCase(recentSearchRepository)
private val getSearchContentsCountUseCase = GetSearchContentsCountUseCase(searchContentsRepository)
+
private lateinit var viewModel: SearchViewModel
@Before
@@ -70,6 +72,7 @@ class SearchViewModelTest {
recentSearchQueriesUseCase = getRecentQueryUseCase,
savedStateHandle = SavedStateHandle(),
recentSearchRepository = recentSearchRepository,
+ userDataRepository = userDataRepository,
analyticsHelper = NoOpAnalyticsHelper(),
)
userDataRepository.setUserData(emptyUserData)
@@ -128,4 +131,22 @@ class SearchViewModelTest {
collectJob.cancel()
}
+
+ @Test
+ fun whenToggleNewsResourceSavedIsCalled_bookmarkStateIsUpdated() = runTest {
+ val newsResourceId = "123"
+ viewModel.setNewsResourceBookmarked(newsResourceId, true)
+
+ assertEquals(
+ expected = setOf(newsResourceId),
+ actual = userDataRepository.userData.first().bookmarkedNewsResources,
+ )
+
+ viewModel.setNewsResourceBookmarked(newsResourceId, false)
+
+ assertEquals(
+ expected = emptySet(),
+ actual = userDataRepository.userData.first().bookmarkedNewsResources,
+ )
+ }
}
diff --git a/feature/settings/README.md b/feature/settings/README.md
new file mode 100644
index 000000000..7a4df04fe
--- /dev/null
+++ b/feature/settings/README.md
@@ -0,0 +1,3 @@
+# :feature:settings module
+## Dependency graph
+
diff --git a/feature/topic/README.md b/feature/topic/README.md
index d74517e63..84588929c 100644
--- a/feature/topic/README.md
+++ b/feature/topic/README.md
@@ -1,3 +1,3 @@
# :feature:topic module
-
-
+## Dependency graph
+
diff --git a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicDetailPlaceholder.kt b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicDetailPlaceholder.kt
new file mode 100644
index 000000000..627fb8fb3
--- /dev/null
+++ b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicDetailPlaceholder.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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.feature.topic
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
+
+@Composable
+fun TopicDetailPlaceholder(modifier: Modifier = Modifier) {
+ Card(
+ modifier = modifier,
+ colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant),
+ shape = RoundedCornerShape(24.dp, 24.dp, 0.dp, 0.dp),
+ ) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(
+ 20.dp,
+ alignment = Alignment.CenterVertically,
+ ),
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.feature_topic_ic_topic_placeholder),
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.primary,
+ )
+ Text(
+ text = stringResource(id = R.string.feature_topic_select_an_interest),
+ style = MaterialTheme.typography.titleLarge,
+ )
+ }
+ }
+}
+
+@Preview(widthDp = 200, heightDp = 300)
+@Composable
+fun TopicDetailPlaceholderPreview() {
+ NiaTheme {
+ TopicDetailPlaceholder()
+ }
+}
diff --git a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt
index 9fe6a2dd2..255e40f8b 100644
--- a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt
+++ b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt
@@ -81,7 +81,7 @@ class TopicViewModel @Inject constructor(
fun bookmarkNews(newsResourceId: String, bookmarked: Boolean) {
viewModelScope.launch {
- userDataRepository.updateNewsResourceBookmark(newsResourceId, bookmarked)
+ userDataRepository.setNewsResourceBookmarked(newsResourceId, bookmarked)
}
}
diff --git a/feature/topic/src/main/res/drawable/feature_topic_ic_topic_placeholder.xml b/feature/topic/src/main/res/drawable/feature_topic_ic_topic_placeholder.xml
new file mode 100644
index 000000000..0518401da
--- /dev/null
+++ b/feature/topic/src/main/res/drawable/feature_topic_ic_topic_placeholder.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/feature/topic/src/main/res/values/strings.xml b/feature/topic/src/main/res/values/strings.xml
index 5fefc3f42..fe4a6dc29 100644
--- a/feature/topic/src/main/res/values/strings.xml
+++ b/feature/topic/src/main/res/values/strings.xml
@@ -16,4 +16,5 @@
-->
Loading topic
+ Select an Interest
diff --git a/generateModuleGraphs.sh b/generateModuleGraphs.sh
new file mode 100755
index 000000000..fb2d74712
--- /dev/null
+++ b/generateModuleGraphs.sh
@@ -0,0 +1,108 @@
+#!/bin/bash
+#
+# Copyright 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# 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.
+#
+#
+# Script to generate dependency graphs for each of the modules. The --exclude-module parameter can
+# be used to exclude modules which are not part of the root dependency graph (and which, if included
+# would cause the script to fail.
+#
+# Usage: generateModuleGraphs.sh --exclude-module :benchmarks --exclude-module :lint --exclude-module :ui-test-hilt-manifest
+
+# Check if the dot command is available
+if ! command -v dot &> /dev/null
+then
+ echo "The 'dot' command is not found. This is required to generate SVGs from the Graphviz files."
+ echo "Installation instructions:"
+ echo " - On macOS: You can install Graphviz using Homebrew with the command: 'brew install graphviz'"
+ echo " - On Ubuntu: You can install Graphviz using APT with the command: 'sudo apt-get install graphviz'"
+ exit 1
+fi
+
+# Initialize an array to store excluded modules
+excluded_modules=()
+
+# Parse command-line arguments for excluded modules
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --exclude-module)
+ excluded_modules+=("$2")
+ shift # Past argument
+ shift # Past value
+ ;;
+ *)
+ echo "Unknown parameter passed: $1"
+ exit 1
+ ;;
+ esac
+done
+
+# Get the module paths
+module_paths=$(./gradlew -q printModulePaths --no-configuration-cache)
+
+# Ensure the output directory exists
+mkdir -p docs/images/graphs/
+
+# Function to check and create a README.md for modules which don't have one.
+check_and_create_readme() {
+ local module_path="$1"
+ local file_name="$2"
+
+ local readme_path="${module_path:1}" # Remove leading colon
+ readme_path=${readme_path//:/\/} # Replace colons with slashes
+ readme_path="${readme_path}/README.md" #Append the filename
+
+ # Check if README.md exists and create it if not
+ if [[ ! -f "$readme_path" ]]; then
+ echo "Creating README.md for ${module_path}"
+
+ # Determine the depth of the module based on the number of colons
+ local depth=$(awk -F: '{print NF-1}' <<< "${module_path}")
+
+ # Construct the relative image path with the correct number of "../"
+ local relative_image_path="../"
+ for ((i=1; i<$depth; i++)); do
+ relative_image_path+="../"
+ done
+ relative_image_path+="docs/images/graphs/${file_name}.svg"
+
+ echo "# ${module_path} module" > "$readme_path"
+ echo "## Dependency graph" >> "$readme_path"
+ echo "" >> "$readme_path"
+ fi
+}
+
+# Loop through each module path
+echo "$module_paths" | while read -r module_path; do
+ # Check if the module is in the excluded list
+ if [[ ! " ${excluded_modules[@]} " =~ " ${module_path} " ]]; then
+ # Derive the filename from the module path
+ file_name="dep_graph${module_path//:/_}" # Replace colons with underscores
+ file_name="${file_name//-/_}" # Replace dashes with underscores
+
+ check_and_create_readme "$module_path" "$file_name"
+
+ # Generate the .gv file in a temporary location
+ # "docs/images/graphs/${file_name}.svg"
+ # Remove the temporary .gv file
+ rm "/tmp/${file_name}.gv"
+ fi
+done
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 87ec125b4..d0f17b229 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -40,6 +40,9 @@ kotlin.code.style=official
android.defaults.buildfeatures.resvalues=false
android.defaults.buildfeatures.shaders=false
+# Run Roborazzi screenshot tests with the local tests
+roborazzi.test.verify=true
+
# Suppress: The following Kotlin/Native targets cannot be built on this machine and are disabled
kotlin.native.ignoreDisabledTargets=true
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index a968a4922..a7710ec76 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,15 +1,15 @@
[versions]
-accompanist = "0.32.0"
+accompanist = "0.34.0"
androidDesugarJdkLibs = "2.0.4"
# AGP and tools should be updated together
-androidGradlePlugin = "8.3.0"
-androidTools = "31.3.0"
+androidGradlePlugin = "8.3.1"
+androidTools = "31.3.1"
androidxActivity = "1.8.0"
androidxAppCompat = "1.6.1"
androidxBrowser = "1.8.0"
-androidxComposeBom = "2024.02.01"
-androidxComposeCompiler = "1.5.10"
-androidxComposeMaterial3Adaptive = "1.0.0-alpha06"
+androidxComposeBom = "2024.03.00"
+androidxComposeCompiler = "1.5.11"
+androidxComposeMaterial3Adaptive = "1.0.0-alpha08"
androidxComposeRuntimeTracing = "1.0.0-beta01"
androidxCore = "1.12.0"
androidxCoreSplashscreen = "1.0.1"
@@ -30,8 +30,8 @@ androidxUiAutomator = "2.2.0"
androidxWindowManager = "1.2.0"
androidxWork = "2.9.0"
coil = "3.0.0-alpha06"
-dependencyGuard = "0.4.3"
-firebaseBom = "32.4.0"
+dependencyGuard = "0.5.0"
+firebaseBom = "32.7.3"
firebaseCrashlyticsPlugin = "2.9.9"
firebasePerfPlugin = "1.4.2"
gmsPlugin = "4.4.1"
@@ -41,18 +41,19 @@ hilt = "2.51"
hiltExt = "1.1.0"
jacoco = "0.8.7"
junit4 = "4.13.2"
-kotlin = "1.9.22"
+kotlin = "1.9.23"
kotlinxCoroutines = "1.8.0"
kotlinxDatetime = "0.5.0"
kotlinxSerializationJson = "1.6.3"
-ksp = "1.9.22-1.0.18"
+ksp = "1.9.23-1.0.19"
+moduleGraph = "2.5.0"
okhttp = "4.12.0"
-protobuf = "3.25.2"
+protobuf = "3.25.3"
protobufPlugin = "0.9.4"
retrofit = "2.9.0"
retrofitKotlinxSerializationJson = "1.0.0"
robolectric = "4.11.1"
-roborazzi = "1.7.0"
+roborazzi = "1.10.1"
room = "2.6.1"
secrets = "2.0.1"
truth = "1.4.2"
@@ -68,7 +69,7 @@ androidx-core-ktx = "1.12.0"
androidx-espresso-core = "3.5.1"
androidx-material = "1.11.0"
androidx-test-junit = "1.1.5"
-compose = "1.6.0"
+compose = "1.6.4"
compose-plugin = "1.6.1"
junit = "4.13.2"
sqldelight = "2.0.1"
@@ -93,7 +94,9 @@ androidx-compose-foundation = { group = "androidx.compose.foundation", name = "f
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" }
androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
-androidx-compose-material3-adaptive = { group = "androidx.compose.material3", name = "material3-adaptive", version.ref = "androidxComposeMaterial3Adaptive" }
+androidx-compose-material3-adaptive = { group = "androidx.compose.material3.adaptive", name = "adaptive", version.ref = "androidxComposeMaterial3Adaptive" }
+androidx-compose-material3-adaptive-layout = { group = "androidx.compose.material3.adaptive", name = "adaptive-layout", version.ref = "androidxComposeMaterial3Adaptive" }
+androidx-compose-material3-adaptive-navigation = { group = "androidx.compose.material3.adaptive", name = "adaptive-navigation", version.ref = "androidxComposeMaterial3Adaptive" }
androidx-compose-material3-windowSizeClass = { group = "androidx.compose.material3", name = "material3-window-size-class" }
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
androidx-compose-runtime-tracing = { group = "androidx.compose.runtime", name = "runtime-tracing", version.ref = "androidxComposeRuntimeTracing" }
@@ -223,6 +226,7 @@ hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
+module-graph = { id = "com.jraska.module.graph.assertion", version.ref = "moduleGraph" }
protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" }
roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" }
room = { id = "androidx.room", version.ref = "room" }
diff --git a/lint/README.md b/lint/README.md
deleted file mode 100644
index 3eceb434b..000000000
--- a/lint/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# :lint module
-
-
diff --git a/settings.gradle.kts b/settings.gradle.kts
index e7b6d8b06..ea816dd1c 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -65,3 +65,4 @@ include(":lint")
include(":sync:work")
include(":sync:sync-test")
include(":ui-test-hilt-manifest")
+
diff --git a/sync/README.md b/sync/README.md
deleted file mode 100644
index b100e27ad..000000000
--- a/sync/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# :sync module
-
-
diff --git a/sync/sync-test/README.md b/sync/sync-test/README.md
new file mode 100644
index 000000000..78876290f
--- /dev/null
+++ b/sync/sync-test/README.md
@@ -0,0 +1,3 @@
+# :sync:sync-test module
+## Dependency graph
+
diff --git a/sync/work/README.md b/sync/work/README.md
new file mode 100644
index 000000000..2fe66d616
--- /dev/null
+++ b/sync/work/README.md
@@ -0,0 +1,3 @@
+# :sync:work module
+## Dependency graph
+