Merge branch 'android-remote-main' into optimize-ftsdatabase

Change-Id: I269d3a5a2fba1d33f519f0e8f195c42db48cc5f9

# Conflicts:
#	core/database/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt
pull/1268/head
Jaehwa Noh 8 months ago
commit db62cf6da6

@ -17,6 +17,8 @@
org.gradle.daemon=false org.gradle.daemon=false
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.workers.max=2 org.gradle.workers.max=2
org.gradle.configuration-cache=true
org.gradle.configuration-cache.parallel=true
kotlin.incremental=false kotlin.incremental=false

@ -7,6 +7,8 @@
"main" "main"
], ],
"gitIgnoredAuthors": [ "gitIgnoredAuthors": [
"renovate[bot]@users.noreply.github.com",
"github-actions[bot]@users.noreply.github.com",
"41898282+github-actions[bot]@users.noreply.github.com" "41898282+github-actions[bot]@users.noreply.github.com"
] ]
} }

@ -37,9 +37,11 @@ jobs:
- name: Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v4
with:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
- name: Check build-logic - name: Check build-logic
run: ./gradlew check -p build-logic run: ./gradlew :build-logic:convention:check
- name: Check spotless - name: Check spotless
run: ./gradlew spotlessCheck --init-script gradle/init.gradle.kts --no-configuration-cache run: ./gradlew spotlessCheck --init-script gradle/init.gradle.kts --no-configuration-cache
@ -186,6 +188,8 @@ jobs:
- name: Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v4
with:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
- name: Build projects and run instrumentation tests - name: Build projects and run instrumentation tests
uses: reactivecircus/android-emulator-runner@v2 uses: reactivecircus/android-emulator-runner@v2

@ -7,6 +7,7 @@ on:
jobs: jobs:
baseline_profiles: baseline_profiles:
name: "Generate Baseline Profiles" name: "Generate Baseline Profiles"
if: github.repository == 'android/nowinandroid'
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
@ -36,6 +37,8 @@ jobs:
- name: Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v4
with:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
- name: Setup Android SDK - name: Setup Android SDK
uses: android-actions/setup-android@v3 uses: android-actions/setup-android@v3
@ -44,7 +47,7 @@ jobs:
run: yes | sdkmanager --licenses || true run: yes | sdkmanager --licenses || true
- name: Check build-logic - name: Check build-logic
run: ./gradlew check -p build-logic run: ./gradlew :build-logic:convention:check
- name: Setup GMD - name: Setup GMD
run: ./gradlew :benchmarks:pixel6Api33Setup run: ./gradlew :benchmarks:pixel6Api33Setup
@ -52,9 +55,8 @@ jobs:
-Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true -Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true
-Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect"
# This generates both baseline and startup profile and adds them into the generated folder - name: Build all build type and flavor permutations including baseline profiles
- name: Generate Baseline Profile run: ./gradlew :app:assemble
run: ./gradlew :app:generateReleaseBaselineProfile -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=baselineprofile
-Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=baselineprofile -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect"
-Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" -Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true
--stacktrace

@ -7,6 +7,7 @@ on:
jobs: jobs:
build: build:
if: github.repository == 'android/nowinandroid'
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 120 timeout-minutes: 120
@ -33,6 +34,8 @@ jobs:
- name: Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v4
with:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
- name: Setup Android SDK - name: Setup Android SDK
uses: android-actions/setup-android@v3 uses: android-actions/setup-android@v3

@ -2,24 +2,24 @@ androidx.activity:activity-compose:1.9.3
androidx.activity:activity-ktx:1.9.3 androidx.activity:activity-ktx:1.9.3
androidx.activity:activity:1.9.3 androidx.activity:activity:1.9.3
androidx.annotation:annotation-experimental:1.4.1 androidx.annotation:annotation-experimental:1.4.1
androidx.annotation:annotation-jvm:1.8.1 androidx.annotation:annotation-jvm:1.9.1
androidx.annotation:annotation:1.8.1 androidx.annotation:annotation:1.9.1
androidx.appcompat:appcompat-resources:1.6.1 androidx.appcompat:appcompat-resources:1.6.1
androidx.arch.core:core-common:2.2.0 androidx.arch.core:core-common:2.2.0
androidx.arch.core:core-runtime:2.2.0 androidx.arch.core:core-runtime:2.2.0
androidx.autofill:autofill:1.0.0 androidx.autofill:autofill:1.0.0
androidx.browser:browser:1.8.0 androidx.browser:browser:1.8.0
androidx.collection:collection-jvm:1.4.4 androidx.collection:collection-jvm:1.5.0-beta01
androidx.collection:collection-ktx:1.4.4 androidx.collection:collection-ktx:1.5.0-beta01
androidx.collection:collection:1.4.4 androidx.collection:collection:1.5.0-beta01
androidx.compose.animation:animation-android:1.7.6 androidx.compose.animation:animation-android:1.8.0-alpha07
androidx.compose.animation:animation-core-android:1.7.6 androidx.compose.animation:animation-core-android:1.8.0-alpha07
androidx.compose.animation:animation-core:1.7.6 androidx.compose.animation:animation-core:1.8.0-alpha07
androidx.compose.animation:animation:1.7.6 androidx.compose.animation:animation:1.8.0-alpha07
androidx.compose.foundation:foundation-android:1.7.6 androidx.compose.foundation:foundation-android:1.8.0-alpha07
androidx.compose.foundation:foundation-layout-android:1.7.6 androidx.compose.foundation:foundation-layout-android:1.8.0-alpha07
androidx.compose.foundation:foundation-layout:1.7.6 androidx.compose.foundation:foundation-layout:1.8.0-alpha07
androidx.compose.foundation:foundation:1.7.6 androidx.compose.foundation:foundation:1.8.0-alpha07
androidx.compose.material3.adaptive:adaptive-android:1.0.0 androidx.compose.material3.adaptive:adaptive-android:1.0.0
androidx.compose.material3.adaptive:adaptive:1.0.0 androidx.compose.material3.adaptive:adaptive:1.0.0
androidx.compose.material3:material3-adaptive-navigation-suite-android:1.3.1 androidx.compose.material3:material3-adaptive-navigation-suite-android:1.3.1
@ -32,55 +32,55 @@ androidx.compose.material:material-icons-extended-android:1.7.6
androidx.compose.material:material-icons-extended:1.7.6 androidx.compose.material:material-icons-extended:1.7.6
androidx.compose.material:material-ripple-android:1.7.6 androidx.compose.material:material-ripple-android:1.7.6
androidx.compose.material:material-ripple:1.7.6 androidx.compose.material:material-ripple:1.7.6
androidx.compose.runtime:runtime-android:1.7.6 androidx.compose.runtime:runtime-android:1.8.0-alpha07
androidx.compose.runtime:runtime-saveable-android:1.7.6 androidx.compose.runtime:runtime-saveable-android:1.8.0-alpha07
androidx.compose.runtime:runtime-saveable:1.7.6 androidx.compose.runtime:runtime-saveable:1.8.0-alpha07
androidx.compose.runtime:runtime:1.7.6 androidx.compose.runtime:runtime:1.8.0-alpha07
androidx.compose.ui:ui-android:1.7.6 androidx.compose.ui:ui-android:1.8.0-alpha07
androidx.compose.ui:ui-geometry-android:1.7.6 androidx.compose.ui:ui-geometry-android:1.8.0-alpha07
androidx.compose.ui:ui-geometry:1.7.6 androidx.compose.ui:ui-geometry:1.8.0-alpha07
androidx.compose.ui:ui-graphics-android:1.7.6 androidx.compose.ui:ui-graphics-android:1.8.0-alpha07
androidx.compose.ui:ui-graphics:1.7.6 androidx.compose.ui:ui-graphics:1.8.0-alpha07
androidx.compose.ui:ui-text-android:1.7.6 androidx.compose.ui:ui-text-android:1.8.0-alpha07
androidx.compose.ui:ui-text:1.7.6 androidx.compose.ui:ui-text:1.8.0-alpha07
androidx.compose.ui:ui-tooling-preview-android:1.7.6 androidx.compose.ui:ui-tooling-preview-android:1.8.0-alpha07
androidx.compose.ui:ui-tooling-preview:1.7.6 androidx.compose.ui:ui-tooling-preview:1.8.0-alpha07
androidx.compose.ui:ui-unit-android:1.7.6 androidx.compose.ui:ui-unit-android:1.8.0-alpha07
androidx.compose.ui:ui-unit:1.7.6 androidx.compose.ui:ui-unit:1.8.0-alpha07
androidx.compose.ui:ui-util-android:1.7.6 androidx.compose.ui:ui-util-android:1.8.0-alpha07
androidx.compose.ui:ui-util:1.7.6 androidx.compose.ui:ui-util:1.8.0-alpha07
androidx.compose.ui:ui:1.7.6 androidx.compose.ui:ui:1.8.0-alpha07
androidx.compose:compose-bom:2024.12.01 androidx.compose:compose-bom:2024.12.01
androidx.concurrent:concurrent-futures:1.1.0 androidx.concurrent:concurrent-futures:1.1.0
androidx.core:core-ktx:1.13.1 androidx.core:core-ktx:1.13.1
androidx.core:core:1.13.1 androidx.core:core:1.13.1
androidx.customview:customview-poolingcontainer:1.0.0 androidx.customview:customview-poolingcontainer:1.0.0
androidx.customview:customview:1.0.0 androidx.customview:customview:1.0.0
androidx.emoji2:emoji2:1.3.0 androidx.emoji2:emoji2:1.4.0
androidx.exifinterface:exifinterface:1.3.7 androidx.exifinterface:exifinterface:1.3.7
androidx.fragment:fragment:1.5.1 androidx.fragment:fragment:1.5.1
androidx.graphics:graphics-path:1.0.1 androidx.graphics:graphics-path:1.0.1
androidx.interpolator:interpolator:1.0.0 androidx.interpolator:interpolator:1.0.0
androidx.lifecycle:lifecycle-common-java8:2.8.3 androidx.lifecycle:lifecycle-common-java8:2.8.7
androidx.lifecycle:lifecycle-common-jvm:2.8.3 androidx.lifecycle:lifecycle-common-jvm:2.8.7
androidx.lifecycle:lifecycle-common:2.8.3 androidx.lifecycle:lifecycle-common:2.8.7
androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.3 androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.7
androidx.lifecycle:lifecycle-livedata-core:2.8.3 androidx.lifecycle:lifecycle-livedata-core:2.8.7
androidx.lifecycle:lifecycle-livedata:2.8.3 androidx.lifecycle:lifecycle-livedata:2.8.7
androidx.lifecycle:lifecycle-process:2.8.3 androidx.lifecycle:lifecycle-process:2.8.7
androidx.lifecycle:lifecycle-runtime-android:2.8.3 androidx.lifecycle:lifecycle-runtime-android:2.8.7
androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3 androidx.lifecycle:lifecycle-runtime-compose-android:2.8.7
androidx.lifecycle:lifecycle-runtime-compose:2.8.3 androidx.lifecycle:lifecycle-runtime-compose:2.8.7
androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3 androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.7
androidx.lifecycle:lifecycle-runtime-ktx:2.8.3 androidx.lifecycle:lifecycle-runtime-ktx:2.8.7
androidx.lifecycle:lifecycle-runtime:2.8.3 androidx.lifecycle:lifecycle-runtime:2.8.7
androidx.lifecycle:lifecycle-viewmodel-android:2.8.3 androidx.lifecycle:lifecycle-viewmodel-android:2.8.7
androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3 androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7
androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3 androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.7
androidx.lifecycle:lifecycle-viewmodel:2.8.3 androidx.lifecycle:lifecycle-viewmodel:2.8.7
androidx.loader:loader:1.0.0 androidx.loader:loader:1.0.0
androidx.metrics:metrics-performance:1.0.0-beta01 androidx.metrics:metrics-performance:1.0.0-beta01
androidx.profileinstaller:profileinstaller:1.3.1 androidx.profileinstaller:profileinstaller:1.4.0
androidx.savedstate:savedstate-ktx:1.2.1 androidx.savedstate:savedstate-ktx:1.2.1
androidx.savedstate:savedstate:1.2.1 androidx.savedstate:savedstate:1.2.1
androidx.startup:startup-runtime:1.1.1 androidx.startup:startup-runtime:1.1.1
@ -96,10 +96,10 @@ androidx.window:window-core:1.3.0
androidx.window:window:1.3.0 androidx.window:window:1.3.0
com.google.accompanist:accompanist-drawablepainter:0.32.0 com.google.accompanist:accompanist-drawablepainter:0.32.0
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger-lint-aar:2.53.1 com.google.dagger:dagger-lint-aar:2.54
com.google.dagger:dagger:2.53.1 com.google.dagger:dagger:2.54
com.google.dagger:hilt-android:2.53.1 com.google.dagger:hilt-android:2.54
com.google.dagger:hilt-core:2.53.1 com.google.dagger:hilt-core:2.54
com.google.guava:listenablefuture:1.0 com.google.guava:listenablefuture:1.0
com.squareup.okhttp3:okhttp:4.12.0 com.squareup.okhttp3:okhttp:4.12.0
com.squareup.okio:okio-jvm:3.9.0 com.squareup.okio:okio-jvm:3.9.0

@ -116,15 +116,16 @@ dependencies {
testImplementation(projects.sync.syncTest) testImplementation(projects.sync.syncTest)
testImplementation(libs.kotlin.test) testImplementation(libs.kotlin.test)
testDemoImplementation(libs.androidx.navigation.testing)
testDemoImplementation(libs.robolectric) testDemoImplementation(libs.robolectric)
testDemoImplementation(libs.roborazzi) testDemoImplementation(libs.roborazzi)
testDemoImplementation(projects.core.screenshotTesting) testDemoImplementation(projects.core.screenshotTesting)
testDemoImplementation(projects.core.testing)
androidTestImplementation(projects.core.testing) androidTestImplementation(projects.core.testing)
androidTestImplementation(projects.core.dataTest) androidTestImplementation(projects.core.dataTest)
androidTestImplementation(projects.core.datastoreTest) androidTestImplementation(projects.core.datastoreTest)
androidTestImplementation(libs.androidx.test.espresso.core) androidTestImplementation(libs.androidx.test.espresso.core)
androidTestImplementation(libs.androidx.navigation.testing)
androidTestImplementation(libs.androidx.compose.ui.test) androidTestImplementation(libs.androidx.compose.ui.test)
androidTestImplementation(libs.hilt.android.testing) androidTestImplementation(libs.hilt.android.testing)
androidTestImplementation(libs.kotlin.test) androidTestImplementation(libs.kotlin.test)

@ -2,25 +2,25 @@ androidx.activity:activity-compose:1.9.3
androidx.activity:activity-ktx:1.9.3 androidx.activity:activity-ktx:1.9.3
androidx.activity:activity:1.9.3 androidx.activity:activity:1.9.3
androidx.annotation:annotation-experimental:1.4.1 androidx.annotation:annotation-experimental:1.4.1
androidx.annotation:annotation-jvm:1.8.1 androidx.annotation:annotation-jvm:1.9.1
androidx.annotation:annotation:1.8.1 androidx.annotation:annotation:1.9.1
androidx.appcompat:appcompat-resources:1.7.0 androidx.appcompat:appcompat-resources:1.7.0
androidx.appcompat:appcompat:1.7.0 androidx.appcompat:appcompat:1.7.0
androidx.arch.core:core-common:2.2.0 androidx.arch.core:core-common:2.2.0
androidx.arch.core:core-runtime:2.2.0 androidx.arch.core:core-runtime:2.2.0
androidx.autofill:autofill:1.0.0 androidx.autofill:autofill:1.0.0
androidx.browser:browser:1.8.0 androidx.browser:browser:1.8.0
androidx.collection:collection-jvm:1.4.4 androidx.collection:collection-jvm:1.5.0-beta01
androidx.collection:collection-ktx:1.4.4 androidx.collection:collection-ktx:1.5.0-beta01
androidx.collection:collection:1.4.4 androidx.collection:collection:1.5.0-beta01
androidx.compose.animation:animation-android:1.7.6 androidx.compose.animation:animation-android:1.8.0-alpha07
androidx.compose.animation:animation-core-android:1.7.6 androidx.compose.animation:animation-core-android:1.8.0-alpha07
androidx.compose.animation:animation-core:1.7.6 androidx.compose.animation:animation-core:1.8.0-alpha07
androidx.compose.animation:animation:1.7.6 androidx.compose.animation:animation:1.8.0-alpha07
androidx.compose.foundation:foundation-android:1.7.6 androidx.compose.foundation:foundation-android:1.8.0-alpha07
androidx.compose.foundation:foundation-layout-android:1.7.6 androidx.compose.foundation:foundation-layout-android:1.8.0-alpha07
androidx.compose.foundation:foundation-layout:1.7.6 androidx.compose.foundation:foundation-layout:1.8.0-alpha07
androidx.compose.foundation:foundation:1.7.6 androidx.compose.foundation:foundation:1.8.0-alpha07
androidx.compose.material3.adaptive:adaptive-android:1.0.0 androidx.compose.material3.adaptive:adaptive-android:1.0.0
androidx.compose.material3.adaptive:adaptive-layout-android:1.0.0 androidx.compose.material3.adaptive:adaptive-layout-android:1.0.0
androidx.compose.material3.adaptive:adaptive-layout:1.0.0 androidx.compose.material3.adaptive:adaptive-layout:1.0.0
@ -39,25 +39,25 @@ androidx.compose.material:material-icons-extended-android:1.7.6
androidx.compose.material:material-icons-extended:1.7.6 androidx.compose.material:material-icons-extended:1.7.6
androidx.compose.material:material-ripple-android:1.7.6 androidx.compose.material:material-ripple-android:1.7.6
androidx.compose.material:material-ripple:1.7.6 androidx.compose.material:material-ripple:1.7.6
androidx.compose.runtime:runtime-android:1.7.6 androidx.compose.runtime:runtime-android:1.8.0-alpha07
androidx.compose.runtime:runtime-saveable-android:1.7.6 androidx.compose.runtime:runtime-saveable-android:1.8.0-alpha07
androidx.compose.runtime:runtime-saveable:1.7.6 androidx.compose.runtime:runtime-saveable:1.8.0-alpha07
androidx.compose.runtime:runtime-tracing:1.7.6 androidx.compose.runtime:runtime-tracing:1.8.0-alpha07
androidx.compose.runtime:runtime:1.7.6 androidx.compose.runtime:runtime:1.8.0-alpha07
androidx.compose.ui:ui-android:1.7.6 androidx.compose.ui:ui-android:1.8.0-alpha07
androidx.compose.ui:ui-geometry-android:1.7.6 androidx.compose.ui:ui-geometry-android:1.8.0-alpha07
androidx.compose.ui:ui-geometry:1.7.6 androidx.compose.ui:ui-geometry:1.8.0-alpha07
androidx.compose.ui:ui-graphics-android:1.7.6 androidx.compose.ui:ui-graphics-android:1.8.0-alpha07
androidx.compose.ui:ui-graphics:1.7.6 androidx.compose.ui:ui-graphics:1.8.0-alpha07
androidx.compose.ui:ui-text-android:1.7.6 androidx.compose.ui:ui-text-android:1.8.0-alpha07
androidx.compose.ui:ui-text:1.7.6 androidx.compose.ui:ui-text:1.8.0-alpha07
androidx.compose.ui:ui-tooling-preview-android:1.7.6 androidx.compose.ui:ui-tooling-preview-android:1.8.0-alpha07
androidx.compose.ui:ui-tooling-preview:1.7.6 androidx.compose.ui:ui-tooling-preview:1.8.0-alpha07
androidx.compose.ui:ui-unit-android:1.7.6 androidx.compose.ui:ui-unit-android:1.8.0-alpha07
androidx.compose.ui:ui-unit:1.7.6 androidx.compose.ui:ui-unit:1.8.0-alpha07
androidx.compose.ui:ui-util-android:1.7.6 androidx.compose.ui:ui-util-android:1.8.0-alpha07
androidx.compose.ui:ui-util:1.7.6 androidx.compose.ui:ui-util:1.8.0-alpha07
androidx.compose.ui:ui:1.7.6 androidx.compose.ui:ui:1.8.0-alpha07
androidx.compose:compose-bom:2024.12.01 androidx.compose:compose-bom:2024.12.01
androidx.concurrent:concurrent-futures-ktx:1.1.0 androidx.concurrent:concurrent-futures-ktx:1.1.0
androidx.concurrent:concurrent-futures:1.1.0 androidx.concurrent:concurrent-futures:1.1.0
@ -79,8 +79,8 @@ androidx.datastore:datastore-preferences:1.1.1
androidx.datastore:datastore:1.1.1 androidx.datastore:datastore:1.1.1
androidx.documentfile:documentfile:1.0.0 androidx.documentfile:documentfile:1.0.0
androidx.drawerlayout:drawerlayout:1.0.0 androidx.drawerlayout:drawerlayout:1.0.0
androidx.emoji2:emoji2-views-helper:1.3.0 androidx.emoji2:emoji2-views-helper:1.4.0
androidx.emoji2:emoji2:1.3.0 androidx.emoji2:emoji2:1.4.0
androidx.exifinterface:exifinterface:1.3.7 androidx.exifinterface:exifinterface:1.3.7
androidx.fragment:fragment:1.5.4 androidx.fragment:fragment:1.5.4
androidx.graphics:graphics-path:1.0.1 androidx.graphics:graphics-path:1.0.1
@ -164,10 +164,10 @@ com.google.android.gms:play-services-oss-licenses:17.1.0
com.google.android.gms:play-services-stats:17.0.2 com.google.android.gms:play-services-stats:17.0.2
com.google.android.gms:play-services-tasks:18.2.0 com.google.android.gms:play-services-tasks:18.2.0
com.google.code.findbugs:jsr305:3.0.2 com.google.code.findbugs:jsr305:3.0.2
com.google.dagger:dagger-lint-aar:2.53.1 com.google.dagger:dagger-lint-aar:2.54
com.google.dagger:dagger:2.53.1 com.google.dagger:dagger:2.54
com.google.dagger:hilt-android:2.53.1 com.google.dagger:hilt-android:2.54
com.google.dagger:hilt-core:2.53.1 com.google.dagger:hilt-core:2.54
com.google.errorprone:error_prone_annotations:2.26.0 com.google.errorprone:error_prone_annotations:2.26.0
com.google.firebase:firebase-abt:21.1.1 com.google.firebase:firebase-abt:21.1.1
com.google.firebase:firebase-analytics:22.1.2 com.google.firebase:firebase-analytics:22.1.2
@ -216,18 +216,18 @@ org.jetbrains.kotlin:kotlin-stdlib-common:2.1.0
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0
org.jetbrains.kotlin:kotlin-stdlib:2.1.0 org.jetbrains.kotlin:kotlin-stdlib:2.1.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.1
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.1
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.1
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1
org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.10.1
org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.10.1
org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.1 org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.1
org.jetbrains.kotlinx:kotlinx-datetime:0.6.1 org.jetbrains.kotlinx:kotlinx-datetime:0.6.1
org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-bom:1.8.0
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.8.0
org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-core:1.8.0
org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.8.0
org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0
org.jetbrains:annotations:23.0.0 org.jetbrains:annotations:23.0.0
org.jspecify:jspecify:1.0.0 org.jspecify:jspecify:1.0.0

@ -21,7 +21,9 @@
<style name="NightAdjusted.Theme.Nia" parent="android:Theme.Material.Light.NoActionBar" /> <style name="NightAdjusted.Theme.Nia" parent="android:Theme.Material.Light.NoActionBar" />
<!-- The final theme we use --> <!-- The final theme we use -->
<style name="Theme.Nia" parent="NightAdjusted.Theme.Nia" /> <style name="Theme.Nia" parent="NightAdjusted.Theme.Nia">
<item name="android:forceDarkAllowed" tools:targetApi="29">false</item>
</style>
<style name="NightAdjusted.Theme.Splash" parent="Theme.SplashScreen"> <style name="NightAdjusted.Theme.Splash" parent="Theme.SplashScreen">
<item name="android:windowLightStatusBar" tools:targetApi="23">true</item> <item name="android:windowLightStatusBar" tools:targetApi="23">true</item>

@ -31,6 +31,8 @@ import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepo
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
import com.google.samples.apps.nowinandroid.core.testing.util.TestNetworkMonitor import com.google.samples.apps.nowinandroid.core.testing.util.TestNetworkMonitor
import com.google.samples.apps.nowinandroid.core.testing.util.TestTimeZoneMonitor import com.google.samples.apps.nowinandroid.core.testing.util.TestTimeZoneMonitor
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.HiltTestApplication
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.UnconfinedTestDispatcher
@ -38,15 +40,18 @@ import kotlinx.coroutines.test.runTest
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
/** /**
* Tests [NiaAppState]. * Tests [NiaAppState].
*
* Note: This could become an unit test if Robolectric is added to the project and the Context
* is faked.
*/ */
@RunWith(RobolectricTestRunner::class)
@Config(application = HiltTestApplication::class)
@HiltAndroidTest
class NiaAppStateTest { class NiaAppStateTest {
@get:Rule @get:Rule

@ -23,18 +23,17 @@ import com.google.samples.apps.nowinandroid.configureKotlinAndroid
import com.google.samples.apps.nowinandroid.configurePrintApksTask import com.google.samples.apps.nowinandroid.configurePrintApksTask
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.getByType
class AndroidApplicationConventionPlugin : Plugin<Project> { class AndroidApplicationConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
with(pluginManager) { apply(plugin = "com.android.application")
apply("com.android.application") apply(plugin = "org.jetbrains.kotlin.android")
apply("org.jetbrains.kotlin.android") apply(plugin = "nowinandroid.android.lint")
apply("nowinandroid.android.lint") apply(plugin = "com.dropbox.dependency-guard")
apply("com.dropbox.dependency-guard")
}
extensions.configure<ApplicationExtension> { extensions.configure<ApplicationExtension> {
configureKotlinAndroid(this) configureKotlinAndroid(this)
@ -49,5 +48,4 @@ class AndroidApplicationConventionPlugin : Plugin<Project> {
} }
} }
} }
} }

@ -19,6 +19,7 @@ import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension
import com.google.samples.apps.nowinandroid.libs import com.google.samples.apps.nowinandroid.libs
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.exclude import org.gradle.kotlin.dsl.exclude
@ -26,11 +27,9 @@ import org.gradle.kotlin.dsl.exclude
class AndroidApplicationFirebaseConventionPlugin : Plugin<Project> { class AndroidApplicationFirebaseConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
with(pluginManager) { apply(plugin = "com.google.gms.google-services")
apply("com.google.gms.google-services") apply(plugin = "com.google.firebase.firebase-perf")
apply("com.google.firebase.firebase-perf") apply(plugin = "com.google.firebase.crashlytics")
apply("com.google.firebase.crashlytics")
}
dependencies { dependencies {
val bom = libs.findLibrary("firebase-bom").get() val bom = libs.findLibrary("firebase-bom").get()

@ -19,12 +19,14 @@ import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.google.samples.apps.nowinandroid.configureJacoco import com.google.samples.apps.nowinandroid.configureJacoco
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.getByType
class AndroidApplicationJacocoConventionPlugin : Plugin<Project> { class AndroidApplicationJacocoConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
pluginManager.apply("jacoco") apply(plugin = "jacoco")
val androidExtension = extensions.getByType<ApplicationExtension>() val androidExtension = extensions.getByType<ApplicationExtension>()
androidExtension.buildTypes.configureEach { androidExtension.buildTypes.configureEach {

@ -19,17 +19,17 @@ import com.google.samples.apps.nowinandroid.configureGradleManagedDevices
import com.google.samples.apps.nowinandroid.libs import com.google.samples.apps.nowinandroid.libs
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.dependencies
class AndroidFeatureConventionPlugin : Plugin<Project> { class AndroidFeatureConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
pluginManager.apply { apply(plugin = "nowinandroid.android.library")
apply("nowinandroid.android.library") apply(plugin = "nowinandroid.hilt")
apply("nowinandroid.hilt") apply(plugin = "org.jetbrains.kotlin.plugin.serialization")
apply("org.jetbrains.kotlin.plugin.serialization")
}
extensions.configure<LibraryExtension> { extensions.configure<LibraryExtension> {
testOptions.animationsDisabled = true testOptions.animationsDisabled = true
configureGradleManagedDevices(this) configureGradleManagedDevices(this)
@ -47,7 +47,9 @@ class AndroidFeatureConventionPlugin : Plugin<Project> {
"implementation"(libs.findLibrary("kotlinx.serialization.json").get()) "implementation"(libs.findLibrary("kotlinx.serialization.json").get())
"testImplementation"(libs.findLibrary("androidx.navigation.testing").get()) "testImplementation"(libs.findLibrary("androidx.navigation.testing").get())
"androidTestImplementation"(libs.findLibrary("androidx.lifecycle.runtimeTesting").get()) "androidTestImplementation"(
libs.findLibrary("androidx.lifecycle.runtimeTesting").get(),
)
} }
} }
} }

@ -24,17 +24,16 @@ import com.google.samples.apps.nowinandroid.disableUnnecessaryAndroidTests
import com.google.samples.apps.nowinandroid.libs import com.google.samples.apps.nowinandroid.libs
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.dependencies
class AndroidLibraryConventionPlugin : Plugin<Project> { class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
with(pluginManager) { apply(plugin = "com.android.library")
apply("com.android.library") apply(plugin = "org.jetbrains.kotlin.android")
apply("org.jetbrains.kotlin.android") apply(plugin = "nowinandroid.android.lint")
apply("nowinandroid.android.lint")
}
extensions.configure<LibraryExtension> { extensions.configure<LibraryExtension> {
configureKotlinAndroid(this) configureKotlinAndroid(this)
@ -45,7 +44,9 @@ class AndroidLibraryConventionPlugin : Plugin<Project> {
configureGradleManagedDevices(this) configureGradleManagedDevices(this)
// The resource prefix is derived from the module name, // The resource prefix is derived from the module name,
// so resources inside ":core:module1" must be prefixed with "core_module1_" // so resources inside ":core:module1" must be prefixed with "core_module1_"
resourcePrefix = path.split("""\W""".toRegex()).drop(1).distinct().joinToString(separator = "_").lowercase() + "_" resourcePrefix =
path.split("""\W""".toRegex()).drop(1).distinct().joinToString(separator = "_")
.lowercase() + "_"
} }
extensions.configure<LibraryAndroidComponentsExtension> { extensions.configure<LibraryAndroidComponentsExtension> {
configurePrintApksTask(this) configurePrintApksTask(this)

@ -15,17 +15,18 @@
*/ */
import com.android.build.api.dsl.LibraryExtension import com.android.build.api.dsl.LibraryExtension
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.api.variant.LibraryAndroidComponentsExtension import com.android.build.api.variant.LibraryAndroidComponentsExtension
import com.google.samples.apps.nowinandroid.configureJacoco import com.google.samples.apps.nowinandroid.configureJacoco
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.getByType
class AndroidLibraryJacocoConventionPlugin : Plugin<Project> { class AndroidLibraryJacocoConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
pluginManager.apply("jacoco") apply(plugin = "jacoco")
val androidExtension = extensions.getByType<LibraryExtension>() val androidExtension = extensions.getByType<LibraryExtension>()
androidExtension.buildTypes.configureEach { androidExtension.buildTypes.configureEach {

@ -19,6 +19,7 @@ import com.android.build.api.dsl.LibraryExtension
import com.android.build.api.dsl.Lint import com.android.build.api.dsl.Lint
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.configure
class AndroidLintConventionPlugin : Plugin<Project> { class AndroidLintConventionPlugin : Plugin<Project> {
@ -32,7 +33,7 @@ class AndroidLintConventionPlugin : Plugin<Project> {
configure<LibraryExtension> { lint(Lint::configure) } configure<LibraryExtension> { lint(Lint::configure) }
else -> { else -> {
pluginManager.apply("com.android.lint") apply(plugin = "com.android.lint")
configure<Lint>(Lint::configure) configure<Lint>(Lint::configure)
} }
} }

@ -19,6 +19,7 @@ import com.google.devtools.ksp.gradle.KspExtension
import com.google.samples.apps.nowinandroid.libs import com.google.samples.apps.nowinandroid.libs
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.dependencies
@ -26,8 +27,8 @@ class AndroidRoomConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
pluginManager.apply("androidx.room") apply(plugin = "androidx.room")
pluginManager.apply("com.google.devtools.ksp") apply(plugin = "com.google.devtools.ksp")
extensions.configure<KspExtension> { extensions.configure<KspExtension> {
arg("room.generateKotlin", "true") arg("room.generateKotlin", "true")

@ -19,15 +19,14 @@ import com.google.samples.apps.nowinandroid.configureGradleManagedDevices
import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configureKotlinAndroid
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.configure
class AndroidTestConventionPlugin : Plugin<Project> { class AndroidTestConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
with(pluginManager) { apply(plugin = "com.android.test")
apply("com.android.test") apply(plugin = "org.jetbrains.kotlin.android")
apply("org.jetbrains.kotlin.android")
}
extensions.configure<TestExtension> { extensions.configure<TestExtension> {
configureKotlinAndroid(this) configureKotlinAndroid(this)
@ -36,5 +35,4 @@ class AndroidTestConventionPlugin : Plugin<Project> {
} }
} }
} }
} }

@ -18,12 +18,14 @@ import com.android.build.gradle.api.AndroidBasePlugin
import com.google.samples.apps.nowinandroid.libs import com.google.samples.apps.nowinandroid.libs
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.dependencies
class HiltConventionPlugin : Plugin<Project> { class HiltConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
pluginManager.apply("com.google.devtools.ksp") apply(plugin = "com.google.devtools.ksp")
dependencies { dependencies {
"ksp"(libs.findLibrary("hilt.compiler").get()) "ksp"(libs.findLibrary("hilt.compiler").get())
} }
@ -37,7 +39,7 @@ class HiltConventionPlugin : Plugin<Project> {
/** Add support for Android modules, based on [AndroidBasePlugin] */ /** Add support for Android modules, based on [AndroidBasePlugin] */
pluginManager.withPlugin("com.android.base") { pluginManager.withPlugin("com.android.base") {
pluginManager.apply("dagger.hilt.android.plugin") apply(plugin = "dagger.hilt.android.plugin")
dependencies { dependencies {
"implementation"(libs.findLibrary("hilt.android").get()) "implementation"(libs.findLibrary("hilt.android").get())
} }

@ -18,15 +18,15 @@ import com.google.samples.apps.nowinandroid.configureKotlinJvm
import com.google.samples.apps.nowinandroid.libs import com.google.samples.apps.nowinandroid.libs
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.dependencies
class JvmLibraryConventionPlugin : Plugin<Project> { class JvmLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
with(pluginManager) { apply(plugin = "org.jetbrains.kotlin.jvm")
apply("org.jetbrains.kotlin.jvm") apply(plugin = "nowinandroid.android.lint")
apply("nowinandroid.android.lint")
}
configureKotlinJvm() configureKotlinJvm()
dependencies { dependencies {
"testImplementation"(libs.findLibrary("kotlin.test").get()) "testImplementation"(libs.findLibrary("kotlin.test").get())

@ -2,3 +2,5 @@
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.caching=true org.gradle.caching=true
org.gradle.configureondemand=true org.gradle.configureondemand=true
org.gradle.configuration-cache=true
org.gradle.configuration-cache.parallel=true

@ -25,7 +25,7 @@ buildscript {
} }
mavenCentral() mavenCentral()
// Android Build Server // This is used only for internal Google builds.
maven { url = uri("../nowinandroid-prebuilts/m2repository") } maven { url = uri("../nowinandroid-prebuilts/m2repository") }
} }
dependencies { dependencies {

@ -89,6 +89,7 @@ class OfflineFirstNewsRepositoryTest {
@Test @Test
fun offlineFirstNewsRepository_news_resources_stream_is_backed_by_news_resource_dao() = fun offlineFirstNewsRepository_news_resources_stream_is_backed_by_news_resource_dao() =
testScope.runTest { testScope.runTest {
subject.syncWith(synchronizer)
assertEquals( assertEquals(
newsResourceDao.getNewsResources() newsResourceDao.getNewsResources()
.first() .first()

@ -67,6 +67,8 @@ class OfflineFirstTopicsRepositoryTest {
@Test @Test
fun offlineFirstTopicsRepository_topics_stream_is_backed_by_topics_dao() = fun offlineFirstTopicsRepository_topics_stream_is_backed_by_topics_dao() =
testScope.runTest { testScope.runTest {
subject.syncWith(synchronizer)
assertEquals( assertEquals(
topicDao.getTopicEntities() topicDao.getTopicEntities()
.first() .first()

@ -0,0 +1,47 @@
/*
* Copyright 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.database.dao
import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import com.google.samples.apps.nowinandroid.core.database.NiaDatabase
import org.junit.After
import org.junit.Before
internal abstract class DatabaseTest {
private lateinit var db: NiaDatabase
protected lateinit var newsResourceDao: NewsResourceDao
protected lateinit var topicDao: TopicDao
@Before
fun setup() {
db = run {
val context = ApplicationProvider.getApplicationContext<Context>()
Room.inMemoryDatabaseBuilder(
context,
NiaDatabase::class.java,
).build()
}
newsResourceDao = db.newsResourceDao()
topicDao = db.topicDao()
}
@After
fun teardown() = db.close()
}

@ -16,10 +16,6 @@
package com.google.samples.apps.nowinandroid.core.database.dao package com.google.samples.apps.nowinandroid.core.database.dao
import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import com.google.samples.apps.nowinandroid.core.database.NiaDatabase
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
@ -27,36 +23,13 @@ import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import org.junit.After
import org.junit.Before
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
/** internal class NewsResourceDaoTest : DatabaseTest() {
* Instrumentation tests for [NewsResourceDao].
*/
class NewsResourceDaoTest {
private lateinit var newsResourceDao: NewsResourceDao
private lateinit var topicDao: TopicDao
private lateinit var db: NiaDatabase
@Before
fun createDb() {
val context = ApplicationProvider.getApplicationContext<Context>()
db = Room.inMemoryDatabaseBuilder(
context,
NiaDatabase::class.java,
).build()
newsResourceDao = db.newsResourceDao()
topicDao = db.topicDao()
}
@After
fun closeDb() = db.close()
@Test @Test
fun newsResourceDao_fetches_items_by_descending_publish_date() = runTest { fun getNewsResources_allEntries_areOrderedByPublishDateDesc() = runTest {
val newsResourceEntities = listOf( val newsResourceEntities = listOf(
testNewsResource( testNewsResource(
id = "0", id = "0",
@ -91,7 +64,7 @@ class NewsResourceDaoTest {
} }
@Test @Test
fun newsResourceDao_filters_items_by_news_ids_by_descending_publish_date() = runTest { fun getNewsResources_filteredById_areOrderedByDescendingPublishDate() = runTest {
val newsResourceEntities = listOf( val newsResourceEntities = listOf(
testNewsResource( testNewsResource(
id = "0", id = "0",
@ -129,7 +102,7 @@ class NewsResourceDaoTest {
} }
@Test @Test
fun newsResourceDao_filters_items_by_topic_ids_by_descending_publish_date() = runTest { fun getNewsResources_filteredByTopicId_areOrderedByDescendingPublishDate() = runTest {
val topicEntities = listOf( val topicEntities = listOf(
testTopicEntity( testTopicEntity(
id = "1", id = "1",
@ -189,7 +162,7 @@ class NewsResourceDaoTest {
} }
@Test @Test
fun newsResourceDao_filters_items_by_news_and_topic_ids_by_descending_publish_date() = runTest { fun getNewsResources_filteredByIdAndTopicId_areOrderedByDescendingPublishDate() = runTest {
val topicEntities = listOf( val topicEntities = listOf(
testTopicEntity( testTopicEntity(
id = "1", id = "1",
@ -251,7 +224,7 @@ class NewsResourceDaoTest {
} }
@Test @Test
fun newsResourceDao_deletes_items_by_ids() = fun deleteNewsResources_byId() =
runTest { runTest {
val newsResourceEntities = listOf( val newsResourceEntities = listOf(
testNewsResource( testNewsResource(

@ -0,0 +1,126 @@
/*
* 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.core.database.dao
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import org.junit.Test
import kotlin.test.assertEquals
internal class TopicDaoTest : DatabaseTest() {
@Test
fun getTopics() = runTest {
insertTopics()
val savedTopics = topicDao.getTopicEntities().first()
assertEquals(
listOf("1", "2", "3"),
savedTopics.map { it.id },
)
}
@Test
fun getTopic() = runTest {
insertTopics()
val savedTopicEntity = topicDao.getTopicEntity("2").first()
assertEquals("performance", savedTopicEntity.name)
}
@Test
fun getTopics_oneOff() = runTest {
insertTopics()
val savedTopics = topicDao.getOneOffTopicEntities()
assertEquals(
listOf("1", "2", "3"),
savedTopics.map { it.id },
)
}
@Test
fun getTopics_byId() = runTest {
insertTopics()
val savedTopics = topicDao.getTopicEntities(setOf("1", "2"))
.first()
assertEquals(listOf("compose", "performance"), savedTopics.map { it.name })
}
@Test
fun insertTopic_newEntryIsIgnoredIfAlreadyExists() = runTest {
insertTopics()
topicDao.insertOrIgnoreTopics(
listOf(testTopicEntity("1", "compose")),
)
val savedTopics = topicDao.getOneOffTopicEntities()
assertEquals(3, savedTopics.size)
}
@Test
fun upsertTopic_existingEntryIsUpdated() = runTest {
insertTopics()
topicDao.upsertTopics(
listOf(testTopicEntity("1", "newName")),
)
val savedTopics = topicDao.getOneOffTopicEntities()
assertEquals(3, savedTopics.size)
assertEquals("newName", savedTopics.first().name)
}
@Test
fun deleteTopics_byId_existingEntriesAreDeleted() = runTest {
insertTopics()
topicDao.deleteTopics(listOf("1", "2"))
val savedTopics = topicDao.getOneOffTopicEntities()
assertEquals(1, savedTopics.size)
assertEquals("3", savedTopics.first().id)
}
private suspend fun insertTopics() {
val topicEntities = listOf(
testTopicEntity("1", "compose"),
testTopicEntity("2", "performance"),
testTopicEntity("3", "headline"),
)
topicDao.insertOrIgnoreTopics(topicEntities)
}
}
private fun testTopicEntity(
id: String = "0",
name: String,
) = TopicEntity(
id = id,
name = name,
shortDescription = "",
longDescription = "",
url = "",
imageUrl = "",
)

@ -17,6 +17,8 @@
package com.google.samples.apps.nowinandroid.core.network.demo package com.google.samples.apps.nowinandroid.core.network.demo
import JvmUnitTestDemoAssetManager import JvmUnitTestDemoAssetManager
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.M
import com.google.samples.apps.nowinandroid.core.network.Dispatcher 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.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
@ -28,6 +30,7 @@ import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
import java.io.BufferedReader
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -39,17 +42,11 @@ class DemoNiaNetworkDataSource @Inject constructor(
private val assets: DemoAssetManager = JvmUnitTestDemoAssetManager, private val assets: DemoAssetManager = JvmUnitTestDemoAssetManager,
) : NiaNetworkDataSource { ) : NiaNetworkDataSource {
@OptIn(ExperimentalSerializationApi::class)
override suspend fun getTopics(ids: List<String>?): List<NetworkTopic> = override suspend fun getTopics(ids: List<String>?): List<NetworkTopic> =
withContext(ioDispatcher) { getDataFromJsonFile(TOPICS_ASSET)
assets.open(TOPICS_ASSET).use(networkJson::decodeFromStream)
}
@OptIn(ExperimentalSerializationApi::class)
override suspend fun getNewsResources(ids: List<String>?): List<NetworkNewsResource> = override suspend fun getNewsResources(ids: List<String>?): List<NetworkNewsResource> =
withContext(ioDispatcher) { getDataFromJsonFile(NEWS_ASSET)
assets.open(NEWS_ASSET).use(networkJson::decodeFromStream)
}
override suspend fun getTopicChangeList(after: Int?): List<NetworkChangeList> = override suspend fun getTopicChangeList(after: Int?): List<NetworkChangeList> =
getTopics().mapToChangeList(NetworkTopic::id) getTopics().mapToChangeList(NetworkTopic::id)
@ -57,6 +54,27 @@ class DemoNiaNetworkDataSource @Inject constructor(
override suspend fun getNewsResourceChangeList(after: Int?): List<NetworkChangeList> = override suspend fun getNewsResourceChangeList(after: Int?): List<NetworkChangeList> =
getNewsResources().mapToChangeList(NetworkNewsResource::id) getNewsResources().mapToChangeList(NetworkNewsResource::id)
/**
* Get data from the given JSON [fileName].
*/
@OptIn(ExperimentalSerializationApi::class)
private suspend inline fun <reified T> getDataFromJsonFile(fileName: String): List<T> =
withContext(ioDispatcher) {
assets.open(fileName).use { inputStream ->
if (SDK_INT <= M) {
/**
* On API 23 (M) and below we must use a workaround to avoid an exception being
* thrown during deserialization. See:
* https://github.com/Kotlin/kotlinx.serialization/issues/2457#issuecomment-1786923342
*/
inputStream.bufferedReader().use(BufferedReader::readText)
.let(networkJson::decodeFromString)
} else {
networkJson.decodeFromStream(inputStream)
}
}
}
companion object { companion object {
private const val NEWS_ASSET = "news.json" private const val NEWS_ASSET = "news.json"
private const val TOPICS_ASSET = "topics.json" private const val TOPICS_ASSET = "topics.json"

@ -24,7 +24,6 @@ import androidx.compose.foundation.Canvas
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.draganddrop.dragAndDropSource import androidx.compose.foundation.draganddrop.dragAndDropSource
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@ -139,19 +138,13 @@ fun NewsResourceCardExpanded(
userNewsResource.title, userNewsResource.title,
modifier = Modifier modifier = Modifier
.fillMaxWidth((.8f)) .fillMaxWidth((.8f))
.dragAndDropSource { .dragAndDropSource { _ ->
detectTapGestures( DragAndDropTransferData(
onLongPress = { ClipData.newPlainText(
startTransfer( sharingLabel,
DragAndDropTransferData( sharingContent,
ClipData.newPlainText( ),
sharingLabel, flags = dragAndDropFlags,
sharingContent,
),
flags = dragAndDropFlags,
),
)
},
) )
}, },
) )

@ -39,6 +39,7 @@ org.gradle.caching=true
# Enable configuration caching between builds. # Enable configuration caching between builds.
org.gradle.configuration-cache=true org.gradle.configuration-cache=true
org.gradle.configuration-cache.parallel=true
# This option is set because of https://github.com/google/play-services-plugins/issues/246 # This option is set because of https://github.com/google/play-services-plugins/issues/246
# to generate the Configuration Cache regardless of incompatible tasks. # to generate the Configuration Cache regardless of incompatible tasks.
# See https://github.com/android/nowinandroid/issues/1022 before using it. # See https://github.com/android/nowinandroid/issues/1022 before using it.

@ -1,6 +1,6 @@
[versions] [versions]
accompanist = "0.37.0" accompanist = "0.37.0"
androidDesugarJdkLibs = "2.1.3" androidDesugarJdkLibs = "2.1.4"
# AGP and tools should be updated together # AGP and tools should be updated together
androidGradlePlugin = "8.7.3" androidGradlePlugin = "8.7.3"
androidTools = "31.7.3" androidTools = "31.7.3"
@ -8,6 +8,7 @@ androidxActivity = "1.9.3"
androidxAppCompat = "1.7.0" androidxAppCompat = "1.7.0"
androidxBrowser = "1.8.0" androidxBrowser = "1.8.0"
androidxComposeBom = "2024.12.01" androidxComposeBom = "2024.12.01"
androidxComposeFoundation = "1.8.0-alpha07"
androidxComposeRuntimeTracing = "1.7.6" androidxComposeRuntimeTracing = "1.7.6"
androidxCore = "1.15.0" androidxCore = "1.15.0"
androidxCoreSplashscreen = "1.0.1" androidxCoreSplashscreen = "1.0.1"
@ -36,14 +37,14 @@ firebasePerfPlugin = "1.4.2"
gmsPlugin = "4.4.2" gmsPlugin = "4.4.2"
googleOss = "17.1.0" googleOss = "17.1.0"
googleOssPlugin = "0.10.6" googleOssPlugin = "0.10.6"
hilt = "2.53.1" hilt = "2.54"
hiltExt = "1.2.0" hiltExt = "1.2.0"
jacoco = "0.8.12" jacoco = "0.8.12"
junit4 = "4.13.2" junit4 = "4.13.2"
kotlin = "2.1.0" kotlin = "2.1.0"
kotlinxCoroutines = "1.9.0" kotlinxCoroutines = "1.10.1"
kotlinxDatetime = "0.6.1" kotlinxDatetime = "0.6.1"
kotlinxSerializationJson = "1.7.3" kotlinxSerializationJson = "1.8.0"
ksp = "2.1.0-1.0.29" ksp = "2.1.0-1.0.29"
moduleGraph = "2.7.1" moduleGraph = "2.7.1"
okhttp = "4.12.0" okhttp = "4.12.0"
@ -52,7 +53,7 @@ protobufPlugin = "0.9.4"
retrofit = "2.11.0" retrofit = "2.11.0"
retrofitKotlinxSerializationJson = "1.0.0" retrofitKotlinxSerializationJson = "1.0.0"
robolectric = "4.14.1" robolectric = "4.14.1"
roborazzi = "1.37.0" roborazzi = "1.39.0"
room = "2.6.1" room = "2.6.1"
secrets = "2.0.1" secrets = "2.0.1"
truth = "1.4.4" truth = "1.4.4"
@ -69,7 +70,7 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version
androidx-benchmark-macro = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidxMacroBenchmark" } androidx-benchmark-macro = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidxMacroBenchmark" }
androidx-browser = { group = "androidx.browser", name = "browser", version.ref = "androidxBrowser" } androidx-browser = { group = "androidx.browser", name = "browser", version.ref = "androidxBrowser" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" }
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" } androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "androidxComposeFoundation" }
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" } 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-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

Loading…
Cancel
Save