diff --git a/.editorconfig b/.editorconfig index 7be3f8784..6c8c930bc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,3 +5,13 @@ ij_kotlin_allow_trailing_comma=true ij_kotlin_allow_trailing_comma_on_call_site=true ktlint_function_naming_ignore_when_annotated_with=Composable, Test +ktlint_standard_backing-property-naming = disabled +ktlint_standard_binary-expression-wrapping = disabled +ktlint_standard_chain-method-continuation = disabled +ktlint_standard_class-signature = disabled +ktlint_standard_condition-wrapping = disabled +ktlint_standard_function-expression-body = disabled +ktlint_standard_function-literal = disabled +ktlint_standard_function-type-modifier-spacing = disabled +ktlint_standard_multiline-loop = disabled +ktlint_standard_function-signature = disabled 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..053b7c7c6 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "local>android/.github:renovate-config" + ], + "baseBranches": [ + "main" + ] +} \ No newline at end of file diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index c67d3b3d8..e090e2155 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 @@ -93,7 +94,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 +117,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 +127,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 +144,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 +164,7 @@ jobs: timeout-minutes: 55 strategy: matrix: - api-level: [26, 30] + api-level: [26, 34] steps: - name: Delete unnecessary tools 🔧 @@ -201,9 +199,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 +221,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 }} @@ -235,7 +230,7 @@ jobs: - name: Display local test coverage (only API 30) if: matrix.api-level == 30 id: jacoco - uses: madrapps/jacoco-report@v1.7.0 + uses: madrapps/jacoco-report@v1.7.1 with: title: Combined test coverage report min-coverage-overall: 40 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 3102b4510..80d3c849e 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 49ab75cec..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.34.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.26.1 -com.google.protobuf:protobuf-kotlin-lite:4.26.1 +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/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt index dc2eef3f1..f26cc5a19 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 @@ -26,10 +26,7 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels import androidx.appcompat.app.AppCompatDelegate -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 @@ -39,21 +36,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 @@ -85,54 +83,66 @@ 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, + shouldFollowSystemTheme = Loading.shouldFollowSystemTheme, + ), + ) // 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, + shouldFollowSystemTheme = uiState.shouldFollowSystemTheme + ) + } + .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 }, + ) + setAppTheme( + uiModeManager = getSystemService(Context.UI_MODE_SERVICE) as UiModeManager, + themeSettings = themeSettings + ) + } + } } } // 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 themeInfo = themeInfo(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(themeInfo.isThemeDark, themeInfo.shouldFollowSystem) { - enableEdgeToEdge( - statusBarStyle = SystemBarStyle.auto( - android.graphics.Color.TRANSPARENT, - android.graphics.Color.TRANSPARENT, - ) { themeInfo.isThemeDark }, - navigationBarStyle = SystemBarStyle.auto( - lightScrim, - darkScrim, - ) { themeInfo.isThemeDark }, - ) - setAppTheme(getSystemService(Context.UI_MODE_SERVICE) as UiModeManager, themeInfo) - onDispose {} - } - val appState = rememberNiaAppState( networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, @@ -146,9 +156,9 @@ class MainActivity : ComponentActivity() { LocalTimeZone provides currentTimeZone, ) { NiaTheme( - darkTheme = themeInfo.isThemeDark, - androidTheme = themeInfo.shouldUseAndroidTheme, - disableDynamicTheming = themeInfo.shouldDisableDynamicTheming, + darkTheme = themeSettings.darkTheme, + androidTheme = themeSettings.androidTheme, + disableDynamicTheming = themeSettings.disableDynamicTheming, ) { NiaApp(appState) } @@ -167,62 +177,18 @@ class MainActivity : ComponentActivity() { } } -private data class ThemeInfo( - /** - * Returns `true` if dark theme should be used. - */ - val isThemeDark: Boolean, - - /** - * Returns `true` if theme should follow the system settings. - */ - val shouldFollowSystem: Boolean, - - /** - * Returns `true` if the dynamic color is disabled. - */ - val shouldDisableDynamicTheming: Boolean, - - /** - * Returns `true` if the Android theme should be used. - */ - val shouldUseAndroidTheme: Boolean, -) -@Composable -private fun themeInfo(uiState: MainActivityUiState): ThemeInfo { - return when (uiState) { - Loading -> ThemeInfo( - isThemeDark = isSystemInDarkTheme(), - shouldFollowSystem = false, - shouldDisableDynamicTheming = false, - shouldUseAndroidTheme = false - ) - - is Success -> { - val isThemeDark = uiState.userData.darkThemeConfig == DarkThemeConfig.DARK - val shouldFollowSystem = uiState.userData.darkThemeConfig == DarkThemeConfig.FOLLOW_SYSTEM - ThemeInfo( - isThemeDark = isThemeDark || (shouldFollowSystem && isSystemInDarkTheme()), - shouldFollowSystem = shouldFollowSystem, - shouldDisableDynamicTheming = !uiState.userData.useDynamicColor, - shouldUseAndroidTheme = uiState.userData.themeBrand == ThemeBrand.ANDROID - ) - } - } -} - /** * Sets app theme to reflect user choice. */ private fun setAppTheme( uiModeManager: UiModeManager, - themeInfo: ThemeInfo + themeSettings: ThemeSettings ) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { uiModeManager.setApplicationNightMode( - if (themeInfo.shouldFollowSystem) { + if (themeSettings.shouldFollowSystemTheme) { UiModeManager.MODE_NIGHT_AUTO - } else if (themeInfo.isThemeDark) { + } else if (themeSettings.darkTheme) { UiModeManager.MODE_NIGHT_YES } else { UiModeManager.MODE_NIGHT_NO @@ -230,9 +196,9 @@ private fun setAppTheme( ) } else { AppCompatDelegate.setDefaultNightMode( - if (themeInfo.shouldFollowSystem) { + if (themeSettings.shouldFollowSystemTheme) { AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM - } else if (themeInfo.isThemeDark) { + } else if (themeSettings.darkTheme) { AppCompatDelegate.MODE_NIGHT_YES } else { AppCompatDelegate.MODE_NIGHT_NO @@ -252,3 +218,14 @@ 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, + val shouldFollowSystemTheme: 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 2f2254310..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 36079dcf1..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 237d6d5b3..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 24a4fe157..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 7f983c424..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 1f1db205c..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 b7cee54bd..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 fa334578b..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 3f185dd62..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 514f5f000..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 624d588e8..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 7d7d697e7..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 887213791..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 f29835e64..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 9602e2a4d..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 1a26bc96d..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 f50dfd9f2..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..9a3a5aadc 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() @@ -66,6 +68,6 @@ internal fun Project.configureAndroidCompose( .let(reportsDestination::set) stabilityConfigurationFile = - rootProject.layout.projectDirectory.file("compose_compiler_config.conf") + isolated.rootProject.projectDirectory.file("compose_compiler_config.conf") } } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidInstrumentedTests.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidInstrumentedTests.kt index d0c26e4e6..c51dac5c9 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidInstrumentedTests.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/AndroidInstrumentedTests.kt @@ -30,6 +30,6 @@ import org.gradle.api.Project internal fun LibraryAndroidComponentsExtension.disableUnnecessaryAndroidTests( project: Project, ) = beforeVariants { - it.enableAndroidTest = it.enableAndroidTest + it.androidTest.enable = it.androidTest.enable && project.projectDir.resolve("src/androidTest").exists() } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt index 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 83c268aab..8c839fa8e 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -25,7 +25,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 1b8487b8b..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 768b278fe..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 ea2af8163..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 84dcfe3e6..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 55551f5cf..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 a346576ea..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 ff9584511..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 ea2af8163..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 65fab36f9..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 a162fe3d0..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 d73b2992e..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 08319f0c7..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 ac4c4c234..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 121a71a98..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 3c2da608e..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 cc0bd5fcf..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 84aa59b05..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 2e63f3058..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 aa7a82e62..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 3ae722ba8..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 aeb57a8b2..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 4d2bc5e50..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 2a455faea..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 fef62e4d7..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 d87c4c19b..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 13792665b..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 85a215412..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 9381f22ab..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 1873be71b..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 25f14ba48..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 2a60bba9b..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 e1ce90025..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 7016cf9e0..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 7d777e9c2..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 2bda37bd0..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 6a2286403..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 d65be724c..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 0fcf31aa8..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 0dc1958fc..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 7bfc9db14..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 8be9289be..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 cf236ccc6..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 c2e73ebcd..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 1806016d4..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 a04a52dd4..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 8d26654a3..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 8d26654a3..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 8d26654a3..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 8d26654a3..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 8d26654a3..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 8d26654a3..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 3388670d1..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 c1cd57e02..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 5ec625a8f..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 051715376..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 e6171872d..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 55dd863dd..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 3123ab438..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 f04e7cbba..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 0dcc25a80..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 79b982f4f..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 2b4050b99..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 f4f478ccc..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 a516a5589..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 cea88ddd4..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 2a5d017a7..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 5b0b9a1b7..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 70a9cdf06..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 ca35ee3df..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 13f887e37..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 469e070cb..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 3858cd12a..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 4049f8c90..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 a3be52a80..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_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png index ee19e0d0c..bdf5d18f3 100644 Binary files a/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_androidTheme_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 1bbfd6218..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_dark_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png index 8f56f13f0..074f3dc8c 100644 Binary files a/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.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 547788564..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_androidTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png index dde09305e..5c38870dc 100644 Binary files a/core/designsystem/src/test/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.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 9af3202ec..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/Tabs/Tabs_light_defaultTheme_notDynamic.png b/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png index 2df7822b9..759641c93 100644 Binary files a/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png and b/core/designsystem/src/test/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.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 a2c979061..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 210d6c96d..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 55fe716df..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 a7c5bb8d0..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 7d93422fd..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 b085ad53e..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 63347e1eb..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 b4157fc79..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 24b53763b..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 cea432900..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 d497ed77d..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 c998e25de..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 f063b0cd6..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 ca5c4e912..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 56b0f5a2e..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 5a7abf9da..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 005957228..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 fd9fd6875..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 96cc8ca9a..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 13233bc93..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 bf0c2b4b6..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 67112f6d1..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 ffb09165c..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 37dbe6e92..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 9c8263d08..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 0f01ae89a..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 1fe072ccf..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 a3d03661b..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/SearchScreen.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt index ff91941a8..b617f98a9 100644 --- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt +++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreen.kt @@ -522,6 +522,7 @@ private fun SearchTextField( .focusRequester(focusRequester) .onKeyEvent { if (it.key == Key.Enter) { + if (searchQuery.isBlank()) return@onKeyEvent false onSearchExplicitlyTriggered() true } else { @@ -536,6 +537,7 @@ private fun SearchTextField( ), keyboardActions = KeyboardActions( onSearch = { + if (searchQuery.isBlank()) return@KeyboardActions onSearchExplicitlyTriggered() }, ), diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt index 6c2af240c..36947880e 100644 --- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt +++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt @@ -59,7 +59,7 @@ class SearchViewModel @Inject constructor( flowOf(SearchResultUiState.SearchNotReady) } else { searchQuery.flatMapLatest { query -> - if (query.length < SEARCH_QUERY_MIN_LENGTH) { + if (query.trim().length < SEARCH_QUERY_MIN_LENGTH) { flowOf(SearchResultUiState.EmptyQuery) } else { getSearchContentsUseCase(query) @@ -102,6 +102,7 @@ class SearchViewModel @Inject constructor( * search query in the search text field, defining this method. */ fun onSearchTriggered(query: String) { + if (query.isBlank()) return viewModelScope.launch { recentSearchRepository.insertOrReplaceRecentSearch(searchQuery = query) } 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/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt b/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt index a62965b52..1b866cec2 100644 --- a/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt +++ b/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt @@ -41,6 +41,7 @@ import org.junit.Rule import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertIs +import kotlin.test.assertNull /** * To learn more about how this test handles Flows created with stateIn, see @@ -122,6 +123,43 @@ class SearchViewModelTest { assertEquals(SearchNotReady, viewModel.searchResultUiState.value) } + @Test + fun emptySearchText_isNotAddedToRecentSearches() = runTest { + viewModel.onSearchTriggered("") + + val recentSearchQueriesStream = getRecentQueryUseCase() + val recentSearchQueries = recentSearchQueriesStream.first() + val recentSearchQuery = recentSearchQueries.firstOrNull() + + assertNull(recentSearchQuery) + } + + @Test + fun searchTextWithThreeSpaces_isEmptyQuery() = runTest { + searchContentsRepository.addNewsResources(newsResourcesTestData) + searchContentsRepository.addTopics(topicsTestData) + val collectJob = launch(UnconfinedTestDispatcher()) { viewModel.searchResultUiState.collect() } + + viewModel.onSearchQueryChanged(" ") + + assertIs(viewModel.searchResultUiState.value) + + collectJob.cancel() + } + + @Test + fun searchTextWithThreeSpacesAndOneLetter_isEmptyQuery() = runTest { + searchContentsRepository.addNewsResources(newsResourcesTestData) + searchContentsRepository.addTopics(topicsTestData) + val collectJob = launch(UnconfinedTestDispatcher()) { viewModel.searchResultUiState.collect() } + + viewModel.onSearchQueryChanged(" a") + + assertIs(viewModel.searchResultUiState.value) + + collectJob.cancel() + } + @Test fun whenToggleNewsResourceSavedIsCalled_bookmarkStateIsUpdated() = runTest { val newsResourceId = "123" 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/init.gradle.kts b/gradle/init.gradle.kts index fe79fa01e..44dc41200 100644 --- a/gradle/init.gradle.kts +++ b/gradle/init.gradle.kts @@ -14,10 +14,10 @@ * limitations under the License. */ -val ktlintVersion = "1.0.1" +val ktlintVersion = "1.4.0" initscript { - val spotlessVersion = "6.23.3" + val spotlessVersion = "6.25.0" repositories { mavenCentral() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 07076ca7d..5ac8236e9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,24 +1,25 @@ [versions] -accompanist = "0.34.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.0" +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" -firebaseCrashlyticsPlugin = "2.9.9" +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.26.1" +protobuf = "4.29.2" protobufPlugin = "0.9.4" retrofit = "2.11.0" retrofitKotlinxSerializationJson = "1.0.0" -robolectric = "4.14" -roborazzi = "1.32.2" +robolectric = "4.14.1" +roborazzi = "1.37.0" room = "2.6.1" secrets = "2.0.1" -truth = "1.4.2" -turbine = "1.1.0" +truth = "1.4.4" +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