diff --git a/.deepsec/matchers/security-matchers.test.ts b/.deepsec/matchers/security-matchers.test.ts index b88929c..66a304b 100644 --- a/.deepsec/matchers/security-matchers.test.ts +++ b/.deepsec/matchers/security-matchers.test.ts @@ -33,6 +33,15 @@ test("sensitive log matcher covers multiline calls", () => { assert.equal(matches.length, 1); }); +test("sensitive log matcher covers Log.wtf calls", () => { + const matches = sensitiveAndroidLog.match( + 'Log.wtf(TAG, "Health export URI: $uri")', + "app/src/main/java/com/dbcheck/app/ExportLogger.kt", + ); + + assert.equal(matches.length, 1); +}); + test("sensitive log matcher does not consume a later Kotlin statement", () => { const matches = sensitiveAndroidLog.match( `Log.i(TAG, "sync complete") diff --git a/.deepsec/matchers/sensitive-android-log.ts b/.deepsec/matchers/sensitive-android-log.ts index cd48368..757fa72 100644 --- a/.deepsec/matchers/sensitive-android-log.ts +++ b/.deepsec/matchers/sensitive-android-log.ts @@ -4,7 +4,7 @@ import { candidate, findBalancedDelimiterEnd, isTestFile } from "./utils.js"; const sensitiveWords = "(?:session|sessionId|measurement|decibel|dbWeighted|dbcheck\\.db|audio|microphone|recording|sample|billing|purchase|token|backup|restore|health|heart|threshold|hearing|csv|pdf|export|uri|fileprovider)"; const sensitiveWordPattern = new RegExp(sensitiveWords, "i"); -const logCallStart = /\b(?:Log|android\.util\.Log)\.(?:v|d|i|w|e)\s*\(/g; +const logCallStart = /\b(?:Log|android\.util\.Log)\.(?:v|d|i|w|e|wtf)\s*\(/g; export const sensitiveAndroidLog: MatcherPlugin = { slug: "sensitive-android-log", diff --git a/.deepsec/pnpm-lock.yaml b/.deepsec/pnpm-lock.yaml index e4cd8ed..1ea7f26 100644 --- a/.deepsec/pnpm-lock.yaml +++ b/.deepsec/pnpm-lock.yaml @@ -648,9 +648,9 @@ packages: bowser@2.14.1: resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} - brace-expansion@5.0.7: - resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} - engines: {node: 18 || 20 || >=22} + brace-expansion@5.0.8: + resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==} + engines: {node: 20 || >=22} buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -2001,7 +2001,7 @@ snapshots: bowser@2.14.1: {} - brace-expansion@5.0.7: + brace-expansion@5.0.8: dependencies: balanced-match: 4.0.4 @@ -2374,7 +2374,7 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.7 + brace-expansion: 5.0.8 minipass@7.1.3: {} diff --git a/PROJECT.md b/PROJECT.md index cb09f25..0410ffc 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -1363,10 +1363,10 @@ Unit-testit: Screenshot-testit: -- `ComponentScreenshotTests.kt` sisaltaa 56 komponenttipreviewta. +- `ComponentScreenshotTests.kt` sisaltaa 59 komponenttipreviewta. - `FullScreenScreenshotTests.kt` sisaltaa 34 light/dark full-screen -tilaa ja 5 fontScale = 1.5f -previewta. -- Rekursiivisesti tiedostojarjestelmasta laskettuna source setissa on yhteensa 95 `@PreviewTest`-funktiota ja - `app/src/screenshotTestDebug/reference/...`-puussa 95 baseline-PNG:ta (56 komponenttia + 39 full-screen-referencea). +- Rekursiivisesti tiedostojarjestelmasta laskettuna source setissa on yhteensa 98 `@PreviewTest`-funktiota ja + `app/src/screenshotTestDebug/reference/...`-puussa 98 baseline-PNG:ta (59 komponenttia + 39 full-screen-referencea). - Screenshot-source set on kytketty AGP:n kokeellisella `android.experimental.enableScreenshotTest = true` -asetuksella. - UI-komponenttien animaatioita voi poistaa screenshot-determinismia varten diff --git a/UI-SPEC.md b/UI-SPEC.md index c29ed16..73d7187 100644 --- a/UI-SPEC.md +++ b/UI-SPEC.md @@ -1797,10 +1797,10 @@ Tama dokumentti kuvaa nykyisen kayttoliittyman koodista johdetun visuaalisen sop ## 22. Screenshot-testit -- `ComponentScreenshotTests.kt` sisaltaa 56 komponenttipreviewta. +- `ComponentScreenshotTests.kt` sisaltaa 59 komponenttipreviewta. - `FullScreenScreenshotTests.kt` lisaa 34 light/dark full-screen -tilaa: Meter 6, Trends 6, Hearing 4, History 4 ja Settings 14. - Lisäksi matriisissa on 5 fontScale = 1.5f -previewta: Meter idle, Hearing Pro, History sessions, Settings Notifications ja Settings Data & privacy. -- Rekursiivisesti tiedostojarjestelmasta laskettu kokonaisuus on 95 `@PreviewTest`-funktiota ja 95 baseline-PNG:ta. Jokaisella previewlla on yksi reference-kuva. +- Rekursiivisesti tiedostojarjestelmasta laskettu kokonaisuus on 98 `@PreviewTest`-funktiota ja 98 baseline-PNG:ta. Jokaisella previewlla on yksi reference-kuva. - Kaikki full-screen-previewt kayttavat `360 x 800dp` -viewportia, oikeaa `DbCheckTheme`-teemaa, tuotannon app shellia ja puhtaita presentation-entrypointteja ilman Hilt/ViewModel/NavController-instansseja. ## 23. Koodilahteet diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 10af4ea..4a320ad 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -396,6 +396,7 @@ dependencies { implementation(libs.androidx.compose.animation) implementation(libs.androidx.compose.foundation) debugImplementation(libs.androidx.compose.ui.tooling) + debugImplementation(libs.androidx.compose.ui.test.manifest) // Sentry on vain debug-diagnostiikkaa. Release-luokkapolku tarkistetaan tools\sentry.ps1-komennolla. debugImplementation(libs.sentry.android.core) @@ -469,4 +470,6 @@ dependencies { testImplementation(libs.androidx.test.core) testImplementation(libs.robolectric) testImplementation(libs.kotlinx.coroutines.test) + testImplementation(platform(libs.androidx.compose.bom)) + testImplementation(libs.androidx.compose.ui.test.junit4) } diff --git a/app/gradle.lockfile b/app/gradle.lockfile index d79c0c2..f52e049 100644 --- a/app/gradle.lockfile +++ b/app/gradle.lockfile @@ -58,6 +58,11 @@ androidx.compose.ui:ui-geometry-android:1.11.4=debugAndroidTestCompileClasspath, androidx.compose.ui:ui-geometry:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath androidx.compose.ui:ui-graphics-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath androidx.compose.ui:ui-graphics:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath +androidx.compose.ui:ui-test-android:1.11.4=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath +androidx.compose.ui:ui-test-junit4-android:1.11.4=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath +androidx.compose.ui:ui-test-junit4:1.11.4=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath +androidx.compose.ui:ui-test-manifest:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath +androidx.compose.ui:ui-test:1.11.4=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath androidx.compose.ui:ui-text-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath androidx.compose.ui:ui-text:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath androidx.compose.ui:ui-tooling-android:1.11.4=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath @@ -174,9 +179,14 @@ androidx.sqlite:sqlite-framework-android:2.6.2=debugAndroidTestCompileClasspath, androidx.sqlite:sqlite-framework:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath androidx.sqlite:sqlite:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath androidx.startup:startup-runtime:1.1.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath +androidx.test.espresso:espresso-core:3.5.0=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath androidx.test.espresso:espresso-idling-resource:3.7.0=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath +androidx.test.ext:junit:1.1.5=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath +androidx.test.services:storage:1.4.2=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath +androidx.test:annotation:1.0.1=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath androidx.test:core:1.7.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath androidx.test:monitor:1.8.0=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath +androidx.test:runner:1.5.0=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath androidx.tracing:tracing-android:1.3.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath androidx.tracing:tracing-ktx:1.3.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath androidx.tracing:tracing:1.1.0=debugUnitTestCompileClasspath @@ -278,10 +288,10 @@ com.android.tools:sdk-common:32.3.1=androidLintTool,unified-test-platform-androi com.android.tools:sdklib:32.1.0=_internal-unified-test-platform-android-test-plugin-result-listener-gradle com.android.tools:sdklib:32.3.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle com.github.ben-manes.caffeine:caffeine:2.9.3=detekt,detektPlugins -com.github.skydoves:compose-stability-compiler:0.11.0=kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathRelease -com.github.skydoves:compose-stability-runtime-android:0.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath -com.github.skydoves:compose-stability-runtime-jvm:0.11.0=kotlinCompilerPluginClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugScreenshotTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseScreenshotTest -com.github.skydoves:compose-stability-runtime:0.11.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,kotlinCompilerPluginClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugScreenshotTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseScreenshotTest,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath +com.github.skydoves:compose-stability-compiler:0.11.1=kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathRelease +com.github.skydoves:compose-stability-runtime-android:0.11.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath +com.github.skydoves:compose-stability-runtime-jvm:0.11.1=kotlinCompilerPluginClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugScreenshotTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseScreenshotTest +com.github.skydoves:compose-stability-runtime:0.11.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,kotlinCompilerPluginClasspath,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugScreenshotTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathRelease,kotlinCompilerPluginClasspathReleaseScreenshotTest,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath com.google.android.datatransport:transport-api:3.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath com.google.android.datatransport:transport-backend-cct:3.1.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath com.google.android.datatransport:transport-runtime:3.1.8=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath @@ -359,6 +369,7 @@ com.intellij:annotations:12.0=_agp_internal_debug_kspClasspath,_agp_internal_rel com.squareup.okio:okio-jvm:3.9.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath com.squareup.okio:okio:3.9.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugScreenshotTestCompileClasspath,debugScreenshotTestLintChecksClasspath,debugScreenshotTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,releaseScreenshotTestCompileClasspath,releaseScreenshotTestLintChecksClasspath,releaseScreenshotTestRuntimeClasspath com.squareup:javapoet:1.13.0=_agp_internal_debug_kspClasspath,_agp_internal_release_kspClasspath,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorRelease,kspDebugKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath +com.squareup:javawriter:2.1.1=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath com.squareup:kotlinpoet-javapoet:2.0.0=_agp_internal_debug_kspClasspath,_agp_internal_release_kspClasspath,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorRelease,kspDebugKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath com.squareup:kotlinpoet-jvm:2.0.0=_agp_internal_debug_kspClasspath,_agp_internal_release_kspClasspath,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorRelease,kspDebugKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath com.squareup:kotlinpoet:2.0.0=_agp_internal_debug_kspClasspath,_agp_internal_release_kspClasspath,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorRelease,kspDebugKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath @@ -473,6 +484,8 @@ org.conscrypt:conscrypt-openjdk-uber:2.6.0=debugUnitTestLintChecksClasspath,debu org.glassfish.jaxb:jaxb-runtime:2.3.2=_internal-unified-test-platform-android-test-plugin-result-listener-gradle,androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle org.glassfish.jaxb:txw2:2.3.2=_internal-unified-test-platform-android-test-plugin-result-listener-gradle,androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle org.hamcrest:hamcrest-core:1.3=debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath +org.hamcrest:hamcrest-integration:1.3=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath +org.hamcrest:hamcrest-library:1.3=debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath org.jacoco:org.jacoco.agent:0.8.14=androidJacocoAnt,jacocoAgent,jacocoAnt org.jacoco:org.jacoco.ant:0.8.14=androidJacocoAnt,jacocoAnt org.jacoco:org.jacoco.core:0.8.14=androidJacocoAnt,jacocoAnt diff --git a/app/src/main/java/com/dbcheck/app/ui/analytics/components/SpectralStatPill.kt b/app/src/main/java/com/dbcheck/app/ui/analytics/components/SpectralStatPill.kt index 799366f..25a2089 100644 --- a/app/src/main/java/com/dbcheck/app/ui/analytics/components/SpectralStatPill.kt +++ b/app/src/main/java/com/dbcheck/app/ui/analytics/components/SpectralStatPill.kt @@ -38,7 +38,7 @@ internal fun spectralBandAmplitudesFor(state: SpectralAnalysisUiState): List "--" - SpectralAnalysisUiState.LockedPreview -> peakBandValue(PREVIEW_SPECTRAL_BANDS) + SpectralAnalysisUiState.LockedPreview -> peakBandValue(spectralPreviewBands) is SpectralAnalysisUiState.Live -> formatSpectralFrequency(state.dominantFrequencyHz) } @@ -79,11 +79,11 @@ internal fun spectralBandwidthResId(bandwidth: SpectralBandwidth): Int? = when ( } private fun spectralBandsFor(state: SpectralAnalysisUiState): List = when (state) { - SpectralAnalysisUiState.Idle -> List(PREVIEW_SPECTRAL_BANDS.size) { + SpectralAnalysisUiState.Idle -> List(spectralPreviewBands.size) { SpectralBandUiState(normalizedAmplitude = 0f) } - SpectralAnalysisUiState.LockedPreview -> PREVIEW_SPECTRAL_BANDS + SpectralAnalysisUiState.LockedPreview -> spectralPreviewBands is SpectralAnalysisUiState.Live -> state.bands } @@ -144,7 +144,7 @@ private val PREVIEW_SPECTRAL_AMPLITUDES = 0.08f, ) -private val PREVIEW_SPECTRAL_BANDS = +internal val spectralPreviewBands = PREVIEW_SPECTRAL_AMPLITUDES.mapIndexed { index, amplitude -> SpectralBandUiState( normalizedAmplitude = amplitude, diff --git a/app/src/main/java/com/dbcheck/app/ui/history/detail/SessionDetailScreen.kt b/app/src/main/java/com/dbcheck/app/ui/history/detail/SessionDetailScreen.kt index 0c383a4..bf19d1a 100644 --- a/app/src/main/java/com/dbcheck/app/ui/history/detail/SessionDetailScreen.kt +++ b/app/src/main/java/com/dbcheck/app/ui/history/detail/SessionDetailScreen.kt @@ -762,7 +762,7 @@ internal fun DbHistogramCard( DbHistogramCardContent( buckets = if (isLocked) { - LOCKED_PREVIEW_HISTOGRAM_BUCKETS + lockedPreviewHistogramBuckets } else { buckets }, @@ -908,7 +908,7 @@ internal fun dbHistogramAccessibilitySummary(buckets: List): private fun List.visibleHistogramBuckets(): List = filter { bucket -> bucket.sampleCount > 0 || bucket.percent > 0 } -private val LOCKED_PREVIEW_HISTOGRAM_BUCKETS = +internal val lockedPreviewHistogramBuckets = listOf( DbHistogramBucket(minDb = 0, maxDb = 10, sampleCount = 0, percent = 0), DbHistogramBucket(minDb = 10, maxDb = 20, sampleCount = 0, percent = 0), diff --git a/app/src/main/java/com/dbcheck/app/ui/meter/components/ExpandableCardHeader.kt b/app/src/main/java/com/dbcheck/app/ui/meter/components/ExpandableCardHeader.kt new file mode 100644 index 0000000..71950af --- /dev/null +++ b/app/src/main/java/com/dbcheck/app/ui/meter/components/ExpandableCardHeader.kt @@ -0,0 +1,28 @@ +package com.dbcheck.app.ui.meter.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.dbcheck.app.ui.theme.DbCheckSpacing + +internal fun Modifier.expandableCardHeader( + spacing: DbCheckSpacing, + stateLabel: String, + expanded: Boolean, + onExpandedChange: (Boolean) -> Unit, + verticalPadding: Dp = 0.dp, +): Modifier = fillMaxWidth() + .heightIn(min = spacing.space12) + .semantics { + stateDescription = stateLabel + }.clickable( + role = Role.Button, + onClick = { onExpandedChange(!expanded) }, + ).padding(horizontal = spacing.cardPadding, vertical = verticalPadding) diff --git a/app/src/main/java/com/dbcheck/app/ui/meter/components/LiveActivityCard.kt b/app/src/main/java/com/dbcheck/app/ui/meter/components/LiveActivityCard.kt index 63f66ba..49c4ec4 100644 --- a/app/src/main/java/com/dbcheck/app/ui/meter/components/LiveActivityCard.kt +++ b/app/src/main/java/com/dbcheck/app/ui/meter/components/LiveActivityCard.kt @@ -1,13 +1,11 @@ package com.dbcheck.app.ui.meter.components -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ExpandLess @@ -18,9 +16,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.semantics.stateDescription import com.dbcheck.app.R import com.dbcheck.app.data.local.preferences.model.WaveformStyle import com.dbcheck.app.ui.components.DbCheckCard @@ -86,14 +81,12 @@ private fun LiveDetailsHeader(isRecording: Boolean, expanded: Boolean, onExpande Row( modifier = Modifier - .fillMaxWidth() - .heightIn(min = spacing.space12) - .semantics { - this.stateDescription = stateLabel - }.clickable( - role = Role.Button, - onClick = { onExpandedChange(!expanded) }, - ).padding(horizontal = spacing.cardPadding), + .expandableCardHeader( + spacing = spacing, + stateLabel = stateLabel, + expanded = expanded, + onExpandedChange = onExpandedChange, + ), verticalAlignment = Alignment.CenterVertically, ) { Text( diff --git a/app/src/main/java/com/dbcheck/app/ui/meter/components/SoundReferenceCard.kt b/app/src/main/java/com/dbcheck/app/ui/meter/components/SoundReferenceCard.kt index fb0e4d1..87b715a 100644 --- a/app/src/main/java/com/dbcheck/app/ui/meter/components/SoundReferenceCard.kt +++ b/app/src/main/java/com/dbcheck/app/ui/meter/components/SoundReferenceCard.kt @@ -3,7 +3,6 @@ package com.dbcheck.app.ui.meter.components import androidx.compose.animation.animateContentSize import androidx.compose.foundation.Canvas import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -30,10 +29,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -120,14 +117,13 @@ private fun SoundReferenceCollapsedRow( Row( modifier = Modifier - .fillMaxWidth() - .heightIn(min = spacing.space12) - .semantics { - this.stateDescription = stateLabel - }.clickable( - role = Role.Button, - onClick = { onExpandedChange(!expanded) }, - ).padding(horizontal = spacing.cardPadding, vertical = spacing.space2), + .expandableCardHeader( + spacing = spacing, + stateLabel = stateLabel, + expanded = expanded, + onExpandedChange = onExpandedChange, + verticalPadding = spacing.space2, + ), verticalAlignment = Alignment.CenterVertically, ) { Column(modifier = Modifier.weight(1f)) { diff --git a/app/src/main/java/com/dbcheck/app/ui/settings/components/AudioCalibrationSection.kt b/app/src/main/java/com/dbcheck/app/ui/settings/components/AudioCalibrationSection.kt index 54890f9..d54c36a 100644 --- a/app/src/main/java/com/dbcheck/app/ui/settings/components/AudioCalibrationSection.kt +++ b/app/src/main/java/com/dbcheck/app/ui/settings/components/AudioCalibrationSection.kt @@ -4,7 +4,6 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer @@ -267,52 +266,35 @@ private fun MicSensitivityControls(sensitivityOffset: Float, onSensitivityChange @Composable private fun FrequencyWeightingControls(frequencyWeighting: String, onWeightingChange: (String) -> Unit) { - Column(modifier = Modifier.fillMaxWidth()) { - Text( - stringResource(R.string.settings_audio_frequency_weighting), - style = DbCheckTheme.typography.bodyLg, - color = DbCheckTheme.colorScheme.material.onSurface, - ) - Spacer(Modifier.height(DbCheckTheme.spacing.space2)) - FlowRow( - horizontalArrangement = Arrangement.spacedBy(DbCheckTheme.spacing.space2), - verticalArrangement = Arrangement.spacedBy(DbCheckTheme.spacing.space2), - ) { - WeightingType.entries.forEach { weight -> - DbCheckChip( - text = stringResource(weight.displayNameStringRes()), - selected = frequencyWeighting == weight.name, - onClick = { onWeightingChange(weight.name) }, - ) - } + CalibrationChipControls(title = stringResource(R.string.settings_audio_frequency_weighting)) { + WeightingType.entries.forEach { weight -> + DbCheckChip( + text = stringResource(weight.displayNameStringRes()), + selected = frequencyWeighting == weight.name, + onClick = { onWeightingChange(weight.name) }, + ) } } } @Composable private fun ResponseTimeControls(responseTime: ResponseTime, onResponseTimeChange: (ResponseTime) -> Unit) { - Column(modifier = Modifier.fillMaxWidth()) { - Text( - stringResource(R.string.settings_audio_response_time), - style = DbCheckTheme.typography.bodyLg, - color = DbCheckTheme.colorScheme.material.onSurface, - ) - Spacer(Modifier.height(DbCheckTheme.spacing.space2)) - FlowRow( - horizontalArrangement = Arrangement.spacedBy(DbCheckTheme.spacing.space2), - verticalArrangement = Arrangement.spacedBy(DbCheckTheme.spacing.space2), - ) { - ResponseTime.entries.forEach { response -> - DbCheckChip( - text = stringResource(response.displayNameStringRes()), - selected = responseTime == response, - onClick = { onResponseTimeChange(response) }, - ) - } + CalibrationChipControls(title = stringResource(R.string.settings_audio_response_time)) { + ResponseTime.entries.forEach { response -> + DbCheckChip( + text = stringResource(response.displayNameStringRes()), + selected = responseTime == response, + onClick = { onResponseTimeChange(response) }, + ) } } } +@Composable +private fun CalibrationChipControls(title: String, content: @Composable () -> Unit) { + SettingsChipGroup(label = title, chips = content) +} + @Composable private fun AudioInputDeviceControls( devices: List, diff --git a/app/src/main/java/com/dbcheck/app/ui/settings/components/DisplayAndFeaturesSection.kt b/app/src/main/java/com/dbcheck/app/ui/settings/components/DisplayAndFeaturesSection.kt index c674ce6..6e61b9e 100644 --- a/app/src/main/java/com/dbcheck/app/ui/settings/components/DisplayAndFeaturesSection.kt +++ b/app/src/main/java/com/dbcheck/app/ui/settings/components/DisplayAndFeaturesSection.kt @@ -1,8 +1,6 @@ package com.dbcheck.app.ui.settings.components -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -159,25 +157,3 @@ private fun DisplayAppearanceCard(state: DisplayAndFeaturesSectionState, actions } } } - -@Composable -private fun SettingsChipGroup(label: String, helperText: String? = null, chips: @Composable () -> Unit) { - val typography = DbCheckTheme.typography - val colors = DbCheckTheme.colorScheme - val spacing = DbCheckTheme.spacing - - Column(modifier = Modifier.fillMaxWidth()) { - Text(label, style = typography.bodyLg, color = colors.material.onSurface) - helperText?.let { - Spacer(Modifier.height(spacing.space1)) - Text(it, style = typography.bodyMd, color = colors.material.onSurfaceVariant) - } - Spacer(Modifier.height(spacing.space2)) - FlowRow( - horizontalArrangement = Arrangement.spacedBy(spacing.space2), - verticalArrangement = Arrangement.spacedBy(spacing.space2), - ) { - chips() - } - } -} diff --git a/app/src/main/java/com/dbcheck/app/ui/settings/components/SettingsChipGroup.kt b/app/src/main/java/com/dbcheck/app/ui/settings/components/SettingsChipGroup.kt new file mode 100644 index 0000000..594226a --- /dev/null +++ b/app/src/main/java/com/dbcheck/app/ui/settings/components/SettingsChipGroup.kt @@ -0,0 +1,34 @@ +package com.dbcheck.app.ui.settings.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.dbcheck.app.ui.theme.DbCheckTheme + +@Composable +internal fun SettingsChipGroup(label: String, helperText: String? = null, chips: @Composable () -> Unit) { + val typography = DbCheckTheme.typography + val colors = DbCheckTheme.colorScheme + val spacing = DbCheckTheme.spacing + + Column(modifier = Modifier.fillMaxWidth()) { + Text(label, style = typography.bodyLg, color = colors.material.onSurface) + helperText?.let { + Spacer(Modifier.height(spacing.space1)) + Text(it, style = typography.bodyMd, color = colors.material.onSurfaceVariant) + } + Spacer(Modifier.height(spacing.space2)) + FlowRow( + horizontalArrangement = Arrangement.spacedBy(spacing.space2), + verticalArrangement = Arrangement.spacedBy(spacing.space2), + ) { + chips() + } + } +} diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 1e69e35..c4bbd17 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -69,7 +69,6 @@ TYÖKALUT PUHEÄÄNEN BASELINE Kuulon tila ei ole saatavilla, koska altistumistietoja ei vielä ole. - Avaa Kuulo Käytä kuulokkeita ja aloita hiljaisessa paikassa. Käytä kuulokkeita diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b7962bd..0eb1ab7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -98,7 +98,6 @@ Alert schedule start time %1$s Not selected Open camera overlay - Open settings Reset Selected Session emoji %1$s @@ -225,7 +224,6 @@ Exposure today matches your weekly average. Noise levels are elevated. Hearing status is unavailable because there is no exposure data yet. - Open Hearing CHECK YOUR HEARING No hearing test result is available to display. %1$s · %2$d/100 · avg. relative level %3$.1f dB @@ -508,7 +506,6 @@ %1$d-%2$d dB %1$d dB reference %1$d dB - Nearest reference SOUND REFERENCES Dangerous Busy / Elevated @@ -835,7 +832,6 @@ Stop recording before clearing history Permanently delete saved sessions, readings, sound detections, passive summaries, and WAV recordings Clear history - SYSTEM PREFERENCES Settings Calibration Octave Calibration diff --git a/app/src/screenshotTest/kotlin/com/dbcheck/app/ComponentScreenshotTests.kt b/app/src/screenshotTest/kotlin/com/dbcheck/app/ComponentScreenshotTests.kt index de706a5..3f2a3bc 100644 --- a/app/src/screenshotTest/kotlin/com/dbcheck/app/ComponentScreenshotTests.kt +++ b/app/src/screenshotTest/kotlin/com/dbcheck/app/ComponentScreenshotTests.kt @@ -9,10 +9,12 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.android.tools.screenshot.PreviewTest @@ -26,7 +28,6 @@ import com.dbcheck.app.domain.noise.DosimeterStandard import com.dbcheck.app.domain.noise.NoiseLevel import com.dbcheck.app.domain.noise.NoiseNotificationSchedule import com.dbcheck.app.domain.noise.SoundReferenceCatalog -import com.dbcheck.app.domain.report.DbHistogramBucket import com.dbcheck.app.ui.ambient.AmbientSoundPlaybackCallbacks import com.dbcheck.app.ui.ambient.AmbientSoundPlaybackScreen import com.dbcheck.app.ui.ambient.AmbientSoundPlaybackUiState @@ -37,6 +38,7 @@ import com.dbcheck.app.ui.analytics.components.SpectralAnalysisCard import com.dbcheck.app.ui.analytics.components.SpectralAnalysisCardActions import com.dbcheck.app.ui.analytics.components.SpectralAnalysisCardState import com.dbcheck.app.ui.analytics.components.YearlyReportCard +import com.dbcheck.app.ui.analytics.components.spectralPreviewBands import com.dbcheck.app.ui.analytics.state.AnalyticsSection import com.dbcheck.app.ui.analytics.state.EnvironmentMixCategory import com.dbcheck.app.ui.analytics.state.EnvironmentMixRowUiState @@ -67,6 +69,7 @@ import com.dbcheck.app.ui.history.detail.SleepInsightsCard import com.dbcheck.app.ui.history.detail.SleepInsightsUiState import com.dbcheck.app.ui.history.detail.SleepResultsCard import com.dbcheck.app.ui.history.detail.SleepResultsUiState +import com.dbcheck.app.ui.history.detail.lockedPreviewHistogramBuckets import com.dbcheck.app.ui.history.components.HistorySearchControls import com.dbcheck.app.ui.history.components.HistorySearchControlsActions import com.dbcheck.app.ui.history.components.HistorySearchControlsState @@ -101,6 +104,7 @@ import com.dbcheck.app.ui.sleep.SleepSetupActions import com.dbcheck.app.ui.sleep.SleepSetupScreen import com.dbcheck.app.ui.sleep.SleepSetupUiState import com.dbcheck.app.ui.theme.DbCheckTheme +import com.dbcheck.app.ui.theme.DbCheckRadii import java.time.DayOfWeek @PreviewTest @@ -108,13 +112,7 @@ import java.time.DayOfWeek @Composable fun ButtonStylesPreview() { DbCheckTheme { - Column(modifier = Modifier.padding(16.dp)) { - DbCheckButton(text = "Primary", onClick = {}, style = DbCheckButtonStyle.Primary) - Spacer(modifier = Modifier.height(8.dp)) - DbCheckButton(text = "Secondary", onClick = {}, style = DbCheckButtonStyle.Secondary) - Spacer(modifier = Modifier.height(8.dp)) - DbCheckButton(text = "Tertiary", onClick = {}, style = DbCheckButtonStyle.Tertiary) - } + ButtonStylesPreviewContent(modifier = Modifier.padding(16.dp)) } } @@ -123,19 +121,24 @@ fun ButtonStylesPreview() { @Composable fun ButtonStylesDarkPreview() { DbCheckTheme { - Column( + ButtonStylesPreviewContent( modifier = Modifier .fillMaxWidth() .background(DbCheckTheme.colorScheme.material.background) .padding(16.dp), - ) { - DbCheckButton(text = "Primary", onClick = {}, style = DbCheckButtonStyle.Primary) - Spacer(modifier = Modifier.height(8.dp)) - DbCheckButton(text = "Secondary", onClick = {}, style = DbCheckButtonStyle.Secondary) - Spacer(modifier = Modifier.height(8.dp)) - DbCheckButton(text = "Tertiary", onClick = {}, style = DbCheckButtonStyle.Tertiary) - } + ) + } +} + +@Composable +private fun ButtonStylesPreviewContent(modifier: Modifier) { + Column(modifier = modifier) { + DbCheckButton(text = "Primary", onClick = {}, style = DbCheckButtonStyle.Primary) + Spacer(modifier = Modifier.height(8.dp)) + DbCheckButton(text = "Secondary", onClick = {}, style = DbCheckButtonStyle.Secondary) + Spacer(modifier = Modifier.height(8.dp)) + DbCheckButton(text = "Tertiary", onClick = {}, style = DbCheckButtonStyle.Tertiary) } } @@ -144,13 +147,7 @@ fun ButtonStylesDarkPreview() { @Composable fun CardPreview() { DbCheckTheme { - DbCheckCard(modifier = Modifier.width(280.dp)) { - Text( - text = "42.5 dB", - style = DbCheckTheme.typography.displayLg, - color = DbCheckTheme.colorScheme.material.onSurface, - ) - } + CardPreviewContent() } } @@ -159,13 +156,18 @@ fun CardPreview() { @Composable fun CardDarkPreview() { DbCheckTheme { - DbCheckCard(modifier = Modifier.width(280.dp)) { - Text( - text = "42.5 dB", - style = DbCheckTheme.typography.displayLg, - color = DbCheckTheme.colorScheme.material.onSurface, - ) - } + CardPreviewContent() + } +} + +@Composable +private fun CardPreviewContent() { + DbCheckCard(modifier = Modifier.width(280.dp)) { + Text( + text = "42.5 dB", + style = DbCheckTheme.typography.displayLg, + color = DbCheckTheme.colorScheme.material.onSurface, + ) } } @@ -560,7 +562,7 @@ fun MeterSessionInfoBarProDarkPreview() { @Composable fun LiveSoundLevelChartEmptyPreview() { DbCheckTheme { - LiveSoundLevelChartPreviewContainer { + ComponentPreviewContainer { LiveSoundLevelChart( points = emptyList(), isRecording = false, @@ -574,7 +576,7 @@ fun LiveSoundLevelChartEmptyPreview() { @Composable fun LiveSoundLevelChartActivePreview() { DbCheckTheme { - LiveSoundLevelChartPreviewContainer { + ComponentPreviewContainer { LiveSoundLevelChart( points = previewLiveChartData, isRecording = true, @@ -588,7 +590,7 @@ fun LiveSoundLevelChartActivePreview() { @Composable fun LiveSoundLevelChartActiveDarkPreview() { DbCheckTheme { - LiveSoundLevelChartPreviewContainer { + ComponentPreviewContainer { LiveSoundLevelChart( points = previewLiveChartData, isRecording = true, @@ -602,7 +604,7 @@ fun LiveSoundLevelChartActiveDarkPreview() { @Composable fun LiveSoundLevelChartPausedDarkPreview() { DbCheckTheme { - LiveSoundLevelChartPreviewContainer { + ComponentPreviewContainer { LiveSoundLevelChart( points = previewLiveChartData.take(8), isRecording = false, @@ -685,7 +687,7 @@ fun DosimeterGaugeOverLimitDarkPreview() { @Composable fun SoundReferenceCardCollapsedPreview() { DbCheckTheme { - SoundReferenceCardPreviewContainer { + ComponentPreviewContainer { SoundReferenceCard( currentDb = 67f, markers = SoundReferenceCatalog.referenceMarkers, @@ -703,7 +705,7 @@ fun SoundReferenceCardCollapsedPreview() { @Composable fun SoundReferenceCardExpandedDarkPreview() { DbCheckTheme { - SoundReferenceCardPreviewContainer { + ComponentPreviewContainer { SoundReferenceCard( currentDb = 101f, markers = SoundReferenceCatalog.referenceMarkers, @@ -737,21 +739,7 @@ fun WaveformStylesPreview() { @Composable fun SessionCardPreview() { DbCheckTheme { - Column(modifier = Modifier.padding(16.dp)) { - SessionCard( - state = - SessionCardState( - emoji = "dB", - title = "Warehouse calibration run with a longer title", - metadata = "18 MIN / 68 AVG / A-WEIGHTED", - peakDb = 94f, - avgDb = 68f, - tags = listOf("workshop", "calibration", "shift-a"), - isSleepSession = true, - ), - editAction = SessionCardEditAction(isLocked = true, onClick = {}), - ) - } + SessionCardPreviewContent(isLocked = true) } } @@ -760,21 +748,26 @@ fun SessionCardPreview() { @Composable fun SessionCardLargeFontPreview() { DbCheckTheme { - Column(modifier = Modifier.padding(16.dp)) { - SessionCard( - state = - SessionCardState( - emoji = "dB", - title = "Warehouse calibration run with a longer title", - metadata = "18 MIN / 68 AVG / A-WEIGHTED", - peakDb = 94f, - avgDb = 68f, - tags = listOf("workshop", "calibration", "shift-a"), - isSleepSession = true, - ), - editAction = SessionCardEditAction(isLocked = false, onClick = {}), - ) - } + SessionCardPreviewContent(isLocked = false) + } +} + +@Composable +private fun SessionCardPreviewContent(isLocked: Boolean) { + Column(modifier = Modifier.padding(16.dp)) { + SessionCard( + state = + SessionCardState( + emoji = "dB", + title = "Warehouse calibration run with a longer title", + metadata = "18 MIN / 68 AVG / A-WEIGHTED", + peakDb = 94f, + avgDb = 68f, + tags = listOf("workshop", "calibration", "shift-a"), + isSleepSession = true, + ), + editAction = SessionCardEditAction(isLocked = isLocked, onClick = {}), + ) } } @@ -857,7 +850,7 @@ fun DbHistogramCardPreview() { DbCheckTheme { Column(modifier = Modifier.padding(16.dp)) { DbHistogramCard( - buckets = previewHistogramBuckets, + buckets = lockedPreviewHistogramBuckets, isLocked = false, onUpgradeClick = {}, ) @@ -872,7 +865,7 @@ fun DbHistogramCardLockedDarkPreview() { DbCheckTheme { Column(modifier = Modifier.padding(16.dp)) { DbHistogramCard( - buckets = previewHistogramBuckets, + buckets = lockedPreviewHistogramBuckets, isLocked = true, onUpgradeClick = {}, ) @@ -898,7 +891,7 @@ fun SleepResultsCardPreview() { peakEventCount = 2, loudPeriodCount = 3, sampleCount = 462, - histogramBuckets = previewHistogramBuckets, + histogramBuckets = lockedPreviewHistogramBuckets, ), ) } @@ -974,24 +967,16 @@ fun SpectralAnalysisIdlePreview() { @Composable fun SpectralAnalysisLivePreview() { DbCheckTheme { - Column(modifier = Modifier.padding(16.dp)) { - SpectralAnalysisCard( - state = - SpectralAnalysisCardState( - spectralState = - SpectralAnalysisUiState.Live( - bands = previewSpectralBands(), - dominantFrequencyHz = 2400f, - bandwidth = SpectralBandwidth.WIDE, - ), - spectrogramState = previewSpectrogramState, - rtaState = previewRtaState, - selectedMode = SpectralMode.BARS, - isLocked = false, - ), - actions = SpectralAnalysisCardActions(onUpgradeClick = {}), - ) - } + SpectralAnalysisPreviewContent(selectedMode = SpectralMode.BARS) + } +} + +@PreviewTest +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun SpectralAnalysisLiveDarkPreview() { + DbCheckTheme { + SpectralAnalysisPreviewContent(selectedMode = SpectralMode.BARS) } } @@ -1140,44 +1125,15 @@ private val previewWaveformData = 0.68f, ) -private val previewHistogramBuckets = - listOf( - DbHistogramBucket(minDb = 0, maxDb = 10, sampleCount = 0, percent = 0), - DbHistogramBucket(minDb = 10, maxDb = 20, sampleCount = 0, percent = 0), - DbHistogramBucket(minDb = 20, maxDb = 30, sampleCount = 1, percent = 4), - DbHistogramBucket(minDb = 30, maxDb = 40, sampleCount = 2, percent = 8), - DbHistogramBucket(minDb = 40, maxDb = 50, sampleCount = 4, percent = 15), - DbHistogramBucket(minDb = 50, maxDb = 60, sampleCount = 6, percent = 23), - DbHistogramBucket(minDb = 60, maxDb = 70, sampleCount = 5, percent = 19), - DbHistogramBucket(minDb = 70, maxDb = 80, sampleCount = 4, percent = 15), - DbHistogramBucket(minDb = 80, maxDb = 90, sampleCount = 2, percent = 8), - DbHistogramBucket(minDb = 90, maxDb = 100, sampleCount = 1, percent = 4), - DbHistogramBucket(minDb = 100, maxDb = 110, sampleCount = 1, percent = 4), - DbHistogramBucket(minDb = 110, maxDb = 120, sampleCount = 0, percent = 0), - DbHistogramBucket(minDb = 120, maxDb = 130, sampleCount = 0, percent = 0), - ) - -@Composable -private fun SoundReferenceCardPreviewContainer(content: @Composable () -> Unit) { - Column( - modifier = - Modifier - .fillMaxWidth() - .background(DbCheckTheme.colorScheme.material.surface) - .padding(16.dp), - ) { - content() - } -} - @Composable -private fun LiveSoundLevelChartPreviewContainer(content: @Composable () -> Unit) { +private fun ComponentPreviewContainer(content: @Composable () -> Unit) { Column( modifier = Modifier .fillMaxWidth() + .clip(RoundedCornerShape(DbCheckRadii.Row)) .background(DbCheckTheme.colorScheme.material.surface) - .padding(16.dp), + .padding(DbCheckTheme.spacing.space4), ) { content() } @@ -1308,24 +1264,16 @@ fun SoundDetectionErrorDarkPreview() { @Composable fun SpectralAnalysisSpectrogramPreview() { DbCheckTheme { - Column(modifier = Modifier.padding(16.dp)) { - SpectralAnalysisCard( - state = - SpectralAnalysisCardState( - spectralState = - SpectralAnalysisUiState.Live( - bands = previewSpectralBands(), - dominantFrequencyHz = 2400f, - bandwidth = SpectralBandwidth.WIDE, - ), - spectrogramState = previewSpectrogramState, - rtaState = previewRtaState, - selectedMode = SpectralMode.SPECTROGRAM, - isLocked = false, - ), - actions = SpectralAnalysisCardActions(onUpgradeClick = {}), - ) - } + SpectralAnalysisPreviewContent(selectedMode = SpectralMode.SPECTROGRAM) + } +} + +@PreviewTest +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun SpectralAnalysisSpectrogramDarkPreview() { + DbCheckTheme { + SpectralAnalysisPreviewContent(selectedMode = SpectralMode.SPECTROGRAM) } } @@ -1334,24 +1282,38 @@ fun SpectralAnalysisSpectrogramPreview() { @Composable fun SpectralAnalysisRtaPreview() { DbCheckTheme { - Column(modifier = Modifier.padding(16.dp)) { - SpectralAnalysisCard( - state = - SpectralAnalysisCardState( - spectralState = - SpectralAnalysisUiState.Live( - bands = previewSpectralBands(), - dominantFrequencyHz = 2400f, - bandwidth = SpectralBandwidth.WIDE, - ), - spectrogramState = previewSpectrogramState, - rtaState = previewRtaState, - selectedMode = SpectralMode.RTA, - isLocked = false, - ), - actions = SpectralAnalysisCardActions(onUpgradeClick = {}), - ) - } + SpectralAnalysisPreviewContent(selectedMode = SpectralMode.RTA) + } +} + +@PreviewTest +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun SpectralAnalysisRtaDarkPreview() { + DbCheckTheme { + SpectralAnalysisPreviewContent(selectedMode = SpectralMode.RTA) + } +} + +@Composable +private fun SpectralAnalysisPreviewContent(selectedMode: SpectralMode) { + Column(modifier = Modifier.padding(16.dp)) { + SpectralAnalysisCard( + state = + SpectralAnalysisCardState( + spectralState = + SpectralAnalysisUiState.Live( + bands = spectralPreviewBands, + dominantFrequencyHz = 2400f, + bandwidth = SpectralBandwidth.WIDE, + ), + spectrogramState = previewSpectrogramState, + rtaState = previewRtaState, + selectedMode = selectedMode, + isLocked = false, + ), + actions = SpectralAnalysisCardActions(onUpgradeClick = {}), + ) } } @@ -1363,80 +1325,16 @@ private val previewSpectrogramState SpectrogramRowUiState( timestampMs = rowIndex.toLong(), bands = - List(previewSpectralCenterFrequenciesHz.size) { bandIndex -> + List(spectralPreviewBands.size) { bandIndex -> SpectralBandUiState( normalizedAmplitude = ((rowIndex + bandIndex) % 8 + 1) / 8f, - centerFrequencyHz = previewSpectralCenterFrequenciesHz[bandIndex], + centerFrequencyHz = spectralPreviewBands[bandIndex].centerFrequencyHz, ) }, ) }, ) -private fun previewSpectralBands(): List = - previewSpectralAmplitudes.mapIndexed { index, amplitude -> - SpectralBandUiState( - normalizedAmplitude = amplitude, - centerFrequencyHz = previewSpectralCenterFrequenciesHz[index], - ) - } - -private val previewSpectralCenterFrequenciesHz = - listOf( - 24f, - 33f, - 46f, - 65f, - 91f, - 127f, - 178f, - 249f, - 349f, - 489f, - 685f, - 960f, - 1_345f, - 1_884f, - 2_400f, - 3_699f, - 5_184f, - 7_264f, - 10_177f, - 14_258f, - 19_975f, - 20_000f, - 20_000f, - 20_000f, - ) - -private val previewSpectralAmplitudes = - listOf( - 0.12f, - 0.16f, - 0.2f, - 0.24f, - 0.28f, - 0.34f, - 0.42f, - 0.5f, - 0.58f, - 0.66f, - 0.74f, - 0.82f, - 0.9f, - 0.94f, - 0.98f, - 0.82f, - 0.64f, - 0.48f, - 0.36f, - 0.28f, - 0.22f, - 0.16f, - 0.12f, - 0.08f, - ) - private val previewRtaState = RtaUiState.Data( bands = diff --git a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartActiveDarkPreview_4b098843_0.png b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartActiveDarkPreview_4b098843_0.png index abbbcda..7c50a38 100644 Binary files a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartActiveDarkPreview_4b098843_0.png and b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartActiveDarkPreview_4b098843_0.png differ diff --git a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartActivePreview_74131fac_0.png b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartActivePreview_74131fac_0.png index 9fa8755..43b991d 100644 Binary files a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartActivePreview_74131fac_0.png and b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartActivePreview_74131fac_0.png differ diff --git a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartEmptyPreview_74131fac_0.png b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartEmptyPreview_74131fac_0.png index 4a4971a..a1cfd2d 100644 Binary files a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartEmptyPreview_74131fac_0.png and b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartEmptyPreview_74131fac_0.png differ diff --git a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartPausedDarkPreview_4b098843_0.png b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartPausedDarkPreview_4b098843_0.png index 2abfe2f..d26c6a8 100644 Binary files a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartPausedDarkPreview_4b098843_0.png and b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/LiveSoundLevelChartPausedDarkPreview_4b098843_0.png differ diff --git a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SoundReferenceCardCollapsedPreview_74131fac_0.png b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SoundReferenceCardCollapsedPreview_74131fac_0.png index 3236e70..8eb55f6 100644 Binary files a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SoundReferenceCardCollapsedPreview_74131fac_0.png and b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SoundReferenceCardCollapsedPreview_74131fac_0.png differ diff --git a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SoundReferenceCardExpandedDarkPreview_4b098843_0.png b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SoundReferenceCardExpandedDarkPreview_4b098843_0.png index 85a974b..09e93b6 100644 Binary files a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SoundReferenceCardExpandedDarkPreview_4b098843_0.png and b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SoundReferenceCardExpandedDarkPreview_4b098843_0.png differ diff --git a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SpectralAnalysisLiveDarkPreview_4b098843_0.png b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SpectralAnalysisLiveDarkPreview_4b098843_0.png new file mode 100644 index 0000000..9e1b752 Binary files /dev/null and b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SpectralAnalysisLiveDarkPreview_4b098843_0.png differ diff --git a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SpectralAnalysisRtaDarkPreview_4b098843_0.png b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SpectralAnalysisRtaDarkPreview_4b098843_0.png new file mode 100644 index 0000000..99c29ac Binary files /dev/null and b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SpectralAnalysisRtaDarkPreview_4b098843_0.png differ diff --git a/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SpectralAnalysisSpectrogramDarkPreview_4b098843_0.png b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SpectralAnalysisSpectrogramDarkPreview_4b098843_0.png new file mode 100644 index 0000000..23e1c02 Binary files /dev/null and b/app/src/screenshotTestDebug/reference/com/dbcheck/app/ComponentScreenshotTestsKt/SpectralAnalysisSpectrogramDarkPreview_4b098843_0.png differ diff --git a/app/src/test/java/com/dbcheck/app/ui/UiDocumentationContractTest.kt b/app/src/test/java/com/dbcheck/app/ui/UiDocumentationContractTest.kt index 3e79d2f..7c352a0 100644 --- a/app/src/test/java/com/dbcheck/app/ui/UiDocumentationContractTest.kt +++ b/app/src/test/java/com/dbcheck/app/ui/UiDocumentationContractTest.kt @@ -155,10 +155,10 @@ class UiDocumentationContractTest { val counts = measuredScreenshotCounts() val documents = listOf(rootDocument("UI-SPEC.md"), rootDocument("PROJECT.md")) - assertEquals(56, counts.componentPreviews) + assertEquals(59, counts.componentPreviews) assertEquals(39, counts.fullScreenPreviews) assertEquals(5, counts.largeFontPreviews) - assertEquals(95, counts.referencePngs) + assertEquals(98, counts.referencePngs) documents.forEach { document -> assertEquals(emptyList(), screenshotDocumentationViolations(document, counts)) } diff --git a/app/src/test/java/com/dbcheck/app/ui/hearing/HearingViewModelTest.kt b/app/src/test/java/com/dbcheck/app/ui/hearing/HearingViewModelTest.kt index 4c7c8d5..be21d49 100644 --- a/app/src/test/java/com/dbcheck/app/ui/hearing/HearingViewModelTest.kt +++ b/app/src/test/java/com/dbcheck/app/ui/hearing/HearingViewModelTest.kt @@ -175,13 +175,7 @@ class HearingViewModelTest { initialPreferences = UserPreferences(isProUser = false, soundDetectionEnabled = true), initialRecording = true, ) { harness, state -> - assertFalse(state.canCalibrateVoiceBaseline) - - harness.viewModel.calibrateVoiceBaseline() - runCurrent() - - verify(exactly = 0) { harness.audioSessionManager.captureVoiceBaseline(any()) } - coVerify(exactly = 0) { harness.preferencesRepository.updateVoiceBaseline(any(), any(), any()) } + assertVoiceBaselineCaptureBlocked(harness, state) } @Test @@ -189,13 +183,7 @@ class HearingViewModelTest { initialPreferences = UserPreferences(isProUser = true, soundDetectionEnabled = true), initialRecording = false, ) { harness, state -> - assertFalse(state.canCalibrateVoiceBaseline) - - harness.viewModel.calibrateVoiceBaseline() - runCurrent() - - verify(exactly = 0) { harness.audioSessionManager.captureVoiceBaseline(any()) } - coVerify(exactly = 0) { harness.preferencesRepository.updateVoiceBaseline(any(), any(), any()) } + assertVoiceBaselineCaptureBlocked(harness, state) } @Test @@ -203,6 +191,13 @@ class HearingViewModelTest { initialPreferences = UserPreferences(isProUser = true, soundDetectionEnabled = false), initialRecording = true, ) { harness, state -> + assertVoiceBaselineCaptureBlocked(harness, state) + } + + private fun HearingTestScope.assertVoiceBaselineCaptureBlocked( + harness: HearingViewModelHarness, + state: HearingUiState, + ) { assertFalse(state.canCalibrateVoiceBaseline) harness.viewModel.calibrateVoiceBaseline() diff --git a/app/src/test/java/com/dbcheck/app/ui/meter/components/ExpandableCardHeaderComposeTest.kt b/app/src/test/java/com/dbcheck/app/ui/meter/components/ExpandableCardHeaderComposeTest.kt new file mode 100644 index 0000000..035a95b --- /dev/null +++ b/app/src/test/java/com/dbcheck/app/ui/meter/components/ExpandableCardHeaderComposeTest.kt @@ -0,0 +1,78 @@ +package com.dbcheck.app.ui.meter.components + +import android.app.Application +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Text +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.SemanticsProperties +import androidx.compose.ui.test.SemanticsMatcher +import androidx.compose.ui.test.assert +import androidx.compose.ui.test.assertHasClickAction +import androidx.compose.ui.test.junit4.v2.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import com.dbcheck.app.ui.theme.DbCheckTheme +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(application = Application::class, sdk = [28]) +class ExpandableCardHeaderComposeTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun headerIsAccessibleAndTogglesStateInLightTheme() { + verifyExpandableHeader(darkTheme = false) + } + + @Test + fun headerIsAccessibleAndTogglesStateInDarkTheme() { + verifyExpandableHeader(darkTheme = true) + } + + private fun verifyExpandableHeader(darkTheme: Boolean) { + val headerText = "Expandable header" + val collapsedLabel = "Collapsed" + val expandedLabel = "Expanded" + val expandedContent = "Expanded content" + + composeTestRule.setContent { + DbCheckTheme(darkTheme = darkTheme) { + var expanded by remember { mutableStateOf(false) } + Column( + modifier = + Modifier.expandableCardHeader( + spacing = DbCheckTheme.spacing, + stateLabel = if (expanded) expandedLabel else collapsedLabel, + expanded = expanded, + onExpandedChange = { expanded = it }, + ), + ) { + Text(headerText) + if (expanded) { + Text(expandedContent) + } + } + } + } + + composeTestRule + .onNodeWithText(headerText) + .assertHasClickAction() + .assert(SemanticsMatcher.expectValue(SemanticsProperties.StateDescription, collapsedLabel)) + .performClick() + + composeTestRule.onNodeWithText(expandedContent).assertExists() + composeTestRule + .onNodeWithText(headerText) + .assert(SemanticsMatcher.expectValue(SemanticsProperties.StateDescription, expandedLabel)) + } +} diff --git a/app/src/test/java/com/dbcheck/app/ui/meter/components/MeterExpandableComponentsContractTest.kt b/app/src/test/java/com/dbcheck/app/ui/meter/components/MeterExpandableComponentsContractTest.kt index 1629476..66c91fc 100644 --- a/app/src/test/java/com/dbcheck/app/ui/meter/components/MeterExpandableComponentsContractTest.kt +++ b/app/src/test/java/com/dbcheck/app/ui/meter/components/MeterExpandableComponentsContractTest.kt @@ -12,9 +12,7 @@ class MeterExpandableComponentsContractTest { val expandedBody = source.substringAfter("if (expanded)") assertTrue(source.contains("R.string.meter_live_details")) - assertTrue(source.contains(".heightIn(min = spacing.space12)")) - assertTrue(source.contains("this.stateDescription = stateLabel")) - assertTrue(source.contains("onExpandedChange(!expanded)")) + assertTrue(source.contains(".expandableCardHeader(")) assertTrue(expandedBody.contains("LiveSoundLevelChart(")) assertTrue(expandedBody.contains("WaveformVisualization(")) } @@ -33,8 +31,7 @@ class MeterExpandableComponentsContractTest { assertTrue(collapsedRow.contains("R.string.sound_reference_db_short")) assertTrue(collapsedRow.contains("R.string.sound_reference_current_db")) assertTrue(collapsedRow.contains("Icons.Outlined.Expand")) - assertTrue(collapsedRow.contains(".heightIn(min = spacing.space12)")) - assertTrue(collapsedRow.contains("this.stateDescription = stateLabel")) + assertTrue(collapsedRow.contains(".expandableCardHeader(")) assertTrue(expandedBody.contains("SoundReferenceRail(")) assertTrue(expandedBody.contains("markers.forEach")) } diff --git a/app/stability/app-debug.stability b/app/stability/app-debug.stability index 11591c1..36de17a 100644 --- a/app/stability/app-debug.stability +++ b/app/stability/app-debug.stability @@ -2696,6 +2696,14 @@ private fun com.dbcheck.app.ui.settings.components.BackupSection(state: com.dbch - state: RUNTIME (requires runtime check) - actions: STABLE (class with no mutable properties) +@Composable +private fun com.dbcheck.app.ui.settings.components.CalibrationChipControls(title: kotlin.String, content: @[Composable] androidx.compose.runtime.internal.ComposableFunction0): kotlin.Unit + skippable: true + restartable: true + params: + - title: STABLE (String is immutable) + - content: STABLE (composable function type) + @Composable private fun com.dbcheck.app.ui.settings.components.CalibrationControlGroup(title: kotlin.String, subtitle: kotlin.String, trailingContent: @[Composable] androidx.compose.runtime.internal.ComposableFunction0, content: @[Composable] @[ExtensionFunctionType] androidx.compose.runtime.internal.ComposableFunction1): kotlin.Unit skippable: true diff --git a/app/stability/app-release.stability b/app/stability/app-release.stability index 11591c1..36de17a 100644 --- a/app/stability/app-release.stability +++ b/app/stability/app-release.stability @@ -2696,6 +2696,14 @@ private fun com.dbcheck.app.ui.settings.components.BackupSection(state: com.dbch - state: RUNTIME (requires runtime check) - actions: STABLE (class with no mutable properties) +@Composable +private fun com.dbcheck.app.ui.settings.components.CalibrationChipControls(title: kotlin.String, content: @[Composable] androidx.compose.runtime.internal.ComposableFunction0): kotlin.Unit + skippable: true + restartable: true + params: + - title: STABLE (String is immutable) + - content: STABLE (composable function type) + @Composable private fun com.dbcheck.app.ui.settings.components.CalibrationControlGroup(title: kotlin.String, subtitle: kotlin.String, trailingContent: @[Composable] androidx.compose.runtime.internal.ComposableFunction0, content: @[Composable] @[ExtensionFunctionType] androidx.compose.runtime.internal.ComposableFunction1): kotlin.Unit skippable: true diff --git a/buildscript-gradle.lockfile b/buildscript-gradle.lockfile index 3d426ab..3b75f81 100644 --- a/buildscript-gradle.lockfile +++ b/buildscript-gradle.lockfile @@ -49,8 +49,8 @@ com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.22.1=classpath com.fasterxml.jackson.module:jackson-module-blackbird:2.22.1=classpath com.fasterxml.jackson:jackson-bom:2.22.1=classpath com.github.package-url:packageurl-java:1.5.0=classpath -com.github.skydoves.compose.stability.analyzer:com.github.skydoves.compose.stability.analyzer.gradle.plugin:0.11.0=classpath -com.github.skydoves:compose-stability-gradle:0.11.0=classpath +com.github.skydoves.compose.stability.analyzer:com.github.skydoves.compose.stability.analyzer.gradle.plugin:0.11.1=classpath +com.github.skydoves:compose-stability-gradle:0.11.1=classpath com.github.spullara.mustache.java:compiler:0.9.6=classpath com.google.auto.value:auto-value-annotations:1.6.2=classpath com.google.code.findbugs:jsr305:3.0.2=classpath diff --git a/config/dependency-check-suppressions.xml b/config/dependency-check-suppressions.xml index b1c8a6d..7bef9e6 100644 --- a/config/dependency-check-suppressions.xml +++ b/config/dependency-check-suppressions.xml @@ -24,7 +24,7 @@ Compose Stability Analyzer Runtime ei ole GitHub Enterprise Server. Dependency-Check yhdistaa com.github.skydoves-ryhman virheellisesti cpe:/a:github:github-tunnisteeseen. ]]> - ^pkg:maven/com\.github\.skydoves/compose-stability-runtime-android@0\.7\.4$ + ^pkg:maven/com\.github\.skydoves/compose-stability-runtime-android@0\.11\.1$ cpe:/a:github:github diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e947c7c..d204e2d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ hiltNavigationCompose = "1.4.0" healthConnect = "1.1.0" cameraX = "1.6.1" composeBom = "2026.06.01" -composeStabilityAnalyzer = "0.11.0" +composeStabilityAnalyzer = "0.11.1" androidSecurityLints = "1.0.4" navigationCompose = "2.9.8" room = "2.8.4" @@ -56,6 +56,8 @@ androidx-compose-material3 = { group = "androidx.compose.material3", name = "mat androidx-compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" } androidx-compose-animation = { group = "androidx.compose.animation", name = "animation" } androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" } +androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } +androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } # Navigation androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index e3aa5c9..61ad64a 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -1252,6 +1252,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2190,6 +2224,9 @@ + + + @@ -2198,6 +2235,9 @@ + + + @@ -2228,6 +2268,9 @@ + + + @@ -2935,6 +2978,9 @@ + + + @@ -2964,6 +3010,9 @@ + + + @@ -2993,6 +3042,9 @@ + + + @@ -3307,6 +3359,14 @@ + + + + + + + + @@ -3336,6 +3396,22 @@ + + + + + + + + + + + + + + + + @@ -3344,6 +3420,22 @@ + + + + + + + + + + + + + + + + @@ -5906,6 +5998,14 @@ + + + + + + + + @@ -5922,6 +6022,14 @@ + + + + + + + + @@ -5935,6 +6043,11 @@ + + + + + @@ -5948,6 +6061,17 @@ + + + + + + + + + + + @@ -5967,6 +6091,14 @@ + + + + + + + + @@ -5980,6 +6112,11 @@ + + + + + @@ -7321,6 +7458,14 @@ + + + + + + + + @@ -8615,6 +8760,9 @@ + + + @@ -8645,6 +8793,9 @@ + + + @@ -9547,6 +9698,22 @@ + + + + + + + + + + + + + + + + @@ -10346,6 +10513,9 @@ + + +