diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 433a7e4a3..000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,25 +0,0 @@ -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - - package-ecosystem: "gradle" - directory: "/" - schedule: - interval: "weekly" - registries: "*" - labels: [ "version update" ] - groups: - kotlin-ksp: - patterns: - - "org.jetbrains.kotlin:*" - - "org.jetbrains.kotlin.jvm" - - "com.google.devtools.ksp" - open-pull-requests-limit: 10 -registries: - maven-google: - type: "maven-repository" - url: "https://maven.google.com" - replaces-base: true diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 000000000..a9eab3c4d --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "local>android/.github:renovate-config" + ], + "baseBranches": [ + "main" + ], + "gitIgnoredAuthors": [ + "41898282+github-actions[bot]@users.noreply.github.com" + ] +} diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index b1aee6f66..8e77a2600 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -18,6 +18,7 @@ jobs: permissions: contents: write pull-requests: write + security-events: write timeout-minutes: 60 @@ -25,13 +26,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Enable KVM group perms - run: | - echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules - sudo udevadm control --reload-rules - sudo udevadm trigger --name-match=kvm - ls /dev/kvm - - name: Copy CI gradle.properties run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties @@ -44,12 +38,6 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 - - name: Setup Android SDK - uses: android-actions/setup-android@v3 - - - name: Accept licenses - run: yes | sdkmanager --licenses || true - - name: Check build-logic run: ./gradlew check -p build-logic @@ -93,7 +81,9 @@ jobs: continue-on-error: false if: steps.screenshotsverify.outcome == 'failure' && github.event.pull_request.head.repo.full_name != github.repository run: | - echo "::error::Screenshot tests failed, please create a PR in your fork first." && exit 1 + echo "::error::Screenshot tests failed, please create a PR in your fork first." + echo "Your fork's CI will take screenshots for your fork." + exit 1 # Runs if previous job failed - name: Generate new screenshots if verification failed and it's a PR @@ -114,19 +104,8 @@ jobs: - name: Run local tests run: ./gradlew testDemoDebug :lint:test - - name: Setup GMD - run: ./gradlew :benchmarks:pixel6Api33Setup - --info - -Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true - -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" - - name: Build all build type and flavor permutations - run: ./gradlew :app:assemble :benchmarks:assemble -Pandroidx.baselineprofile.skipgeneration - -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" - -Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true - -Pandroid.experimental.androidTest.numManagedDeviceShards=1 - -Pandroid.experimental.testOptions.managedDevices.maxConcurrentDevices=1 - -Pandroid.experimental.testOptions.managedDevices.setupTimeoutMinutes=5 + run: ./gradlew :app:assemble - name: Upload build outputs (APKs) uses: actions/upload-artifact@v4 @@ -135,14 +114,14 @@ jobs: path: '**/build/outputs/apk/**/*.apk' - name: Upload JVM local results (XML) - if: always() + if: ${{ !cancelled() }} uses: actions/upload-artifact@v4 with: name: local-test-results path: '**/build/test-results/test*UnitTest/**.xml' - name: Upload screenshot results (PNG) - if: always() + if: ${{ !cancelled() }} uses: actions/upload-artifact@v4 with: name: screenshot-test-results @@ -152,12 +131,18 @@ jobs: run: ./gradlew :app:lintProdRelease :app-nia-catalog:lintRelease :lint:lint - name: Upload lint reports (HTML) - if: always() + if: ${{ !cancelled() }} uses: actions/upload-artifact@v4 with: name: lint-reports path: '**/build/reports/lint-results-*.html' + - name: Upload lint reports (SARIF) + if: ${{ !cancelled() && hashFiles('**/*.sarif') != '' }} + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: './' + - name: Check badging run: ./gradlew :app:checkProdReleaseBadging @@ -166,7 +151,7 @@ jobs: timeout-minutes: 55 strategy: matrix: - api-level: [26, 30] + api-level: [26, 34] steps: - name: Delete unnecessary tools 🔧 @@ -201,9 +186,6 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 - with: - validate-wrappers: true - gradle-home-cache-cleanup: true - name: Build projects and run instrumentation tests uses: reactivecircus/android-emulator-runner@v2 @@ -226,7 +208,7 @@ jobs: run: ./gradlew createDemoDebugCombinedCoverageReport - name: Upload test reports - if: always() + if: ${{ !cancelled() }} uses: actions/upload-artifact@v4 with: name: test-reports-${{ matrix.api-level }} diff --git a/.github/workflows/NightlyBaselineProfiles.yaml b/.github/workflows/NightlyBaselineProfiles.yaml index 288842ac7..de626b0a5 100644 --- a/.github/workflows/NightlyBaselineProfiles.yaml +++ b/.github/workflows/NightlyBaselineProfiles.yaml @@ -52,8 +52,9 @@ jobs: -Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" - - name: Build all build type and flavor permutations including baseline profiles - run: ./gradlew :app:assemble - -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=baselineprofile - -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" - -Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true + # This generates both baseline and startup profile and adds them into the generated folder + - name: Generate Baseline Profile + run: ./gradlew :app:generateReleaseBaselineProfile + -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=baselineprofile + -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" + --stacktrace \ No newline at end of file diff --git a/README.md b/README.md index 83bf139be..1e2dfde17 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,8 @@ To run the tests execute the following gradle tasks: (see below for explanation). To avoid this, run `recordRoborazziDemoDebug` prior to running unit tests. - `connectedDemoDebugAndroidTest` run all instrumented tests against the `demoDebug` variant. -**Note:** You should not run `./gradlew test` or `./gradlew connectedAndroidTest` as this will execute +> [!NOTE] +> You should not run `./gradlew test` or `./gradlew connectedAndroidTest` as this will execute tests against _all_ build variants which is both unnecessary and will result in failures as only the `demoDebug` variant is supported. No other variants have any tests (although this might change in future). @@ -138,7 +139,9 @@ stored in `modulename/src/test/screenshots`. - `compareRoborazziDemoDebug` create comparison images between failed tests and the known correct images. These can also be found in `modulename/src/test/screenshots`. -**Note on failing screenshot tests:** The known correct screenshots stored in this repository are recorded on CI using Linux. Other +> [!NOTE] +> **Note on failing screenshot tests** +> The known correct screenshots stored in this repository are recorded on CI using Linux. Other platforms may (and probably will) generate slightly different images, making the screenshot tests fail. When working on a non-Linux platform, a workaround to this is to run `recordRoborazziDemoDebug` on the `main` branch before starting work. After making changes, `verifyRoborazziDemoDebug` will identify only diff --git a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt index e009e88ee..852b3db52 100644 --- a/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt +++ b/app-nia-catalog/dependencies/releaseRuntimeClasspath.txt @@ -12,45 +12,45 @@ androidx.browser:browser:1.8.0 androidx.collection:collection-jvm:1.4.4 androidx.collection:collection-ktx:1.4.4 androidx.collection:collection:1.4.4 -androidx.compose.animation:animation-android:1.7.5 -androidx.compose.animation:animation-core-android:1.7.5 -androidx.compose.animation:animation-core:1.7.5 -androidx.compose.animation:animation:1.7.5 -androidx.compose.foundation:foundation-android:1.7.5 -androidx.compose.foundation:foundation-layout-android:1.7.5 -androidx.compose.foundation:foundation-layout:1.7.5 -androidx.compose.foundation:foundation:1.7.5 +androidx.compose.animation:animation-android:1.7.6 +androidx.compose.animation:animation-core-android:1.7.6 +androidx.compose.animation:animation-core:1.7.6 +androidx.compose.animation:animation:1.7.6 +androidx.compose.foundation:foundation-android:1.7.6 +androidx.compose.foundation:foundation-layout-android:1.7.6 +androidx.compose.foundation:foundation-layout:1.7.6 +androidx.compose.foundation:foundation:1.7.6 androidx.compose.material3.adaptive:adaptive-android: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:1.3.1 androidx.compose.material3:material3-android:1.3.1 androidx.compose.material3:material3:1.3.1 -androidx.compose.material:material-icons-core-android:1.7.5 -androidx.compose.material:material-icons-core:1.7.5 -androidx.compose.material:material-icons-extended-android:1.7.5 -androidx.compose.material:material-icons-extended:1.7.5 -androidx.compose.material:material-ripple-android:1.7.5 -androidx.compose.material:material-ripple:1.7.5 -androidx.compose.runtime:runtime-android:1.7.5 -androidx.compose.runtime:runtime-saveable-android:1.7.5 -androidx.compose.runtime:runtime-saveable:1.7.5 -androidx.compose.runtime:runtime:1.7.5 -androidx.compose.ui:ui-android:1.7.5 -androidx.compose.ui:ui-geometry-android:1.7.5 -androidx.compose.ui:ui-geometry:1.7.5 -androidx.compose.ui:ui-graphics-android:1.7.5 -androidx.compose.ui:ui-graphics:1.7.5 -androidx.compose.ui:ui-text-android:1.7.5 -androidx.compose.ui:ui-text:1.7.5 -androidx.compose.ui:ui-tooling-preview-android:1.7.5 -androidx.compose.ui:ui-tooling-preview:1.7.5 -androidx.compose.ui:ui-unit-android:1.7.5 -androidx.compose.ui:ui-unit:1.7.5 -androidx.compose.ui:ui-util-android:1.7.5 -androidx.compose.ui:ui-util:1.7.5 -androidx.compose.ui:ui:1.7.5 -androidx.compose:compose-bom:2024.11.00 +androidx.compose.material:material-icons-core-android:1.7.6 +androidx.compose.material:material-icons-core:1.7.6 +androidx.compose.material:material-icons-extended-android:1.7.6 +androidx.compose.material:material-icons-extended:1.7.6 +androidx.compose.material:material-ripple-android:1.7.6 +androidx.compose.material:material-ripple:1.7.6 +androidx.compose.runtime:runtime-android:1.7.6 +androidx.compose.runtime:runtime-saveable-android:1.7.6 +androidx.compose.runtime:runtime-saveable:1.7.6 +androidx.compose.runtime:runtime:1.7.6 +androidx.compose.ui:ui-android:1.7.6 +androidx.compose.ui:ui-geometry-android:1.7.6 +androidx.compose.ui:ui-geometry:1.7.6 +androidx.compose.ui:ui-graphics-android:1.7.6 +androidx.compose.ui:ui-graphics:1.7.6 +androidx.compose.ui:ui-text-android:1.7.6 +androidx.compose.ui:ui-text:1.7.6 +androidx.compose.ui:ui-tooling-preview-android:1.7.6 +androidx.compose.ui:ui-tooling-preview:1.7.6 +androidx.compose.ui:ui-unit-android:1.7.6 +androidx.compose.ui:ui-unit:1.7.6 +androidx.compose.ui:ui-util-android:1.7.6 +androidx.compose.ui:ui-util:1.7.6 +androidx.compose.ui:ui:1.7.6 +androidx.compose:compose-bom:2024.12.01 androidx.concurrent:concurrent-futures:1.1.0 androidx.core:core-ktx:1.13.1 androidx.core:core:1.13.1 @@ -96,10 +96,10 @@ androidx.window:window-core:1.3.0 androidx.window:window:1.3.0 com.google.accompanist:accompanist-drawablepainter:0.32.0 com.google.code.findbugs:jsr305:3.0.2 -com.google.dagger:dagger-lint-aar:2.52 -com.google.dagger:dagger:2.52 -com.google.dagger:hilt-android:2.52 -com.google.dagger:hilt-core:2.52 +com.google.dagger:dagger-lint-aar:2.53.1 +com.google.dagger:dagger:2.53.1 +com.google.dagger:hilt-android:2.53.1 +com.google.dagger:hilt-core:2.53.1 com.google.guava:listenablefuture:1.0 com.squareup.okhttp3:okhttp:4.12.0 com.squareup.okio:okio-jvm:3.9.0 @@ -110,10 +110,10 @@ io.coil-kt:coil-compose:2.7.0 io.coil-kt:coil:2.7.0 jakarta.inject:jakarta.inject-api:2.0.1 javax.inject:javax.inject:1 -org.jetbrains.kotlin:kotlin-stdlib-common:2.0.20 +org.jetbrains.kotlin:kotlin-stdlib-common:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0 -org.jetbrains.kotlin:kotlin-stdlib:2.0.20 +org.jetbrains.kotlin:kotlin-stdlib:2.1.0 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.1 @@ -121,3 +121,4 @@ org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1 org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.1 org.jetbrains.kotlinx:kotlinx-datetime:0.6.1 org.jetbrains:annotations:23.0.0 +org.jspecify:jspecify:1.0.0 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5434df8c2..166eb8582 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -36,9 +36,6 @@ android { // Custom test runner to set up Hilt dependency graph testInstrumentationRunner = "com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner" - vectorDrawables { - useSupportLibrary = true - } } buildTypes { diff --git a/app/dependencies/prodReleaseRuntimeClasspath.txt b/app/dependencies/prodReleaseRuntimeClasspath.txt index a2719ef22..2db7fa46c 100644 --- a/app/dependencies/prodReleaseRuntimeClasspath.txt +++ b/app/dependencies/prodReleaseRuntimeClasspath.txt @@ -13,14 +13,14 @@ androidx.browser:browser:1.8.0 androidx.collection:collection-jvm:1.4.4 androidx.collection:collection-ktx:1.4.4 androidx.collection:collection:1.4.4 -androidx.compose.animation:animation-android:1.7.5 -androidx.compose.animation:animation-core-android:1.7.5 -androidx.compose.animation:animation-core:1.7.5 -androidx.compose.animation:animation:1.7.5 -androidx.compose.foundation:foundation-android:1.7.5 -androidx.compose.foundation:foundation-layout-android:1.7.5 -androidx.compose.foundation:foundation-layout:1.7.5 -androidx.compose.foundation:foundation:1.7.5 +androidx.compose.animation:animation-android:1.7.6 +androidx.compose.animation:animation-core-android:1.7.6 +androidx.compose.animation:animation-core:1.7.6 +androidx.compose.animation:animation:1.7.6 +androidx.compose.foundation:foundation-android:1.7.6 +androidx.compose.foundation:foundation-layout-android:1.7.6 +androidx.compose.foundation:foundation-layout:1.7.6 +androidx.compose.foundation:foundation:1.7.6 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:1.0.0 @@ -33,36 +33,37 @@ androidx.compose.material3:material3-android:1.3.1 androidx.compose.material3:material3-window-size-class-android:1.3.1 androidx.compose.material3:material3-window-size-class:1.3.1 androidx.compose.material3:material3:1.3.1 -androidx.compose.material:material-icons-core-android:1.7.5 -androidx.compose.material:material-icons-core:1.7.5 -androidx.compose.material:material-icons-extended-android:1.7.5 -androidx.compose.material:material-icons-extended:1.7.5 -androidx.compose.material:material-ripple-android:1.7.5 -androidx.compose.material:material-ripple:1.7.5 -androidx.compose.runtime:runtime-android:1.7.5 -androidx.compose.runtime:runtime-saveable-android:1.7.5 -androidx.compose.runtime:runtime-saveable:1.7.5 -androidx.compose.runtime:runtime-tracing:1.7.5 -androidx.compose.runtime:runtime:1.7.5 -androidx.compose.ui:ui-android:1.7.5 -androidx.compose.ui:ui-geometry-android:1.7.5 -androidx.compose.ui:ui-geometry:1.7.5 -androidx.compose.ui:ui-graphics-android:1.7.5 -androidx.compose.ui:ui-graphics:1.7.5 -androidx.compose.ui:ui-text-android:1.7.5 -androidx.compose.ui:ui-text:1.7.5 -androidx.compose.ui:ui-tooling-preview-android:1.7.5 -androidx.compose.ui:ui-tooling-preview:1.7.5 -androidx.compose.ui:ui-unit-android:1.7.5 -androidx.compose.ui:ui-unit:1.7.5 -androidx.compose.ui:ui-util-android:1.7.5 -androidx.compose.ui:ui-util:1.7.5 -androidx.compose.ui:ui:1.7.5 -androidx.compose:compose-bom:2024.11.00 +androidx.compose.material:material-icons-core-android:1.7.6 +androidx.compose.material:material-icons-core:1.7.6 +androidx.compose.material:material-icons-extended-android:1.7.6 +androidx.compose.material:material-icons-extended:1.7.6 +androidx.compose.material:material-ripple-android:1.7.6 +androidx.compose.material:material-ripple:1.7.6 +androidx.compose.runtime:runtime-android:1.7.6 +androidx.compose.runtime:runtime-saveable-android:1.7.6 +androidx.compose.runtime:runtime-saveable:1.7.6 +androidx.compose.runtime:runtime-tracing:1.7.6 +androidx.compose.runtime:runtime:1.7.6 +androidx.compose.ui:ui-android:1.7.6 +androidx.compose.ui:ui-geometry-android:1.7.6 +androidx.compose.ui:ui-geometry:1.7.6 +androidx.compose.ui:ui-graphics-android:1.7.6 +androidx.compose.ui:ui-graphics:1.7.6 +androidx.compose.ui:ui-text-android:1.7.6 +androidx.compose.ui:ui-text:1.7.6 +androidx.compose.ui:ui-tooling-preview-android:1.7.6 +androidx.compose.ui:ui-tooling-preview:1.7.6 +androidx.compose.ui:ui-unit-android:1.7.6 +androidx.compose.ui:ui-unit:1.7.6 +androidx.compose.ui:ui-util-android:1.7.6 +androidx.compose.ui:ui-util:1.7.6 +androidx.compose.ui:ui:1.7.6 +androidx.compose:compose-bom:2024.12.01 +androidx.concurrent:concurrent-futures-ktx:1.1.0 androidx.concurrent:concurrent-futures:1.1.0 -androidx.core:core-ktx:1.13.1 +androidx.core:core-ktx:1.15.0 androidx.core:core-splashscreen:1.0.1 -androidx.core:core:1.13.1 +androidx.core:core:1.15.0 androidx.cursoradapter:cursoradapter:1.0.0 androidx.customview:customview-poolingcontainer:1.0.0 androidx.customview:customview:1.0.0 @@ -89,38 +90,38 @@ androidx.hilt:hilt-navigation:1.2.0 androidx.hilt:hilt-work:1.2.0 androidx.interpolator:interpolator:1.0.0 androidx.legacy:legacy-support-core-utils:1.0.0 -androidx.lifecycle:lifecycle-common-java8:2.8.6 -androidx.lifecycle:lifecycle-common-jvm:2.8.6 -androidx.lifecycle:lifecycle-common:2.8.6 -androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.6 -androidx.lifecycle:lifecycle-livedata-core:2.8.6 -androidx.lifecycle:lifecycle-livedata:2.8.6 -androidx.lifecycle:lifecycle-process:2.8.6 -androidx.lifecycle:lifecycle-runtime-android:2.8.6 -androidx.lifecycle:lifecycle-runtime-compose-android:2.8.6 -androidx.lifecycle:lifecycle-runtime-compose:2.8.6 -androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.6 -androidx.lifecycle:lifecycle-runtime-ktx:2.8.6 -androidx.lifecycle:lifecycle-runtime:2.8.6 -androidx.lifecycle:lifecycle-service:2.8.6 -androidx.lifecycle:lifecycle-viewmodel-android:2.8.6 -androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.6 -androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6 -androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.6 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.6 -androidx.lifecycle:lifecycle-viewmodel:2.8.6 +androidx.lifecycle:lifecycle-common-java8:2.8.7 +androidx.lifecycle:lifecycle-common-jvm:2.8.7 +androidx.lifecycle:lifecycle-common:2.8.7 +androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.7 +androidx.lifecycle:lifecycle-livedata-core:2.8.7 +androidx.lifecycle:lifecycle-livedata:2.8.7 +androidx.lifecycle:lifecycle-process:2.8.7 +androidx.lifecycle:lifecycle-runtime-android:2.8.7 +androidx.lifecycle:lifecycle-runtime-compose-android:2.8.7 +androidx.lifecycle:lifecycle-runtime-compose:2.8.7 +androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.7 +androidx.lifecycle:lifecycle-runtime-ktx:2.8.7 +androidx.lifecycle:lifecycle-runtime:2.8.7 +androidx.lifecycle:lifecycle-service:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-android:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.7 +androidx.lifecycle:lifecycle-viewmodel:2.8.7 androidx.loader:loader:1.0.0 androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 androidx.metrics:metrics-performance:1.0.0-beta01 -androidx.navigation:navigation-common-ktx:2.8.0 -androidx.navigation:navigation-common:2.8.0 -androidx.navigation:navigation-compose:2.8.0 -androidx.navigation:navigation-runtime-ktx:2.8.0 -androidx.navigation:navigation-runtime:2.8.0 +androidx.navigation:navigation-common-ktx:2.8.5 +androidx.navigation:navigation-common:2.8.5 +androidx.navigation:navigation-compose:2.8.5 +androidx.navigation:navigation-runtime-ktx:2.8.5 +androidx.navigation:navigation-runtime:2.8.5 androidx.print:print:1.0.0 androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 -androidx.profileinstaller:profileinstaller:1.3.1 +androidx.profileinstaller:profileinstaller:1.4.1 androidx.resourceinspection:resourceinspection-annotation:1.0.1 androidx.room:room-common:2.6.1 androidx.room:room-ktx:2.6.1 @@ -141,11 +142,11 @@ androidx.window.extensions.core:core:1.0.0 androidx.window:window-core-android:1.3.0 androidx.window:window-core:1.3.0 androidx.window:window:1.3.0 -androidx.work:work-runtime-ktx:2.9.0 -androidx.work:work-runtime:2.9.0 +androidx.work:work-runtime-ktx:2.10.0 +androidx.work:work-runtime:2.10.0 com.caverock:androidsvg-aar:1.4 com.google.accompanist:accompanist-drawablepainter:0.32.0 -com.google.accompanist:accompanist-permissions:0.36.0 +com.google.accompanist:accompanist-permissions:0.37.0 com.google.android.datatransport:transport-api:3.2.0 com.google.android.datatransport:transport-backend-cct:3.3.0 com.google.android.datatransport:transport-runtime:3.3.0 @@ -153,31 +154,31 @@ com.google.android.gms:play-services-ads-identifier:18.0.0 com.google.android.gms:play-services-base:18.5.0 com.google.android.gms:play-services-basement:18.4.0 com.google.android.gms:play-services-cloud-messaging:17.2.0 -com.google.android.gms:play-services-measurement-api:22.1.0 -com.google.android.gms:play-services-measurement-base:22.1.0 -com.google.android.gms:play-services-measurement-impl:22.1.0 -com.google.android.gms:play-services-measurement-sdk-api:22.1.0 -com.google.android.gms:play-services-measurement-sdk:22.1.0 -com.google.android.gms:play-services-measurement:22.1.0 +com.google.android.gms:play-services-measurement-api:22.1.2 +com.google.android.gms:play-services-measurement-base:22.1.2 +com.google.android.gms:play-services-measurement-impl:22.1.2 +com.google.android.gms:play-services-measurement-sdk-api:22.1.2 +com.google.android.gms:play-services-measurement-sdk:22.1.2 +com.google.android.gms:play-services-measurement:22.1.2 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-tasks:18.2.0 com.google.code.findbugs:jsr305:3.0.2 -com.google.dagger:dagger-lint-aar:2.52 -com.google.dagger:dagger:2.52 -com.google.dagger:hilt-android:2.52 -com.google.dagger:hilt-core:2.52 +com.google.dagger:dagger-lint-aar:2.53.1 +com.google.dagger:dagger:2.53.1 +com.google.dagger:hilt-android:2.53.1 +com.google.dagger:hilt-core:2.53.1 com.google.errorprone:error_prone_annotations:2.26.0 com.google.firebase:firebase-abt:21.1.1 -com.google.firebase:firebase-analytics:22.1.0 +com.google.firebase:firebase-analytics:22.1.2 com.google.firebase:firebase-annotations:16.2.0 -com.google.firebase:firebase-bom:33.3.0 +com.google.firebase:firebase-bom:33.7.0 com.google.firebase:firebase-common-ktx:21.0.0 com.google.firebase:firebase-common:21.0.0 com.google.firebase:firebase-components:18.0.0 com.google.firebase:firebase-config-interop:16.0.1 -com.google.firebase:firebase-config:22.0.0 -com.google.firebase:firebase-crashlytics:19.1.0 +com.google.firebase:firebase-config:22.0.1 +com.google.firebase:firebase-crashlytics:19.3.0 com.google.firebase:firebase-datatransport:19.0.0 com.google.firebase:firebase-encoders-json:18.0.1 com.google.firebase:firebase-encoders-proto:16.0.0 @@ -186,16 +187,15 @@ com.google.firebase:firebase-iid-interop:17.1.0 com.google.firebase:firebase-installations-interop:17.2.0 com.google.firebase:firebase-installations:18.0.0 com.google.firebase:firebase-measurement-connector:20.0.1 -com.google.firebase:firebase-messaging:24.0.1 -com.google.firebase:firebase-perf:21.0.1 -com.google.firebase:firebase-sessions:2.0.4 -com.google.firebase:protolite-well-known-types:18.0.0 +com.google.firebase:firebase-messaging:24.1.0 +com.google.firebase:firebase-perf:21.0.3 +com.google.firebase:firebase-sessions:2.0.7 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:4.28.2 -com.google.protobuf:protobuf-kotlin-lite:4.28.2 +com.google.protobuf:protobuf-javalite:4.29.2 +com.google.protobuf:protobuf-kotlin-lite:4.29.2 com.squareup.okhttp3:logging-interceptor:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 com.squareup.okio:okio-jvm:3.9.0 @@ -212,10 +212,10 @@ javax.inject:javax.inject:1 org.checkerframework:checker-qual:3.12.0 org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.9.22 org.jetbrains.kotlin:kotlin-parcelize-runtime:1.9.22 -org.jetbrains.kotlin:kotlin-stdlib-common:2.0.20 +org.jetbrains.kotlin:kotlin-stdlib-common:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0 -org.jetbrains.kotlin:kotlin-stdlib:2.0.20 +org.jetbrains.kotlin:kotlin-stdlib:2.1.0 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0 @@ -224,9 +224,10 @@ org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.9.0 org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.9.0 org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.1 org.jetbrains.kotlinx:kotlinx-datetime:0.6.1 -org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3 -org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.3 -org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3 -org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.3 -org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3 +org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3 +org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.3 +org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3 +org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.3 +org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3 org.jetbrains:annotations:23.0.0 +org.jspecify:jspecify:1.0.0 diff --git a/app/prodRelease-badging.txt b/app/prodRelease-badging.txt index 9908e775a..0d770604e 100644 --- a/app/prodRelease-badging.txt +++ b/app/prodRelease-badging.txt @@ -1,6 +1,6 @@ -package: name='com.google.samples.apps.nowinandroid' versionCode='8' versionName='0.1.2' platformBuildVersionName='14' platformBuildVersionCode='34' compileSdkVersion='34' compileSdkVersionCodename='14' +package: name='com.google.samples.apps.nowinandroid' versionCode='8' versionName='0.1.2' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15' sdkVersion:'21' -targetSdkVersion:'34' +targetSdkVersion:'35' uses-permission: name='android.permission.INTERNET' uses-permission: name='android.permission.ACCESS_NETWORK_STATE' uses-permission: name='android.permission.POST_NOTIFICATIONS' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5b22f9865..af3534342 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -41,6 +41,7 @@ diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt index 599bd0b35..ecc23d80e 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt @@ -22,10 +22,7 @@ import androidx.activity.SystemBarStyle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -35,21 +32,22 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.metrics.performance.JankStats +import androidx.tracing.trace import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading -import com.google.samples.apps.nowinandroid.MainActivityUiState.Success import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme -import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig -import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone import com.google.samples.apps.nowinandroid.ui.NiaApp import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState +import com.google.samples.apps.nowinandroid.util.isSystemInDarkTheme import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import javax.inject.Inject @@ -81,53 +79,60 @@ class MainActivity : ComponentActivity() { val splashScreen = installSplashScreen() super.onCreate(savedInstanceState) - var uiState: MainActivityUiState by mutableStateOf(Loading) + // We keep this as a mutable state, so that we can track changes inside the composition. + // This allows us to react to dark/light mode changes. + var themeSettings by mutableStateOf( + ThemeSettings( + darkTheme = resources.configuration.isSystemInDarkTheme, + androidTheme = Loading.shouldUseAndroidTheme, + disableDynamicTheming = Loading.shouldDisableDynamicTheming, + ), + ) // Update the uiState lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.uiState - .onEach { uiState = it } - .collect() + combine( + isSystemInDarkTheme(), + viewModel.uiState, + ) { systemDark, uiState -> + ThemeSettings( + darkTheme = uiState.shouldUseDarkTheme(systemDark), + androidTheme = uiState.shouldUseAndroidTheme, + disableDynamicTheming = uiState.shouldDisableDynamicTheming, + ) + } + .onEach { themeSettings = it } + .map { it.darkTheme } + .distinctUntilChanged() + .collect { darkTheme -> + trace("niaEdgeToEdge") { + // Turn off the decor fitting system windows, which allows us to handle insets, + // including IME animations, and go edge-to-edge. + // This is the same parameters as the default enableEdgeToEdge call, but we manually + // resolve whether or not to show dark theme using uiState, since it can be different + // than the configuration's dark theme value based on the user preference. + enableEdgeToEdge( + statusBarStyle = SystemBarStyle.auto( + lightScrim = android.graphics.Color.TRANSPARENT, + darkScrim = android.graphics.Color.TRANSPARENT, + ) { darkTheme }, + navigationBarStyle = SystemBarStyle.auto( + lightScrim = lightScrim, + darkScrim = darkScrim, + ) { darkTheme }, + ) + } + } } } // Keep the splash screen on-screen until the UI state is loaded. This condition is // evaluated each time the app needs to be redrawn so it should be fast to avoid blocking // the UI. - splashScreen.setKeepOnScreenCondition { - when (uiState) { - Loading -> true - is Success -> false - } - } - - // Turn off the decor fitting system windows, which allows us to handle insets, - // including IME animations, and go edge-to-edge - // This also sets up the initial system bar style based on the platform theme - enableEdgeToEdge() + splashScreen.setKeepOnScreenCondition { viewModel.uiState.value.shouldKeepSplashScreen() } setContent { - val darkTheme = shouldUseDarkTheme(uiState) - - // Update the edge to edge configuration to match the theme - // This is the same parameters as the default enableEdgeToEdge call, but we manually - // resolve whether or not to show dark theme using uiState, since it can be different - // than the configuration's dark theme value based on the user preference. - DisposableEffect(darkTheme) { - enableEdgeToEdge( - statusBarStyle = SystemBarStyle.auto( - android.graphics.Color.TRANSPARENT, - android.graphics.Color.TRANSPARENT, - ) { darkTheme }, - navigationBarStyle = SystemBarStyle.auto( - lightScrim, - darkScrim, - ) { darkTheme }, - ) - onDispose {} - } - val appState = rememberNiaAppState( networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, @@ -141,9 +146,9 @@ class MainActivity : ComponentActivity() { LocalTimeZone provides currentTimeZone, ) { NiaTheme( - darkTheme = darkTheme, - androidTheme = shouldUseAndroidTheme(uiState), - disableDynamicTheming = shouldDisableDynamicTheming(uiState), + darkTheme = themeSettings.darkTheme, + androidTheme = themeSettings.androidTheme, + disableDynamicTheming = themeSettings.disableDynamicTheming, ) { NiaApp(appState) } @@ -162,47 +167,6 @@ class MainActivity : ComponentActivity() { } } -/** - * Returns `true` if the Android theme should be used, as a function of the [uiState]. - */ -@Composable -private fun shouldUseAndroidTheme( - uiState: MainActivityUiState, -): Boolean = when (uiState) { - Loading -> false - is Success -> when (uiState.userData.themeBrand) { - ThemeBrand.DEFAULT -> false - ThemeBrand.ANDROID -> true - } -} - -/** - * Returns `true` if the dynamic color is disabled, as a function of the [uiState]. - */ -@Composable -private fun shouldDisableDynamicTheming( - uiState: MainActivityUiState, -): Boolean = when (uiState) { - Loading -> false - is Success -> !uiState.userData.useDynamicColor -} - -/** - * Returns `true` if dark theme should be used, as a function of the [uiState] and the - * current system context. - */ -@Composable -private fun shouldUseDarkTheme( - uiState: MainActivityUiState, -): Boolean = when (uiState) { - Loading -> isSystemInDarkTheme() - is Success -> when (uiState.userData.darkThemeConfig) { - DarkThemeConfig.FOLLOW_SYSTEM -> isSystemInDarkTheme() - DarkThemeConfig.LIGHT -> false - DarkThemeConfig.DARK -> true - } -} - /** * The default light scrim, as defined by androidx and the platform: * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt;l=35-38;drc=27e7d52e8604a080133e8b842db10c89b4482598 @@ -214,3 +178,13 @@ private val lightScrim = android.graphics.Color.argb(0xe6, 0xFF, 0xFF, 0xFF) * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt;l=40-44;drc=27e7d52e8604a080133e8b842db10c89b4482598 */ private val darkScrim = android.graphics.Color.argb(0x80, 0x1b, 0x1b, 0x1b) + +/** + * Class for the system theme settings. + * This wrapping class allows us to combine all the changes and prevent unnecessary recompositions. + */ +data class ThemeSettings( + val darkTheme: Boolean, + val androidTheme: Boolean, + val disableDynamicTheming: Boolean, +) diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt index 09f4597a7..2d22b7d9c 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt @@ -21,6 +21,8 @@ import androidx.lifecycle.viewModelScope import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading import com.google.samples.apps.nowinandroid.MainActivityUiState.Success import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository +import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig +import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.data.UserData import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.SharingStarted @@ -44,5 +46,40 @@ class MainActivityViewModel @Inject constructor( sealed interface MainActivityUiState { data object Loading : MainActivityUiState - data class Success(val userData: UserData) : MainActivityUiState + + data class Success(val userData: UserData) : MainActivityUiState { + override val shouldDisableDynamicTheming = !userData.useDynamicColor + + override val shouldUseAndroidTheme: Boolean = when (userData.themeBrand) { + ThemeBrand.DEFAULT -> false + ThemeBrand.ANDROID -> true + } + + override fun shouldUseDarkTheme(isSystemDarkTheme: Boolean) = + when (userData.darkThemeConfig) { + DarkThemeConfig.FOLLOW_SYSTEM -> isSystemDarkTheme + DarkThemeConfig.LIGHT -> false + DarkThemeConfig.DARK -> true + } + } + + /** + * Returns `true` if the state wasn't loaded yet and it should keep showing the splash screen. + */ + fun shouldKeepSplashScreen() = this is Loading + + /** + * Returns `true` if the dynamic color is disabled. + */ + val shouldDisableDynamicTheming: Boolean get() = true + + /** + * Returns `true` if the Android theme should be used. + */ + val shouldUseAndroidTheme: Boolean get() = false + + /** + * Returns `true` if dark theme should be used. + */ + fun shouldUseDarkTheme(isSystemDarkTheme: Boolean) = isSystemDarkTheme } diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt index 8e3ad814a..77f72e5fc 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt @@ -17,6 +17,9 @@ package com.google.samples.apps.nowinandroid import android.app.Application +import android.content.pm.ApplicationInfo +import android.os.StrictMode +import android.os.StrictMode.ThreadPolicy.Builder import coil.ImageLoader import coil.ImageLoaderFactory import com.google.samples.apps.nowinandroid.sync.initializers.Sync @@ -37,10 +40,34 @@ class NiaApplication : Application(), ImageLoaderFactory { override fun onCreate() { super.onCreate() + + setStrictModePolicy() + // Initialize Sync; the system responsible for keeping data in the app up to date. Sync.initialize(context = this) profileVerifierLogger() } override fun newImageLoader(): ImageLoader = imageLoader.get() + + /** + * Return true if the application is debuggable. + */ + private fun isDebuggable(): Boolean { + return 0 != applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE + } + + /** + * Set a thread policy that detects all potential problems on the main thread, such as network + * and disk access. + * + * If a problem is found, the offending call will be logged and the application will be killed. + */ + private fun setStrictModePolicy() { + if (isDebuggable()) { + StrictMode.setThreadPolicy( + Builder().detectAll().penaltyLog().penaltyDeath().build(), + ) + } + } } 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 640b22e83..f27b90cbe 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 @@ -37,7 +37,6 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult.ActionPerformed import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.runtime.Composable @@ -76,7 +75,6 @@ import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination import kotlin.reflect.KClass import com.google.samples.apps.nowinandroid.feature.settings.R as settingsR -@OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun NiaApp( appState: NiaAppState, @@ -126,7 +124,6 @@ fun NiaApp( @OptIn( ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class, - ExperimentalMaterial3AdaptiveApi::class, ) internal fun NiaApp( appState: NiaAppState, diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt index 249f07590..7c892c854 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt @@ -18,6 +18,8 @@ package com.google.samples.apps.nowinandroid.ui import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.navigation.NavController @@ -25,7 +27,6 @@ import androidx.navigation.NavDestination import androidx.navigation.NavDestination.Companion.hasRoute import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController -import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.navOptions import androidx.tracing.trace @@ -83,9 +84,21 @@ class NiaAppState( userNewsResourceRepository: UserNewsResourceRepository, timeZoneMonitor: TimeZoneMonitor, ) { + private val previousDestination = mutableStateOf(null) + val currentDestination: NavDestination? - @Composable get() = navController - .currentBackStackEntryAsState().value?.destination + @Composable get() { + // Collect the currentBackStackEntryFlow as a state + val currentEntry = navController.currentBackStackEntryFlow + .collectAsState(initial = null) + + // Fallback to previousDestination if currentEntry is null + return currentEntry.value?.destination.also { destination -> + if (destination != null) { + previousDestination.value = destination + } + } ?: previousDestination.value + } val currentTopLevelDestination: TopLevelDestination? @Composable get() { diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/util/UiExtensions.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/util/UiExtensions.kt new file mode 100644 index 000000000..20d55ab4c --- /dev/null +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/util/UiExtensions.kt @@ -0,0 +1,49 @@ +/* + * 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.util + +import android.content.res.Configuration +import androidx.activity.ComponentActivity +import androidx.core.util.Consumer +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.distinctUntilChanged + +/** + * Convenience wrapper for dark mode checking + */ +val Configuration.isSystemInDarkTheme + get() = (uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES + +/** + * Registers listener for configuration changes to retrieve whether system is in dark theme or not. + * Immediately upon subscribing, it sends the current value and then registers listener for changes. + */ +fun ComponentActivity.isSystemInDarkTheme() = callbackFlow { + channel.trySend(resources.configuration.isSystemInDarkTheme) + + val listener = Consumer { + channel.trySend(it.isSystemInDarkTheme) + } + + addOnConfigurationChangedListener(listener) + + awaitClose { removeOnConfigurationChangedListener(listener) } +} + .distinctUntilChanged() + .conflate() diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt index e84b96b73..9c9488fde 100644 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt @@ -16,7 +16,6 @@ package com.google.samples.apps.nowinandroid.ui -import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi import androidx.compose.material3.adaptive.Posture import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.runtime.CompositionLocalProvider @@ -113,7 +112,6 @@ class NiaAppScreenSizesScreenshotTests { TimeZone.setDefault(TimeZone.getTimeZone("UTC")) } - @OptIn(ExperimentalMaterial3AdaptiveApi::class) private fun testNiaAppScreenshotWithSize(width: Dp, height: Dp, screenshotName: String) { composeTestRule.setContent { CompositionLocalProvider( diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt index 2ef0d3e4f..78f568e03 100644 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt @@ -34,7 +34,6 @@ import androidx.compose.foundation.layout.windowInsetsStartWidth import androidx.compose.foundation.layout.windowInsetsTopHeight import androidx.compose.material3.SnackbarDuration.Indefinite import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi import androidx.compose.material3.adaptive.Posture import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.runtime.Composable @@ -209,7 +208,6 @@ class SnackbarInsetsScreenshotTests { } } - @OptIn(ExperimentalMaterial3AdaptiveApi::class) private fun testSnackbarScreenshotWithSize( snackbarHostState: SnackbarHostState, width: Dp, diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt index fe2e98452..b9b1047c1 100644 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt @@ -19,7 +19,6 @@ package com.google.samples.apps.nowinandroid.ui import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.material3.SnackbarDuration.Indefinite import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi import androidx.compose.material3.adaptive.Posture import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.runtime.CompositionLocalProvider @@ -182,7 +181,6 @@ class SnackbarScreenshotTests { } } - @OptIn(ExperimentalMaterial3AdaptiveApi::class) private fun testSnackbarScreenshotWithSize( snackbarHostState: SnackbarHostState, width: Dp, diff --git a/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png index 912fca4c7..2e6d1037c 100644 Binary files a/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png and b/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png differ diff --git a/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png index 30873b584..1acb34aba 100644 Binary files a/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png and b/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png differ diff --git a/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png index 668d69146..d52c844a3 100644 Binary files a/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png and b/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png differ diff --git a/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationBar.png index 1daf5ec34..e6e574347 100644 Binary files a/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationBar.png and b/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationBar.png differ diff --git a/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png index e2dffaf01..222fc21aa 100644 Binary files a/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png and b/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png differ diff --git a/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png index c5b7fe883..a33ad8863 100644 Binary files a/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png and b/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png differ diff --git a/app/src/testDemo/screenshots/insets_snackbar_compact_medium.png b/app/src/testDemo/screenshots/insets_snackbar_compact_medium.png index 1c5b10b47..4e741bdcc 100644 Binary files a/app/src/testDemo/screenshots/insets_snackbar_compact_medium.png and b/app/src/testDemo/screenshots/insets_snackbar_compact_medium.png differ diff --git a/app/src/testDemo/screenshots/insets_snackbar_compact_medium_noSnackbar.png b/app/src/testDemo/screenshots/insets_snackbar_compact_medium_noSnackbar.png index d37f02c65..2e76d3a68 100644 Binary files a/app/src/testDemo/screenshots/insets_snackbar_compact_medium_noSnackbar.png and b/app/src/testDemo/screenshots/insets_snackbar_compact_medium_noSnackbar.png differ diff --git a/app/src/testDemo/screenshots/insets_snackbar_expanded_expanded.png b/app/src/testDemo/screenshots/insets_snackbar_expanded_expanded.png index c6509df7d..d39df7b79 100644 Binary files a/app/src/testDemo/screenshots/insets_snackbar_expanded_expanded.png and b/app/src/testDemo/screenshots/insets_snackbar_expanded_expanded.png differ diff --git a/app/src/testDemo/screenshots/insets_snackbar_medium_medium.png b/app/src/testDemo/screenshots/insets_snackbar_medium_medium.png index deae27a40..ec6377143 100644 Binary files a/app/src/testDemo/screenshots/insets_snackbar_medium_medium.png and b/app/src/testDemo/screenshots/insets_snackbar_medium_medium.png differ diff --git a/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationBar.png index 4bc5d2b1c..fdbf9ee8b 100644 Binary files a/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationBar.png and b/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationBar.png differ diff --git a/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png index 79f808f44..882e62760 100644 Binary files a/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png and b/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png differ diff --git a/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png index f914a0454..8ffe73e86 100644 Binary files a/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png and b/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png differ diff --git a/app/src/testDemo/screenshots/snackbar_compact_medium.png b/app/src/testDemo/screenshots/snackbar_compact_medium.png index 24d282964..3f67285a3 100644 Binary files a/app/src/testDemo/screenshots/snackbar_compact_medium.png and b/app/src/testDemo/screenshots/snackbar_compact_medium.png differ diff --git a/app/src/testDemo/screenshots/snackbar_compact_medium_noSnackbar.png b/app/src/testDemo/screenshots/snackbar_compact_medium_noSnackbar.png index ff9ed7669..893d43d19 100644 Binary files a/app/src/testDemo/screenshots/snackbar_compact_medium_noSnackbar.png and b/app/src/testDemo/screenshots/snackbar_compact_medium_noSnackbar.png differ diff --git a/app/src/testDemo/screenshots/snackbar_expanded_expanded.png b/app/src/testDemo/screenshots/snackbar_expanded_expanded.png index 02a96999d..b429a7315 100644 Binary files a/app/src/testDemo/screenshots/snackbar_expanded_expanded.png and b/app/src/testDemo/screenshots/snackbar_expanded_expanded.png differ diff --git a/app/src/testDemo/screenshots/snackbar_medium_medium.png b/app/src/testDemo/screenshots/snackbar_medium_medium.png index 6f89ba2ad..00e9e3051 100644 Binary files a/app/src/testDemo/screenshots/snackbar_medium_medium.png and b/app/src/testDemo/screenshots/snackbar_medium_medium.png differ diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index c735fa4a0..6d0237010 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -15,10 +15,10 @@ */ import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { `kotlin-dsl` + alias(libs.plugins.android.lint) } group = "com.google.samples.apps.nowinandroid.buildlogic" @@ -46,6 +46,7 @@ dependencies { compileOnly(libs.ksp.gradlePlugin) compileOnly(libs.room.gradlePlugin) implementation(libs.truth) + lintChecks(libs.androidx.lint.gradle) } tasks { @@ -58,59 +59,59 @@ tasks { gradlePlugin { plugins { register("androidApplicationCompose") { - id = "nowinandroid.android.application.compose" + id = libs.plugins.nowinandroid.android.application.compose.get().pluginId implementationClass = "AndroidApplicationComposeConventionPlugin" } register("androidApplication") { - id = "nowinandroid.android.application" + id = libs.plugins.nowinandroid.android.application.asProvider().get().pluginId implementationClass = "AndroidApplicationConventionPlugin" } register("androidApplicationJacoco") { - id = "nowinandroid.android.application.jacoco" + id = libs.plugins.nowinandroid.android.application.jacoco.get().pluginId implementationClass = "AndroidApplicationJacocoConventionPlugin" } register("androidLibraryCompose") { - id = "nowinandroid.android.library.compose" + id = libs.plugins.nowinandroid.android.library.compose.get().pluginId implementationClass = "AndroidLibraryComposeConventionPlugin" } register("androidLibrary") { - id = "nowinandroid.android.library" + id = libs.plugins.nowinandroid.android.library.asProvider().get().pluginId implementationClass = "AndroidLibraryConventionPlugin" } register("androidFeature") { - id = "nowinandroid.android.feature" + id = libs.plugins.nowinandroid.android.feature.get().pluginId implementationClass = "AndroidFeatureConventionPlugin" } register("androidLibraryJacoco") { - id = "nowinandroid.android.library.jacoco" + id = libs.plugins.nowinandroid.android.library.jacoco.get().pluginId implementationClass = "AndroidLibraryJacocoConventionPlugin" } register("androidTest") { - id = "nowinandroid.android.test" + id = libs.plugins.nowinandroid.android.test.get().pluginId implementationClass = "AndroidTestConventionPlugin" } register("hilt") { - id = "nowinandroid.hilt" + id = libs.plugins.nowinandroid.hilt.get().pluginId implementationClass = "HiltConventionPlugin" } register("androidRoom") { - id = "nowinandroid.android.room" + id = libs.plugins.nowinandroid.android.room.get().pluginId implementationClass = "AndroidRoomConventionPlugin" } register("androidFirebase") { - id = "nowinandroid.android.application.firebase" + id = libs.plugins.nowinandroid.android.application.firebase.get().pluginId implementationClass = "AndroidApplicationFirebaseConventionPlugin" } register("androidFlavors") { - id = "nowinandroid.android.application.flavors" + id = libs.plugins.nowinandroid.android.application.flavors.get().pluginId implementationClass = "AndroidApplicationFlavorsConventionPlugin" } register("androidLint") { - id = "nowinandroid.android.lint" + id = libs.plugins.nowinandroid.android.lint.get().pluginId implementationClass = "AndroidLintConventionPlugin" } register("jvmLibrary") { - id = "nowinandroid.jvm.library" + id = libs.plugins.nowinandroid.jvm.library.get().pluginId implementationClass = "JvmLibraryConventionPlugin" } } diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt index f4d5bb0d0..72be1cb74 100644 --- a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt @@ -38,7 +38,7 @@ class AndroidApplicationConventionPlugin : Plugin { extensions.configure { configureKotlinAndroid(this) - defaultConfig.targetSdk = 34 + defaultConfig.targetSdk = 35 @Suppress("UnstableApiUsage") testOptions.animationsDisabled = true configureGradleManagedDevices(this) diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt index 422592b8a..7aacc0ffe 100644 --- a/build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt @@ -21,6 +21,7 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.exclude class AndroidApplicationFirebaseConventionPlugin : Plugin { override fun apply(target: Project) { @@ -33,9 +34,18 @@ class AndroidApplicationFirebaseConventionPlugin : Plugin { dependencies { val bom = libs.findLibrary("firebase-bom").get() - add("implementation", platform(bom)) + "implementation"(platform(bom)) "implementation"(libs.findLibrary("firebase.analytics").get()) - "implementation"(libs.findLibrary("firebase.performance").get()) + "implementation"(libs.findLibrary("firebase.performance").get()) { + /* + Exclusion of protobuf / protolite dependencies is necessary as the + datastore-proto brings in protobuf dependencies. These are the source of truth + for Now in Android. + That's why the duplicate classes from below dependencies are excluded. + */ + exclude(group = "com.google.protobuf", module = "protobuf-javalite") + exclude(group = "com.google.firebase", module = "protolite-well-known-types") + } "implementation"(libs.findLibrary("firebase.crashlytics").get()) } diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt index ac385b0d9..5bd58fabe 100644 --- a/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt @@ -14,8 +14,8 @@ * limitations under the License. */ +import com.android.build.api.dsl.ApplicationExtension 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 @@ -25,7 +25,7 @@ class AndroidApplicationJacocoConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { pluginManager.apply("jacoco") - val androidExtension = extensions.getByType() + val androidExtension = extensions.getByType() androidExtension.buildTypes.configureEach { enableAndroidTestCoverage = true diff --git a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt index 6d0f213d4..aa30f5115 100644 --- a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt @@ -36,18 +36,18 @@ class AndroidFeatureConventionPlugin : Plugin { } dependencies { - add("implementation", project(":core:ui")) - add("implementation", project(":core:designsystem")) + "implementation"(project(":core:ui")) + "implementation"(project(":core:designsystem")) - add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get()) - add("implementation", libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) - add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) - add("implementation", libs.findLibrary("androidx.navigation.compose").get()) - add("implementation", libs.findLibrary("androidx.tracing.ktx").get()) - add("implementation", libs.findLibrary("kotlinx.serialization.json").get()) + "implementation"(libs.findLibrary("androidx.hilt.navigation.compose").get()) + "implementation"(libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) + "implementation"(libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) + "implementation"(libs.findLibrary("androidx.navigation.compose").get()) + "implementation"(libs.findLibrary("androidx.tracing.ktx").get()) + "implementation"(libs.findLibrary("kotlinx.serialization.json").get()) - add("testImplementation", libs.findLibrary("androidx.navigation.testing").get()) - add("androidTestImplementation", libs.findLibrary("androidx.lifecycle.runtimeTesting").get()) + "testImplementation"(libs.findLibrary("androidx.navigation.testing").get()) + "androidTestImplementation"(libs.findLibrary("androidx.lifecycle.runtimeTesting").get()) } } } diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt index 2a10901fb..feffa5037 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -26,7 +26,6 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.kotlin class AndroidLibraryConventionPlugin : Plugin { override fun apply(target: Project) { @@ -39,7 +38,7 @@ class AndroidLibraryConventionPlugin : Plugin { extensions.configure { configureKotlinAndroid(this) - defaultConfig.targetSdk = 34 + defaultConfig.targetSdk = 35 defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testOptions.animationsDisabled = true configureFlavors(this) @@ -53,10 +52,10 @@ class AndroidLibraryConventionPlugin : Plugin { disableUnnecessaryAndroidTests(target) } dependencies { - add("androidTestImplementation", libs.findLibrary("kotlin.test").get()) - add("testImplementation", libs.findLibrary("kotlin.test").get()) + "androidTestImplementation"(libs.findLibrary("kotlin.test").get()) + "testImplementation"(libs.findLibrary("kotlin.test").get()) - add("implementation", libs.findLibrary("androidx.tracing.ktx").get()) + "implementation"(libs.findLibrary("androidx.tracing.ktx").get()) } } } diff --git a/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt index 1734df930..1a01a7725 100644 --- a/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt @@ -42,5 +42,7 @@ class AndroidLintConventionPlugin : Plugin { private fun Lint.configure() { xmlReport = true + sarifReport = true checkDependencies = true + disable += "GradleDependency" } diff --git a/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt index dbca79a5e..5a8d648f3 100644 --- a/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt @@ -41,10 +41,10 @@ class AndroidRoomConventionPlugin : Plugin { } dependencies { - add("implementation", libs.findLibrary("room.runtime").get()) - add("implementation", libs.findLibrary("room.ktx").get()) - add("ksp", libs.findLibrary("room.compiler").get()) + "implementation"(libs.findLibrary("room.runtime").get()) + "implementation"(libs.findLibrary("room.ktx").get()) + "ksp"(libs.findLibrary("room.compiler").get()) } } } -} \ No newline at end of file +} diff --git a/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt index e48d75757..8a084598f 100644 --- a/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt @@ -31,7 +31,7 @@ class AndroidTestConventionPlugin : Plugin { extensions.configure { configureKotlinAndroid(this) - defaultConfig.targetSdk = 34 + defaultConfig.targetSdk = 35 configureGradleManagedDevices(this) } } diff --git a/build-logic/convention/src/main/kotlin/HiltConventionPlugin.kt b/build-logic/convention/src/main/kotlin/HiltConventionPlugin.kt index f49366f75..24d8c3382 100644 --- a/build-logic/convention/src/main/kotlin/HiltConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/HiltConventionPlugin.kt @@ -25,13 +25,13 @@ class HiltConventionPlugin : Plugin { with(target) { pluginManager.apply("com.google.devtools.ksp") dependencies { - add("ksp", libs.findLibrary("hilt.compiler").get()) + "ksp"(libs.findLibrary("hilt.compiler").get()) } // Add support for Jvm Module, base on org.jetbrains.kotlin.jvm pluginManager.withPlugin("org.jetbrains.kotlin.jvm") { dependencies { - add("implementation", libs.findLibrary("hilt.core").get()) + "implementation"(libs.findLibrary("hilt.core").get()) } } @@ -39,7 +39,7 @@ class HiltConventionPlugin : Plugin { pluginManager.withPlugin("com.android.base") { pluginManager.apply("dagger.hilt.android.plugin") dependencies { - add("implementation", libs.findLibrary("hilt.android").get()) + "implementation"(libs.findLibrary("hilt.android").get()) } } } diff --git a/build-logic/convention/src/main/kotlin/JvmLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/JvmLibraryConventionPlugin.kt index afe47eeee..f9ae4a1c4 100644 --- a/build-logic/convention/src/main/kotlin/JvmLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/JvmLibraryConventionPlugin.kt @@ -19,7 +19,6 @@ import com.google.samples.apps.nowinandroid.libs import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.kotlin class JvmLibraryConventionPlugin : Plugin { override fun apply(target: Project) { @@ -30,7 +29,7 @@ class JvmLibraryConventionPlugin : Plugin { } configureKotlinJvm() dependencies { - add("testImplementation", libs.findLibrary("kotlin.test").get()) + "testImplementation"(libs.findLibrary("kotlin.test").get()) } } } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt index ffb6358c3..ed2a5289b 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidCompose.kt @@ -37,10 +37,10 @@ internal fun Project.configureAndroidCompose( dependencies { val bom = libs.findLibrary("androidx-compose-bom").get() - add("implementation", platform(bom)) - add("androidTestImplementation", platform(bom)) - add("implementation", libs.findLibrary("androidx-compose-ui-tooling-preview").get()) - add("debugImplementation", libs.findLibrary("androidx-compose-ui-tooling").get()) + "implementation"(platform(bom)) + "androidTestImplementation"(platform(bom)) + "implementation"(libs.findLibrary("androidx-compose-ui-tooling-preview").get()) + "debugImplementation"(libs.findLibrary("androidx-compose-ui-tooling").get()) } testOptions { @@ -53,8 +53,10 @@ internal fun Project.configureAndroidCompose( extensions.configure { fun Provider.onlyIfTrue() = flatMap { provider { it.takeIf(String::toBoolean) } } - fun Provider<*>.relativeToRootProject(dir: String) = flatMap { - rootProject.layout.buildDirectory.dir(projectDir.toRelativeString(rootDir)) + fun Provider<*>.relativeToRootProject(dir: String) = map { + isolated.rootProject.projectDirectory + .dir("build") + .dir(projectDir.toRelativeString(rootDir)) }.map { it.dir(dir) } project.providers.gradleProperty("enableComposeCompilerMetrics").onlyIfTrue() @@ -65,7 +67,7 @@ internal fun Project.configureAndroidCompose( .relativeToRootProject("compose-reports") .let(reportsDestination::set) - stabilityConfigurationFile = - rootProject.layout.projectDirectory.file("compose_compiler_config.conf") + stabilityConfigurationFiles + .add(isolated.rootProject.projectDirectory.file("compose_compiler_config.conf")) } } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt index 9eabff31b..886c70625 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt @@ -39,8 +39,6 @@ import org.gradle.kotlin.dsl.assign import org.gradle.kotlin.dsl.register import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.process.ExecOperations -import java.io.File -import java.util.Locale import javax.inject.Inject @CacheableTask @@ -123,15 +121,17 @@ fun Project.configureBadgingTasks( val generateBadging = tasks.register(generateBadgingTaskName) { apk = variant.artifacts.get(SingleArtifact.APK_FROM_BUNDLE) - - aapt2Executable = File( - baseExtension.sdkDirectory, - "${SdkConstants.FD_BUILD_TOOLS}/" + - "${baseExtension.buildToolsVersion}/" + - SdkConstants.FN_AAPT2, + aapt2Executable.set( + // TODO: Replace with `sdkComponents.aapt2` when it's available in AGP + // https://issuetracker.google.com/issues/376815836 + componentsExtension.sdkComponents.sdkDirectory.map { directory -> + directory.file( + "${SdkConstants.FD_BUILD_TOOLS}/" + + "${baseExtension.buildToolsVersion}/" + + SdkConstants.FN_AAPT2, + ) + } ) - - badging = project.layout.buildDirectory.file( "outputs/apk_from_bundle/${variant.name}/${variant.name}-badging.txt", ) @@ -140,7 +140,7 @@ fun Project.configureBadgingTasks( val updateBadgingTaskName = "update${capitalizedVariantName}Badging" tasks.register(updateBadgingTaskName) { - from(generateBadging.get().badging) + from(generateBadging.map(GenerateBadgingTask::badging)) into(project.layout.projectDirectory) } @@ -148,7 +148,7 @@ fun Project.configureBadgingTasks( tasks.register(checkBadgingTaskName) { goldenBadging = project.layout.projectDirectory.file("${variant.name}-badging.txt") - generatedBadging = generateBadging.get().badging + generatedBadging.set(generateBadging.flatMap(GenerateBadgingTask::badging)) this.updateBadgingTaskName = updateBadgingTaskName 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 972d539c6..ed1ea4254 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 @@ -19,10 +19,12 @@ 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 com.android.build.api.variant.SourceDirectories 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.provider.Provider import org.gradle.api.tasks.testing.Test import org.gradle.kotlin.dsl.assign import org.gradle.kotlin.dsl.configure @@ -88,11 +90,14 @@ internal fun Project.configureJacoco( html.required = true } - // TODO: This is missing files in src/debug/, src/prod, src/demo, src/demoDebug... + fun SourceDirectories.Flat?.toFilePaths(): Provider> = this + ?.all + ?.map { directories -> directories.map { it.asFile.path } } + ?: provider { emptyList() } sourceDirectories.setFrom( files( - "$projectDir/src/main/java", - "$projectDir/src/main/kotlin", + variant.sources.java.toFilePaths(), + variant.sources.kotlin.toFilePaths() ), ) diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt index bfb799595..8a41dbfcc 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinAndroid.kt @@ -26,8 +26,8 @@ import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.provideDelegate import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension /** * Configure base Kotlin with Android options @@ -36,7 +36,7 @@ internal fun Project.configureKotlinAndroid( commonExtension: CommonExtension<*, *, *, *, *, *>, ) { commonExtension.apply { - compileSdk = 34 + compileSdk = 35 defaultConfig { minSdk = 21 @@ -54,7 +54,7 @@ internal fun Project.configureKotlinAndroid( configureKotlin() dependencies { - add("coreLibraryDesugaring", libs.findLibrary("android.desugarJdkLibs").get()) + "coreLibraryDesugaring"(libs.findLibrary("android.desugarJdkLibs").get()) } } @@ -75,7 +75,7 @@ internal fun Project.configureKotlinJvm() { /** * Configure base Kotlin options */ -private inline fun Project.configureKotlin() = configure { +private inline fun Project.configureKotlin() = configure { // Treat all Kotlin warnings as errors (disabled by default) // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties val warningsAsErrors: String? by project @@ -90,5 +90,19 @@ private inline fun Project.configureKotlin // Enable experimental coroutines APIs, including Flow "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", ) + freeCompilerArgs.add( + /** + * Remove this args after Phase 3. + * https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-consistent-copy-visibility/#deprecation-timeline + * + * Deprecation timeline + * Phase 3. (Supposedly Kotlin 2.2 or Kotlin 2.3). + * The default changes. + * Unless ExposedCopyVisibility is used, the generated 'copy' method has the same visibility as the primary constructor. + * The binary signature changes. The error on the declaration is no longer reported. + * '-Xconsistent-data-class-copy-visibility' compiler flag and ConsistentCopyVisibility annotation are now unnecessary. + */ + "-Xconsistent-data-class-copy-visibility" + ) } } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt index 633098604..f57e634cc 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaFlavor.kt @@ -16,23 +16,26 @@ enum class FlavorDimension { @Suppress("EnumEntryName") enum class NiaFlavor(val dimension: FlavorDimension, val applicationIdSuffix: String? = null) { demo(FlavorDimension.contentType, applicationIdSuffix = ".demo"), - prod(FlavorDimension.contentType) + prod(FlavorDimension.contentType), } fun configureFlavors( commonExtension: CommonExtension<*, *, *, *, *, *>, - flavorConfigurationBlock: ProductFlavor.(flavor: NiaFlavor) -> Unit = {} + flavorConfigurationBlock: ProductFlavor.(flavor: NiaFlavor) -> Unit = {}, ) { commonExtension.apply { - flavorDimensions += FlavorDimension.contentType.name + FlavorDimension.values().forEach { flavorDimension -> + flavorDimensions += flavorDimension.name + } + productFlavors { - NiaFlavor.values().forEach { - create(it.name) { - dimension = it.dimension.name - flavorConfigurationBlock(this, it) + NiaFlavor.values().forEach { niaFlavor -> + register(niaFlavor.name) { + dimension = niaFlavor.dimension.name + flavorConfigurationBlock(this, niaFlavor) if (this@apply is ApplicationExtension && this is ApplicationProductFlavor) { - if (it.applicationIdSuffix != null) { - applicationIdSuffix = it.applicationIdSuffix + if (niaFlavor.applicationIdSuffix != null) { + applicationIdSuffix = niaFlavor.applicationIdSuffix } } } diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts index b359a5207..ff96cc84a 100644 --- a/build-logic/settings.gradle.kts +++ b/build-logic/settings.gradle.kts @@ -14,6 +14,13 @@ * limitations under the License. */ +pluginManagement { + repositories { + gradlePluginPortal() + google() + } +} + dependencyResolutionManagement { repositories { google { diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index 8ca52fea2..aef9ca554 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -31,7 +31,6 @@ android { testOptions { unitTests { isIncludeAndroidResources = true - isReturnDefaultValues = true } } } diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts index b17bf6abd..0d4ba37c5 100644 --- a/core/datastore/build.gradle.kts +++ b/core/datastore/build.gradle.kts @@ -25,11 +25,6 @@ android { consumerProguardFiles("consumer-proguard-rules.pro") } namespace = "com.google.samples.apps.nowinandroid.core.datastore" - testOptions { - unitTests { - isReturnDefaultValues = true - } - } } dependencies { diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index 31635865c..aac2ddb8f 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -21,9 +21,6 @@ plugins { } android { - defaultConfig { - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } namespace = "com.google.samples.apps.nowinandroid.core.designsystem" } @@ -47,6 +44,4 @@ dependencies { testImplementation(libs.hilt.android.testing) testImplementation(libs.robolectric) testImplementation(projects.core.screenshotTesting) - - androidTestImplementation(libs.bundles.androidx.compose.ui.test) } diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt index 4ac19b482..4a2099dc6 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/Navigation.kt @@ -28,10 +28,8 @@ import androidx.compose.material3.NavigationRail import androidx.compose.material3.NavigationRailItem import androidx.compose.material3.NavigationRailItemDefaults import androidx.compose.material3.Text -import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo -import androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteDefaults import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteItemColors import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold @@ -184,10 +182,6 @@ fun NiaNavigationRail( * @param windowAdaptiveInfo The window adaptive info. * @param content The app content inside the scaffold. */ -@OptIn( - ExperimentalMaterial3AdaptiveNavigationSuiteApi::class, - ExperimentalMaterial3AdaptiveApi::class, -) @Composable fun NiaNavigationSuiteScaffold( navigationSuiteItems: NiaNavigationSuiteScope.() -> Unit, @@ -242,7 +236,6 @@ fun NiaNavigationSuiteScaffold( /** * A wrapper around [NavigationSuiteScope] to declare navigation items. */ -@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class) class NiaNavigationSuiteScope internal constructor( private val navigationSuiteScope: NavigationSuiteScope, private val navigationSuiteItemColors: NavigationSuiteItemColors, diff --git a/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_dynamic.png index 67cafa03d..6a9a51fe6 100644 Binary files a/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_notDynamic.png index 3f187d9d2..04de28429 100644 Binary files a/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Background/Background_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/Background_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Background/Background_light_androidTheme_notDynamic.png index ebcf62c08..758c35dce 100644 Binary files a/core/designsystem/src/test/screenshots/Background/Background_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Background/Background_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_dynamic.png index 7f910a34b..1b16b8fc4 100644 Binary files a/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_notDynamic.png index 912480c6a..3d802687a 100644 Binary files a/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Background/Background_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png index a9b2c8694..6e30d8b63 100644 Binary files a/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png index f88a672c4..ca46d579b 100644 Binary files a/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png index ebcf62c08..758c35dce 100644 Binary files a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png index 6fef6436a..d1961cd01 100644 Binary files a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png index e619f1332..a9baaaac3 100644 Binary files a/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png index cf0656fbd..aadd4c9dc 100644 Binary files a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png index 9514112f1..01de6984a 100644 Binary files a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png index 7774a18bc..58f94b221 100644 Binary files a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png index 8858fb493..c7b6cee37 100644 Binary files a/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/Button_dark_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Button/Button_dark_androidTheme_notDynamic.png index a5d3d4a3d..0883a4497 100644 Binary files a/core/designsystem/src/test/screenshots/Button/Button_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Button/Button_dark_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_dynamic.png index 01538b44b..1086161e0 100644 Binary files a/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_notDynamic.png index cd0c07df1..fc12d5a7d 100644 Binary files a/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Button/Button_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/Button_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Button/Button_light_androidTheme_notDynamic.png index ab113beec..2863a06d3 100644 Binary files a/core/designsystem/src/test/screenshots/Button/Button_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Button/Button_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_dynamic.png index fdbbb820d..f67bd4342 100644 Binary files a/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_notDynamic.png index b567adf84..fad380492 100644 Binary files a/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Button/Button_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png index a9ba099c0..a113e51bc 100644 Binary files a/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png index 6fce27976..c1bd448e1 100644 Binary files a/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png index ce30b66ba..8855a359e 100644 Binary files a/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png index bb6aa592f..d6d5bd628 100644 Binary files a/core/designsystem/src/test/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png index c18a86878..50a6570f1 100644 Binary files a/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png index d2059e4d7..ea96acea3 100644 Binary files a/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png index 5881f76b7..464be9cb4 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png index 4b5c91914..129bd707f 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png index f2e863865..54e665cf9 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png index 1e3b04e50..0e35be51d 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png index 865368ca1..01e8d9690 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png index be73f060d..55d3a80fa 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png index 364f59a47..568e41d78 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png index 8f90977fd..27c1f4195 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png index 5303eb64e..5d86614f2 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_fontScale2.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_fontScale2.png index 2dc430ca8..fbd4109cd 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_fontScale2.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_fontScale2.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png index fadd074d8..c4015dbc6 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png index 2f3749cf3..df97855f0 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png index 0cfaaefae..28a1c895f 100644 Binary files a/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png index a1512fa75..8e372685a 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png index fe4b54ae2..436c8d999 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png index f912ce3c1..536e5cf25 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png index 339479779..abc882504 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png index 92079273a..5628b5698 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png index 24580adf2..f88fa1d08 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png index ce2cdf804..d217e1116 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png index ce2cdf804..d217e1116 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png index ce2cdf804..d217e1116 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png index ce2cdf804..d217e1116 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png index ce2cdf804..d217e1116 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png index ce2cdf804..d217e1116 100644 Binary files a/core/designsystem/src/test/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_1000.png b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_1000.png index 450d55a09..316d899d2 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_1000.png and b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_1000.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_115.png b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_115.png index 5aa1eb89a..4705b9beb 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_115.png and b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_115.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_20.png b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_20.png index 74309056f..203a2ea2e 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_20.png and b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_20.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_724.png b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_724.png index 03bf6709e..50a822621 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_724.png and b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_animation_724.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png index cf35893fd..d266c68f0 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png index 013aac763..87ae3ac55 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png index 19265495c..73ad8f0fb 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png index 156fc1983..17b56813d 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png index 36d79ab6c..e0b86ebb6 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png index 89ea5e37c..e1b73d22b 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png index ac0065cc7..fbfa9fd0a 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png index 0133bc71a..dcd3b3eca 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png index 2f1d9767c..8f1a9343d 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png index d90547cd8..e66b491a3 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png index 142051d68..daa5ee299 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png index 6949a8908..b922f641b 100644 Binary files a/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png index b2a0fb99c..5e2a27c65 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png index 8836faebc..efb1f8756 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png index a4abd2d5b..160a72d61 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_fontScale2.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_fontScale2.png index 97bbb0892..b4b4842c3 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_fontScale2.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_fontScale2.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png index a526e36c7..c3200091b 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png index 5e27d2497..877c45cd0 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png index f5671cb14..9fd6fc843 100644 Binary files a/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png index 15cb061a0..3c5fffc04 100644 Binary files a/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_fontScale2.png b/core/designsystem/src/test/screenshots/Tabs/Tabs_fontScale2.png index f62ea3ced..d7feb863b 100644 Binary files a/core/designsystem/src/test/screenshots/Tabs/Tabs_fontScale2.png and b/core/designsystem/src/test/screenshots/Tabs/Tabs_fontScale2.png differ diff --git a/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png index 0564b3881..1e3270f94 100644 Binary files a/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png index 522dcd301..2a065eb57 100644 Binary files a/core/designsystem/src/test/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png index 13345c365..c532a307e 100644 Binary files a/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png index d6cfb48d0..61d3321b8 100644 Binary files a/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_fontScale2.png b/core/designsystem/src/test/screenshots/Tag/Tag_fontScale2.png index 475707556..7cfdbb2a7 100644 Binary files a/core/designsystem/src/test/screenshots/Tag/Tag_fontScale2.png and b/core/designsystem/src/test/screenshots/Tag/Tag_fontScale2.png differ diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Tag/Tag_light_androidTheme_notDynamic.png index 38ebe8b42..fc29b6e11 100644 Binary files a/core/designsystem/src/test/screenshots/Tag/Tag_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Tag/Tag_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_dynamic.png index 00144ba15..ed825ba71 100644 Binary files a/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png index 53b1da266..8acc6b0a8 100644 Binary files a/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png index 753c13605..3617feab1 100644 Binary files a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png index 1c2d9b3ec..418e0f88d 100644 Binary files a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png index 1baa2362c..921698d22 100644 Binary files a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_fontScale2.png b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_fontScale2.png index 234304db1..497561ef7 100644 Binary files a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_fontScale2.png and b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_fontScale2.png differ diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png index 7c0348b04..4988252e5 100644 Binary files a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png differ diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png index fbf61adc4..6ab40ebf9 100644 Binary files a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png and b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png differ diff --git a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png index 078378bea..d9d014a18 100644 Binary files a/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png differ diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserNewsResource.kt b/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserNewsResource.kt index 77dfa4394..a56bbcb8d 100644 --- a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserNewsResource.kt +++ b/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserNewsResource.kt @@ -22,7 +22,6 @@ import kotlinx.datetime.Instant * A [NewsResource] with additional user information such as whether the user is following the * news resource's topics and whether they have saved (bookmarked) this news resource. */ -@ConsistentCopyVisibility data class UserNewsResource internal constructor( val id: String, val title: String, diff --git a/core/screenshot-testing/build.gradle.kts b/core/screenshot-testing/build.gradle.kts index 794416ba9..57a43a200 100644 --- a/core/screenshot-testing/build.gradle.kts +++ b/core/screenshot-testing/build.gradle.kts @@ -26,6 +26,7 @@ android { dependencies { api(libs.bundles.androidx.compose.ui.test) api(libs.roborazzi) + api(libs.roborazzi.accessibility.check) implementation(libs.androidx.compose.ui.test) implementation(libs.androidx.activity.compose) implementation(libs.robolectric) diff --git a/core/screenshot-testing/src/main/AndroidManifest.xml b/core/screenshot-testing/src/main/AndroidManifest.xml index 51d0cfc2e..5952f8bfd 100644 --- a/core/screenshot-testing/src/main/AndroidManifest.xml +++ b/core/screenshot-testing/src/main/AndroidManifest.xml @@ -14,4 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. --> - \ No newline at end of file + + + + \ No newline at end of file diff --git a/core/screenshot-testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt b/core/screenshot-testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt index f11651220..20b9a5deb 100644 --- a/core/screenshot-testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt +++ b/core/screenshot-testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/ScreenshotHelper.kt @@ -14,8 +14,11 @@ * limitations under the License. */ +@file:OptIn(ExperimentalRoborazziApi::class) + package com.google.samples.apps.nowinandroid.core.testing.util +import android.graphics.Bitmap.CompressFormat.PNG import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.runtime.Composable @@ -30,12 +33,25 @@ import androidx.compose.ui.test.DeviceConfigurationOverride import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.onRoot import androidx.test.ext.junit.rules.ActivityScenarioRule +import com.github.takahirom.roborazzi.ExperimentalRoborazziApi +import com.github.takahirom.roborazzi.RoborazziATFAccessibilityCheckOptions +import com.github.takahirom.roborazzi.RoborazziATFAccessibilityChecker +import com.github.takahirom.roborazzi.RoborazziATFAccessibilityChecker.CheckLevel import com.github.takahirom.roborazzi.RoborazziOptions import com.github.takahirom.roborazzi.RoborazziOptions.CompareOptions import com.github.takahirom.roborazzi.RoborazziOptions.RecordOptions import com.github.takahirom.roborazzi.captureRoboImage +import com.github.takahirom.roborazzi.checkRoboAccessibility +import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckPreset +import com.google.android.apps.common.testing.accessibility.framework.AccessibilityViewCheckResult +import com.google.android.apps.common.testing.accessibility.framework.integrations.espresso.AccessibilityViewCheckException +import com.google.android.apps.common.testing.accessibility.framework.utils.contrast.BitmapImage import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import org.hamcrest.Matcher +import org.hamcrest.Matchers import org.robolectric.RuntimeEnvironment +import java.io.File +import java.io.FileOutputStream val DefaultRoborazziOptions = RoborazziOptions( @@ -52,10 +68,17 @@ enum class DefaultTestDevices(val description: String, val spec: String) { } fun AndroidComposeTestRule, A>.captureMultiDevice( screenshotName: String, + accessibilitySuppressions: Matcher = Matchers.not(Matchers.anything()), body: @Composable () -> Unit, ) { DefaultTestDevices.entries.forEach { - this.captureForDevice(it.description, it.spec, screenshotName, body = body) + this.captureForDevice( + deviceName = it.description, + deviceSpec = it.spec, + screenshotName = screenshotName, + body = body, + accessibilitySuppressions = accessibilitySuppressions, + ) } } @@ -64,6 +87,7 @@ fun AndroidComposeTestRule, A>.c deviceSpec: String, screenshotName: String, roborazziOptions: RoborazziOptions = DefaultRoborazziOptions, + accessibilitySuppressions: Matcher = Matchers.not(Matchers.anything()), darkMode: Boolean = false, body: @Composable () -> Unit, ) { @@ -83,11 +107,46 @@ fun AndroidComposeTestRule, A>.c } } } + + // Run Accessibility checks first so logging is included + val accessibilityException = try { + this.onRoot().checkRoboAccessibility( + roborazziATFAccessibilityCheckOptions = RoborazziATFAccessibilityCheckOptions( + failureLevel = CheckLevel.Error, + checker = RoborazziATFAccessibilityChecker( + preset = AccessibilityCheckPreset.LATEST, + suppressions = accessibilitySuppressions, + ), + ), + ) + null + } catch (e: AccessibilityViewCheckException) { + e + } + this.onRoot() .captureRoboImage( "src/test/screenshots/${screenshotName}_$deviceName.png", roborazziOptions = roborazziOptions, ) + + // Rethrow the Accessibility exception once screenshots have passed + if (accessibilityException != null) { + accessibilityException.results.forEachIndexed { index, check -> + val viewImage = check.viewImage + if (viewImage is BitmapImage) { + val file = File("build/outputs/roborazzi/${screenshotName}_${deviceName}_$index.png") + println("Writing check.viewImage to $file") + FileOutputStream( + file, + ).use { + viewImage.bitmap.compress(PNG, 100, it) + } + } + } + + throw accessibilityException + } } /** diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt index c22a02fa1..0b7b9f570 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt @@ -21,7 +21,6 @@ import android.net.Uri import androidx.annotation.ColorInt import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabsIntent -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope @@ -45,7 +44,6 @@ import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource * An extension on [LazyListScope] defining a feed with news resources. * Depending on the [feedState], this might emit no items. */ -@OptIn(ExperimentalFoundationApi::class) fun LazyStaggeredGridScope.newsFeed( feedState: NewsFeedUiState, onNewsResourcesCheckedChanged: (String, Boolean) -> Unit, diff --git a/docs/images/screenshot-1-foryou.png b/docs/images/screenshot-1-foryou.png deleted file mode 100644 index 0d76fce79..000000000 Binary files a/docs/images/screenshot-1-foryou.png and /dev/null differ diff --git a/docs/images/screenshot-2-interests.png b/docs/images/screenshot-2-interests.png deleted file mode 100644 index 1137e59c3..000000000 Binary files a/docs/images/screenshot-2-interests.png and /dev/null differ diff --git a/docs/images/screenshot-3-topicdetail.png b/docs/images/screenshot-3-topicdetail.png deleted file mode 100644 index d5150d376..000000000 Binary files a/docs/images/screenshot-3-topicdetail.png and /dev/null differ diff --git a/docs/images/screenshots.png b/docs/images/screenshots.png index f92fe6455..3f143bc6c 100644 Binary files a/docs/images/screenshots.png and b/docs/images/screenshots.png differ 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 19ab0ad85..aa42adae2 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 @@ -31,7 +31,9 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertIs +import kotlin.test.assertTrue /** * To learn more about how this test handles Flows created with stateIn, see @@ -86,5 +88,49 @@ class BookmarksViewModelTest { val item = viewModel.feedUiState.value assertIs(item) assertEquals(item.feed.size, 0) + assertTrue(viewModel.shouldDisplayUndoBookmark) + } + + @Test + fun feedUiState_resourceIsViewed_setResourcesViewed() = runTest { + backgroundScope.launch(UnconfinedTestDispatcher()) { viewModel.feedUiState.collect() } + + // Given + newsRepository.sendNewsResources(newsResourcesTestData) + userDataRepository.setNewsResourceBookmarked(newsResourcesTestData[0].id, true) + val itemBeforeViewed = viewModel.feedUiState.value + assertIs(itemBeforeViewed) + assertFalse(itemBeforeViewed.feed.first().hasBeenViewed) + + // When + viewModel.setNewsResourceViewed(newsResourcesTestData[0].id, true) + + // Then + val item = viewModel.feedUiState.value + assertIs(item) + assertTrue(item.feed.first().hasBeenViewed) + } + + @Test + fun feedUiState_undoneBookmarkRemoval_bookmarkIsRestored() = runTest { + backgroundScope.launch(UnconfinedTestDispatcher()) { viewModel.feedUiState.collect() } + + // Given + newsRepository.sendNewsResources(newsResourcesTestData) + userDataRepository.setNewsResourceBookmarked(newsResourcesTestData[0].id, true) + viewModel.removeFromSavedResources(newsResourcesTestData[0].id) + assertTrue(viewModel.shouldDisplayUndoBookmark) + val itemBeforeUndo = viewModel.feedUiState.value + assertIs(itemBeforeUndo) + assertEquals(0, itemBeforeUndo.feed.size) + + // When + viewModel.undoBookmarkRemoval() + + // Then + assertFalse(viewModel.shouldDisplayUndoBookmark) + val item = viewModel.feedUiState.value + assertIs(item) + assertEquals(1, item.feed.size) } } diff --git a/feature/foryou/build.gradle.kts b/feature/foryou/build.gradle.kts index 41d5b16a2..59f6844cf 100644 --- a/feature/foryou/build.gradle.kts +++ b/feature/foryou/build.gradle.kts @@ -29,7 +29,7 @@ dependencies { implementation(libs.accompanist.permissions) implementation(projects.core.data) implementation(projects.core.domain) - implementation(project(":core:notifications")) + implementation(projects.core.notifications) testImplementation(libs.hilt.android.testing) testImplementation(libs.robolectric) diff --git a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt index 14b67c64e..29fc6f536 100644 --- a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt +++ b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt @@ -19,6 +19,10 @@ package com.google.samples.apps.nowinandroid.feature.foryou import androidx.activity.ComponentActivity import androidx.compose.runtime.Composable import androidx.compose.ui.test.junit4.createAndroidComposeRule +import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils +import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesElements +import com.google.android.apps.common.testing.accessibility.framework.checks.TextContrastCheck +import com.google.android.apps.common.testing.accessibility.framework.matcher.ElementMatchers.withText import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.testing.util.DefaultTestDevices @@ -31,6 +35,7 @@ import com.google.samples.apps.nowinandroid.feature.foryou.OnboardingUiState.Loa import com.google.samples.apps.nowinandroid.feature.foryou.OnboardingUiState.NotShown import com.google.samples.apps.nowinandroid.feature.foryou.OnboardingUiState.Shown import dagger.hilt.android.testing.HiltTestApplication +import org.hamcrest.Matchers import org.junit.Before import org.junit.Rule import org.junit.Test @@ -108,7 +113,20 @@ class ForYouScreenScreenshotTests { @Test fun forYouScreenTopicSelection() { - composeTestRule.captureMultiDevice("ForYouScreenTopicSelection") { + composeTestRule.captureMultiDevice( + "ForYouScreenTopicSelection", + accessibilitySuppressions = Matchers.allOf( + AccessibilityCheckResultUtils.matchesCheck(TextContrastCheck::class.java), + Matchers.anyOf( + // Disabled Button + matchesElements(withText("Done")), + + // TODO investigate, seems a false positive + matchesElements(withText("What are you interested in?")), + matchesElements(withText("UI")), + ), + ), + ) { ForYouScreenTopicSelection() } } diff --git a/feature/foryou/src/test/screenshots/ForYouScreenLoading_foldable.png b/feature/foryou/src/test/screenshots/ForYouScreenLoading_foldable.png index 92d2978e0..a699345c2 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenLoading_foldable.png and b/feature/foryou/src/test/screenshots/ForYouScreenLoading_foldable.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenLoading_phone.png b/feature/foryou/src/test/screenshots/ForYouScreenLoading_phone.png index 0e6aedd53..30c8fdad7 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenLoading_phone.png and b/feature/foryou/src/test/screenshots/ForYouScreenLoading_phone.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenLoading_tablet.png b/feature/foryou/src/test/screenshots/ForYouScreenLoading_tablet.png index 88b6ce240..f54966dbc 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenLoading_tablet.png and b/feature/foryou/src/test/screenshots/ForYouScreenLoading_tablet.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_foldable.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_foldable.png index 1972b1ca2..3990aee3f 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_foldable.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_foldable.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone.png index 16df589f9..a86a8232f 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png index d28704e49..d500394ef 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_tablet.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_tablet.png index c2a01f2d8..3b735462f 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_tablet.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedAndLoading_tablet.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_foldable.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_foldable.png index 0b539aeca..0da7e19ac 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_foldable.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_foldable.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_phone.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_phone.png index b19c8d708..a7b775315 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_phone.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_phone.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_tablet.png b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_tablet.png index bdf44b2a3..e7f0cc050 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_tablet.png and b/feature/foryou/src/test/screenshots/ForYouScreenPopulatedFeed_tablet.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_foldable.png b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_foldable.png index b095c1a7a..f5d68fce2 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_foldable.png and b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_foldable.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone.png b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone.png index 140fa8d6d..b04c632c3 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone.png and b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone_dark.png b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone_dark.png index 5d90732a0..6c4d66b28 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone_dark.png and b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_phone_dark.png differ diff --git a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_tablet.png b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_tablet.png index 3dd62e765..f7c20a890 100644 Binary files a/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_tablet.png and b/feature/foryou/src/test/screenshots/ForYouScreenTopicSelection_tablet.png differ diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt index 81f3576b4..3b16e5f71 100644 --- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt +++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/navigation/SearchNavigation.kt @@ -21,10 +21,12 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.google.samples.apps.nowinandroid.feature.search.SearchRoute +import kotlinx.serialization.Serializable -const val SEARCH_ROUTE = "search_route" +@Serializable data object SearchRoute -fun NavController.navigateToSearch(navOptions: NavOptions? = null) = navigate(SEARCH_ROUTE, navOptions) +fun NavController.navigateToSearch(navOptions: NavOptions? = null) = + navigate(SearchRoute, navOptions) fun NavGraphBuilder.searchScreen( onBackClick: () -> Unit, @@ -33,7 +35,7 @@ fun NavGraphBuilder.searchScreen( ) { // TODO: Handle back stack for each top-level destination. At the moment each top-level // destination may have own search screen's back stack. - composable(route = SEARCH_ROUTE) { + composable { SearchRoute( onBackClick = onBackClick, onInterestsClick = onInterestsClick, diff --git a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt index db60a6447..ad7f30f43 100644 --- a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt +++ b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt @@ -20,7 +20,6 @@ package com.google.samples.apps.nowinandroid.feature.settings import android.content.Intent import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope @@ -141,14 +140,16 @@ fun SettingsDialog( TrackScreenViewEvent(screenName = "Settings") }, confirmButton = { - Text( - text = stringResource(string.feature_settings_dismiss_dialog_button_text), - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.primary, - modifier = Modifier - .padding(horizontal = 8.dp) - .clickable { onDismiss() }, - ) + NiaTextButton( + onClick = onDismiss, + modifier = Modifier.padding(horizontal = 8.dp), + ) { + Text( + text = stringResource(string.feature_settings_dismiss_dialog_button_text), + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.primary, + ) + } }, ) } diff --git a/generateModuleGraphs.sh b/generateModuleGraphs.sh index 3c3583e67..5307d2932 100755 --- a/generateModuleGraphs.sh +++ b/generateModuleGraphs.sh @@ -27,7 +27,8 @@ 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'" + echo " - On Ubuntu: You can install Graphviz using APT with the command: 'sudo apt install graphviz'" + echo " - Others: Visit https://graphviz.org/download/" exit 1 fi diff --git a/gradle.properties b/gradle.properties index e2d6e0903..30c4eae04 100644 --- a/gradle.properties +++ b/gradle.properties @@ -58,3 +58,7 @@ android.defaults.buildfeatures.shaders=false # Run Roborazzi screenshot tests with the local tests roborazzi.test.verify=true + +# Prevent uninstall app after instrumented tests +# https://issuetracker.google.com/issues/295039976 +android.injected.androidTest.leaveApksInstalledAfterRun=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 25a7183ad..5ac8236e9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,24 +1,25 @@ [versions] -accompanist = "0.36.0" -androidDesugarJdkLibs = "2.0.4" +accompanist = "0.37.0" +androidDesugarJdkLibs = "2.1.3" # AGP and tools should be updated together -androidGradlePlugin = "8.6.1" -androidTools = "31.7.2" +androidGradlePlugin = "8.7.3" +androidTools = "31.7.3" androidxActivity = "1.9.3" androidxAppCompat = "1.7.0" androidxBrowser = "1.8.0" -androidxComposeBom = "2024.11.00" -androidxComposeRuntimeTracing = "1.7.5" -androidxCore = "1.13.1" +androidxComposeBom = "2024.12.01" +androidxComposeRuntimeTracing = "1.7.6" +androidxCore = "1.15.0" androidxCoreSplashscreen = "1.0.1" androidxDataStore = "1.1.1" androidxEspresso = "3.6.1" androidxHiltNavigationCompose = "1.2.0" -androidxLifecycle = "2.8.6" -androidxMacroBenchmark = "1.3.1" +androidxLifecycle = "2.8.7" +androidxLintGradle = "1.0.0-alpha03" +androidxMacroBenchmark = "1.3.3" androidxMetrics = "1.0.0-beta01" -androidxNavigation = "2.8.0" -androidxProfileinstaller = "1.3.1" +androidxNavigation = "2.8.5" +androidxProfileinstaller = "1.4.1" androidxTestCore = "1.6.1" androidxTestExt = "1.2.1" androidxTestRules = "1.6.1" @@ -26,36 +27,36 @@ androidxTestRunner = "1.6.2" androidxTracing = "1.3.0-alpha02" androidxUiAutomator = "2.3.0" androidxWindowManager = "1.3.0" -androidxWork = "2.9.0" +androidxWork = "2.10.0" coil = "2.7.0" dependencyGuard = "0.5.0" -firebaseBom = "33.3.0" +firebaseBom = "33.7.0" firebaseCrashlyticsPlugin = "3.0.2" firebasePerfPlugin = "1.4.2" -gmsPlugin = "4.4.1" +gmsPlugin = "4.4.2" googleOss = "17.1.0" googleOssPlugin = "0.10.6" -hilt = "2.52" +hilt = "2.53.1" hiltExt = "1.2.0" jacoco = "0.8.12" junit4 = "4.13.2" -kotlin = "2.0.20" +kotlin = "2.1.0" kotlinxCoroutines = "1.9.0" kotlinxDatetime = "0.6.1" -kotlinxSerializationJson = "1.6.3" -ksp = "2.0.20-1.0.25" +kotlinxSerializationJson = "1.7.3" +ksp = "2.1.0-1.0.29" moduleGraph = "2.7.1" okhttp = "4.12.0" -protobuf = "4.28.2" +protobuf = "4.29.2" protobufPlugin = "0.9.4" retrofit = "2.11.0" retrofitKotlinxSerializationJson = "1.0.0" -robolectric = "4.11.1" -roborazzi = "1.7.0" +robolectric = "4.14.1" +roborazzi = "1.37.0" room = "2.6.1" secrets = "2.0.1" truth = "1.4.4" -turbine = "1.1.0" +turbine = "1.2.0" [bundles] androidx-compose-ui-test = ["androidx-compose-ui-test", "androidx-compose-ui-testManifest"] @@ -92,6 +93,7 @@ androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navig androidx-lifecycle-runtimeCompose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" } androidx-lifecycle-runtimeTesting = { group = "androidx.lifecycle", name = "lifecycle-runtime-testing", version.ref = "androidxLifecycle" } androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } +androidx-lint-gradle = { group = "androidx.lint", name = "lint-gradle", version.ref = "androidxLintGradle" } androidx-metrics = { group = "androidx.metrics", name = "metrics-performance", version.ref = "androidxMetrics" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" } androidx-navigation-testing = { group = "androidx.navigation", name = "navigation-testing", version.ref = "androidxNavigation" } @@ -141,6 +143,7 @@ retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.r retrofit-kotlin-serialization = { group = "com.squareup.retrofit2", name = "converter-kotlinx-serialization", version.ref = "retrofit" } robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } roborazzi = { group = "io.github.takahirom.roborazzi", name = "roborazzi", version.ref = "roborazzi" } +roborazzi-accessibility-check = { group = "io.github.takahirom.roborazzi", name = "roborazzi-accessibility-check", version.ref = "roborazzi" } room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } @@ -160,6 +163,7 @@ room-gradlePlugin = { group = "androidx.room", name = "room-gradle-plugin", vers [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } +android-lint = { id = "com.android.lint", version.ref = "androidGradlePlugin" } android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" } baselineprofile = { id = "androidx.baselineprofile", version.ref = "androidxMacroBenchmark"} compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9355b4155..e2847c820 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/sync/work/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/sync/workers/SyncWorkerTest.kt b/sync/work/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/sync/workers/SyncWorkerTest.kt index 9c9d13510..481875e69 100644 --- a/sync/work/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/sync/workers/SyncWorkerTest.kt +++ b/sync/work/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/sync/workers/SyncWorkerTest.kt @@ -64,12 +64,12 @@ class SyncWorkerTest { val preRunWorkInfo = workManager.getWorkInfoById(request.id).get() // Assert - assertEquals(WorkInfo.State.ENQUEUED, preRunWorkInfo.state) + assertEquals(WorkInfo.State.ENQUEUED, preRunWorkInfo?.state) // Tells the testing framework that the constraints have been met testDriver.setAllConstraintsMet(request.id) val postRequirementWorkInfo = workManager.getWorkInfoById(request.id).get() - assertEquals(WorkInfo.State.RUNNING, postRequirementWorkInfo.state) + assertEquals(WorkInfo.State.RUNNING, postRequirementWorkInfo?.state) } } diff --git a/ui-test-hilt-manifest/src/main/AndroidManifest.xml b/ui-test-hilt-manifest/src/main/AndroidManifest.xml index 8fbb1c299..d35bfe1e2 100644 --- a/ui-test-hilt-manifest/src/main/AndroidManifest.xml +++ b/ui-test-hilt-manifest/src/main/AndroidManifest.xml @@ -17,7 +17,14 @@ - + + \ No newline at end of file