Implement CameraX GPG QR transfer on Android (#106)
Android ARM verification / rust (push) Failing after 47m42s
Android ARM verification / android (push) Failing after 8s
Dependency security audit / rustsec (push) Successful in 23s

This commit is contained in:
hugo committed 2026-10-12 09:49:38 +02:00
1 parent 6669b77f4e
commit d466b170ab
33 files changed
+1616 -162

No files matched your search

+2
View File
@@ -8,6 +8,8 @@ UniFFI API. Local-first and HTTPS onboarding is described in
revision-checked [adaptive navigation](../docs/android/password-navigation.md);
Home renders Rust-owned [Git activity and HTTPS synchronization](../docs/android/home-sync.md).
Passwords and Preferences share [Rust-owned biometric authentication](../docs/android/authentication.md).
CameraX GPG scanning and QR export are described in
[key transfer](../docs/android/key-transfer.md).
Remaining feature screens retain their milestone issues. The full [CI and emulator workflow](../docs/android/ci.md) documents the
ARM-only runner/toolchain policy and artifact audits.
+29 -13
View File
@@ -16,6 +16,14 @@ androidx.arch.core:core-common:2.2.0=benchmarkCompileClasspath,benchmarkLintChec
androidx.arch.core:core-runtime:2.2.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.autofill:autofill:1.0.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.biometric:biometric:1.1.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera.featurecombinationquery:featurecombinationquery:1.6.2=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera.viewfinder:viewfinder-core:1.5.1=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-camera2-pipe:1.6.2=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-camera2:1.6.2=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-core:1.6.2=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-lifecycle:1.6.2=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-video:1.6.2=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-view:1.6.2=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.cardview:cardview:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.collection:collection-jvm:1.5.0=benchmarkCompileClasspath,debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,profileCompileClasspath,releaseCompileClasspath
androidx.collection:collection-jvm:1.6.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
@@ -67,10 +75,12 @@ androidx.compose.ui:ui-util-android:1.12.1=benchmarkCompileClasspath,benchmarkLi
androidx.compose.ui:ui-util:1.12.1=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose.ui:ui:1.12.1=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.compose:compose-bom:2026.09.00=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.concurrent:concurrent-futures-ktx:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.concurrent:concurrent-futures-ktx:1.1.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.concurrent:concurrent-futures-ktx:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath
androidx.concurrent:concurrent-futures:1.1.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.concurrent:concurrent-futures:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath
androidx.coordinatorlayout:coordinatorlayout:1.1.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.core:core-backported-fixes:1.0.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.core:core-ktx:1.18.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.core:core-viewtree:1.0.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.core:core:1.18.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
@@ -79,6 +89,7 @@ androidx.customview:customview-poolingcontainer:1.0.0=benchmarkLintChecksClasspa
androidx.customview:customview:1.0.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.drawerlayout:drawerlayout:1.0.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.emoji2:emoji2:1.4.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.exifinterface:exifinterface:1.4.2=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.fragment:fragment:1.9.1=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.graphics:graphics-path:1.0.1=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.interpolator:interpolator:1.0.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
@@ -103,6 +114,9 @@ androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.11.0=benchmarkCompil
androidx.lifecycle:lifecycle-viewmodel-savedstate:2.11.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel:2.11.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.loader:loader:1.0.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.media3:media3-common:1.9.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.media3:media3-container:1.9.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.media3:media3-muxer:1.9.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation3:navigation3-runtime-android:1.2.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation3:navigation3-runtime:1.2.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.navigation3:navigation3-ui-android:1.2.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
@@ -128,6 +142,7 @@ androidx.test:monitor:1.8.0=debugAndroidTestCompileClasspath,debugAndroidTestLin
androidx.test:rules:1.4.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.test:runner:1.7.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.tracing:tracing-android:2.0.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.tracing:tracing-ktx:2.0.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.tracing:tracing:1.1.0=debugAndroidTestCompileClasspath
androidx.tracing:tracing:2.0.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.transition:transition:1.2.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
@@ -174,6 +189,7 @@ com.github.ajalt.clikt:clikt-jvm:5.0.3=ktlint
com.github.ajalt.clikt:clikt:5.0.3=ktlint
com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:4.1.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
com.google.android.material:material:1.2.0-rc01=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
com.google.auto.value:auto-value-annotations:1.6.3=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.code.findbugs:jsr305:3.0.2=_agp_internal_benchmark_kspClasspath,_agp_internal_debug_kspClasspath,_agp_internal_profile_kspClasspath,_agp_internal_release_kspClasspath,androidLintTool,benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,hiltAnnotationProcessorBenchmark,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorProfile,hiltAnnotationProcessorRelease,kspBenchmarkKotlinProcessorClasspath,kspDebugKotlinProcessorClasspath,kspProfileKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.code.gson:gson:2.11.0=androidLintTool
com.google.dagger:dagger-compiler:2.60.1=_agp_internal_benchmark_kspClasspath,_agp_internal_debug_kspClasspath,_agp_internal_profile_kspClasspath,_agp_internal_release_kspClasspath,hiltAnnotationProcessorBenchmark,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorProfile,hiltAnnotationProcessorRelease,kspBenchmarkKotlinProcessorClasspath,kspDebugKotlinProcessorClasspath,kspProfileKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath
@@ -185,20 +201,19 @@ com.google.dagger:hilt-compiler:2.60.1=_agp_internal_benchmark_kspClasspath,_agp
com.google.dagger:hilt-core:2.60.1=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.devtools.ksp:symbol-processing-api:2.3.12=kotlinCompilerPluginClasspathBenchmark,kotlinCompilerPluginClasspathDebug,kotlinCompilerPluginClasspathDebugAndroidTest,kotlinCompilerPluginClasspathDebugUnitTest,kotlinCompilerPluginClasspathProfile,kotlinCompilerPluginClasspathRelease,kspPluginClasspath,kspPluginClasspathNonEmbeddable
com.google.devtools.ksp:symbol-processing-api:2.3.7=_agp_internal_benchmark_kspClasspath,_agp_internal_debug_kspClasspath,_agp_internal_profile_kspClasspath,_agp_internal_release_kspClasspath,hiltAnnotationProcessorBenchmark,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorProfile,hiltAnnotationProcessorRelease,kspBenchmarkKotlinProcessorClasspath,kspDebugKotlinProcessorClasspath,kspProfileKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath
com.google.errorprone:error_prone_annotations:2.30.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
com.google.errorprone:error_prone_annotations:2.28.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.errorprone:error_prone_annotations:2.30.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath
com.google.errorprone:error_prone_annotations:2.36.0=androidLintTool
com.google.errorprone:error_prone_annotations:2.47.0=_agp_internal_benchmark_kspClasspath,_agp_internal_debug_kspClasspath,_agp_internal_profile_kspClasspath,_agp_internal_release_kspClasspath,hiltAnnotationProcessorBenchmark,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorProfile,hiltAnnotationProcessorRelease,kspBenchmarkKotlinProcessorClasspath,kspDebugKotlinProcessorClasspath,kspProfileKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath
com.google.googlejavaformat:google-java-format:1.33.0=_agp_internal_benchmark_kspClasspath,_agp_internal_debug_kspClasspath,_agp_internal_profile_kspClasspath,_agp_internal_release_kspClasspath,hiltAnnotationProcessorBenchmark,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorProfile,hiltAnnotationProcessorRelease,kspBenchmarkKotlinProcessorClasspath,kspDebugKotlinProcessorClasspath,kspProfileKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath
com.google.guava:failureaccess:1.0.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
com.google.guava:failureaccess:1.0.2=androidLintTool
com.google.guava:failureaccess:1.0.2=androidLintTool,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.guava:failureaccess:1.0.3=_agp_internal_benchmark_kspClasspath,_agp_internal_debug_kspClasspath,_agp_internal_profile_kspClasspath,_agp_internal_release_kspClasspath,hiltAnnotationProcessorBenchmark,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorProfile,hiltAnnotationProcessorRelease,kspBenchmarkKotlinProcessorClasspath,kspDebugKotlinProcessorClasspath,kspProfileKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath
com.google.guava:guava:31.0.1-android=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
com.google.guava:guava:33.3.1-android=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.guava:guava:33.4.0-jre=androidLintTool
com.google.guava:guava:33.6.0-jre=_agp_internal_benchmark_kspClasspath,_agp_internal_debug_kspClasspath,_agp_internal_profile_kspClasspath,_agp_internal_release_kspClasspath,hiltAnnotationProcessorBenchmark,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorProfile,hiltAnnotationProcessorRelease,kspBenchmarkKotlinProcessorClasspath,kspDebugKotlinProcessorClasspath,kspProfileKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath
com.google.guava:listenablefuture:1.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=_agp_internal_benchmark_kspClasspath,_agp_internal_debug_kspClasspath,_agp_internal_profile_kspClasspath,_agp_internal_release_kspClasspath,androidLintTool,debugAndroidTestLintChecksClasspath,hiltAnnotationProcessorBenchmark,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorProfile,hiltAnnotationProcessorRelease,kspBenchmarkKotlinProcessorClasspath,kspDebugKotlinProcessorClasspath,kspProfileKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath
com.google.j2objc:j2objc-annotations:1.3=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
com.google.j2objc:j2objc-annotations:3.0.0=androidLintTool
com.google.guava:listenablefuture:1.0=benchmarkCompileClasspath,debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,profileCompileClasspath,releaseCompileClasspath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=_agp_internal_benchmark_kspClasspath,_agp_internal_debug_kspClasspath,_agp_internal_profile_kspClasspath,_agp_internal_release_kspClasspath,androidLintTool,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,hiltAnnotationProcessorBenchmark,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorProfile,hiltAnnotationProcessorRelease,kspBenchmarkKotlinProcessorClasspath,kspDebugKotlinProcessorClasspath,kspProfileKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.j2objc:j2objc-annotations:3.0.0=androidLintTool,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.j2objc:j2objc-annotations:3.1=_agp_internal_benchmark_kspClasspath,_agp_internal_debug_kspClasspath,_agp_internal_profile_kspClasspath,_agp_internal_release_kspClasspath,hiltAnnotationProcessorBenchmark,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorProfile,hiltAnnotationProcessorRelease,kspBenchmarkKotlinProcessorClasspath,kspDebugKotlinProcessorClasspath,kspProfileKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath
com.google.jimfs:jimfs:1.1=androidLintTool
com.google.protobuf:protobuf-java:3.25.5=androidLintTool,unified-test-platform-gradle-work-action
@@ -252,15 +267,14 @@ org.bouncycastle:bcpkix-jdk18on:1.80.2=androidLintTool
org.bouncycastle:bcprov-jdk18on:1.80.2=androidLintTool
org.bouncycastle:bcutil-jdk18on:1.80.2=androidLintTool
org.checkerframework:checker-compat-qual:2.5.3=_agp_internal_benchmark_kspClasspath,_agp_internal_debug_kspClasspath,_agp_internal_profile_kspClasspath,_agp_internal_release_kspClasspath,hiltAnnotationProcessorBenchmark,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorProfile,hiltAnnotationProcessorRelease,kspBenchmarkKotlinProcessorClasspath,kspDebugKotlinProcessorClasspath,kspProfileKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath
org.checkerframework:checker-compat-qual:2.5.5=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
org.checkerframework:checker-qual:3.22.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
org.checkerframework:checker-qual:3.43.0=androidLintTool
org.checkerframework:checker-qual:3.43.0=androidLintTool,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.ec4j.core:ec4j-core:1.1.1=ktlint,ktlintBaselineReporter,ktlintRuleset
org.glassfish.jaxb:jaxb-runtime:2.3.2=androidLintTool
org.glassfish.jaxb:txw2:2.3.2=androidLintTool
org.hamcrest:hamcrest-core:1.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.hamcrest:hamcrest-library:1.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
org.jetbrains.kotlin:compose-group-mapping:2.4.21=composeMappingProducerClasspath
org.jetbrains.kotlin:kotlin-bom:1.8.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-build-tools-compat:2.4.21=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
@@ -298,6 +312,8 @@ org.jetbrains.kotlin:kotlin-stdlib:2.3.21=_agp_internal_benchmark_kspClasspath,_
org.jetbrains.kotlin:kotlin-stdlib:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.4.21=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlin:kotlin-tooling-core:2.4.21=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlinx:atomicfu-jvm:0.28.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:atomicfu:0.28.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.11.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0=androidLintTool,unified-test-platform-gradle-work-action
@@ -333,4 +349,4 @@ org.ow2.asm:asm:9.9=androidLintTool
org.ow2.asm:asm:9.9.1=composeMappingProducerClasspath
org.slf4j:slf4j-api:2.0.7=ktlint
rustls:rustls-platform-verifier:0.1.1=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
empty=_agp_internal_debugAndroidTest_kspClasspath,_agp_internal_debugUnitTest_kspClasspath,androidApis,androidJdkImage,androidTestUtil,benchmarkAnnotationProcessorClasspath,benchmarkImplementationDependenciesMetadata,benchmarkReverseMetadataValues,coreLibraryDesugaring,debugAndroidTestAnnotationProcessorClasspath,debugAndroidTestImplementationDependenciesMetadata,debugAnnotationProcessorClasspath,debugImplementationDependenciesMetadata,debugReverseMetadataValues,debugUnitTestAnnotationProcessorClasspath,debugUnitTestImplementationDependenciesMetadata,hiltCompileOnlyBenchmark,hiltCompileOnlyDebug,hiltCompileOnlyDebugAndroidTest,hiltCompileOnlyDebugUnitTest,hiltCompileOnlyProfile,hiltCompileOnlyRelease,kotlinCompilerPluginClasspath,kspDebugAndroidTestKotlinProcessorClasspath,kspDebugUnitTestKotlinProcessorClasspath,lintChecks,lintPublish,profileAnnotationProcessorClasspath,profileReverseMetadataValues,releaseAnnotationProcessorClasspath,releaseImplementationDependenciesMetadata,releaseReverseMetadataValues
empty=_agp_internal_debugAndroidTest_kspClasspath,_agp_internal_debugUnitTest_kspClasspath,androidApis,androidJdkImage,androidTestUtil,benchmarkAnnotationProcessorClasspath,benchmarkImplementationDependenciesMetadata,benchmarkReverseMetadataValues,coreLibraryDesugaring,debugAndroidTestAnnotationProcessorClasspath,debugAndroidTestImplementationDependenciesMetadata,debugAnnotationProcessorClasspath,debugImplementationDependenciesMetadata,debugReverseMetadataValues,debugUnitTestAnnotationProcessorClasspath,debugUnitTestImplementationDependenciesMetadata,hiltCompileOnlyBenchmark,hiltCompileOnlyDebug,hiltCompileOnlyDebugAndroidTest,hiltCompileOnlyDebugUnitTest,hiltCompileOnlyProfile,hiltCompileOnlyRelease,kotlinCompilerPluginClasspath,kspDebugAndroidTestKotlinProcessorClasspath,kspDebugUnitTestKotlinProcessorClasspath,lintChecks,lintPublish,profileAnnotationProcessorClasspath,profileImplementationDependenciesMetadata,profileReverseMetadataValues,releaseAnnotationProcessorClasspath,releaseImplementationDependenciesMetadata,releaseReverseMetadataValues
@@ -8,11 +8,13 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTextInput
import androidx.lifecycle.ViewModelProvider
import de.rfc1437.ironstorage.core.OnboardingRepository
import de.rfc1437.ironstorage.core.SetupKey
import de.rfc1437.ironstorage.core.SetupStatus
import de.rfc1437.ironstorage.features.OnboardingScreen
import de.rfc1437.ironstorage.features.SetupFormViewModel
import de.rfc1437.ironstorage.features.ShellViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
@@ -32,7 +34,16 @@ class OnboardingScreenTest {
val secrets = SetupFormViewModel()
compose.activity.runOnUiThread {
compose.activity.setProtectedTestContent {
MaterialTheme { OnboardingScreen(repository, secrets) }
MaterialTheme {
OnboardingScreen(
repository,
secrets,
SettingsScreenTest.Fixture(),
androidx.lifecycle.ViewModelProvider(
compose.activity
)[de.rfc1437.ironstorage.features.ShellViewModel::class.java].authentication
)
}
}
}
compose.waitForIdle()
@@ -60,6 +71,45 @@ class OnboardingScreenTest {
)
}
@Test fun closingQrReviewReloadsPreparedOnboardingKeysWithoutCreatingAStore() {
val original = FixtureRepository()
val settings = SettingsScreenTest.Fixture()
val secrets = SetupFormViewModel()
var staged = false
val repository = object : OnboardingRepository by original {
override fun refresh() {
if (staged) {
original.setup.value = original.setup.value.copy(
keys = listOf(
SetupKey("SCANNED-SETUP-KEY", listOf("Scanned setup fixture"))
),
selectedKey = "SCANNED-SETUP-KEY"
)
}
}
}
val model = ViewModelProvider(compose.activity)[ShellViewModel::class.java]
val authentication = model.authentication
compose.activity.runOnUiThread {
compose.activity.setProtectedTestContent {
MaterialTheme {
OnboardingScreen(repository, secrets, settings, authentication)
}
}
}
compose.onNodeWithText("Create local store").performClick()
compose.onNodeWithText("Import private GPG key").performScrollTo().performClick()
compose.onNodeWithText("Scan GPG QR codes").performScrollTo().performClick()
compose.runOnIdle { staged = true }
compose.onNodeWithText("Done").performClick()
compose.runOnIdle { assertEquals("SCANNED-SETUP-KEY", original.setup.value.selectedKey) }
compose.onNodeWithText("Back to setup choices").performScrollTo().performClick()
compose.onNodeWithText("Create local store").performClick()
compose.onNodeWithText("SCANNED-SETUP-KEY").performScrollTo().assertIsDisplayed()
assertTrue(!original.setup.value.configured)
assertTrue(original.calls.isEmpty())
}
private class FixtureRepository : OnboardingRepository {
override val setup = MutableStateFlow(SetupStatus())
val calls = mutableListOf<String>()
@@ -6,6 +6,8 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.test.assertHeightIsAtLeast
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertTextContains
import androidx.compose.ui.test.assertWidthIsAtLeast
import androidx.compose.ui.test.hasText
@@ -24,6 +26,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.test.platform.app.InstrumentationRegistry
import de.rfc1437.ironstorage.core.AuthenticationRepository
import de.rfc1437.ironstorage.core.AuthenticationState
import de.rfc1437.ironstorage.core.CameraPreview
import de.rfc1437.ironstorage.core.KeyTransferState
import de.rfc1437.ironstorage.core.ProtectedPresentation
import de.rfc1437.ironstorage.core.ProviderStatus
@@ -31,7 +34,9 @@ import de.rfc1437.ironstorage.core.SettingsPage
import de.rfc1437.ironstorage.core.SettingsRepository
import de.rfc1437.ironstorage.core.SettingsState
import de.rfc1437.ironstorage.core.TransferFrame
import de.rfc1437.ironstorage.core.TransferKey
import de.rfc1437.ironstorage.features.ApplicationTheme
import de.rfc1437.ironstorage.features.R as FeatureR
import de.rfc1437.ironstorage.features.SettingsScreen
import de.rfc1437.ironstorage.features.SetupFormViewModel
import de.rfc1437.ironstorage.features.ShellViewModel
@@ -164,6 +169,69 @@ class SettingsScreenTest {
authentication.presentation.clear()
}
}
@Test fun privateQrActionsRequireFingerprintConfirmationAndExplicitClosing() {
val original = ViewModelProvider(compose.activity)[ShellViewModel::class.java]
val repository = Fixture()
val authentication = object : AuthenticationRepository by original.authentication {
override val presentation = ProtectedPresentation()
}
compose.activity.runOnUiThread {
compose.activity.setProtectedTestContent(authentication.presentation) {
ApplicationTheme(authentication) {
de.rfc1437.ironstorage.features.SettingsScreen(
repository,
authentication,
original.onboarding,
ViewModelProvider(compose.activity)[SetupFormViewModel::class.java]
) {}
}
}
}
compose.onNodeWithTag(
"settings-list"
).performScrollToNode(hasText("Import or export GPG QR codes"))
compose.onNodeWithText("Import or export GPG QR codes").performClick()
compose.onNodeWithText("Export private key").performScrollTo().performClick()
compose.onNodeWithText(
"Confirm and export selected key"
).performScrollTo().assertIsNotEnabled()
compose.onNodeWithText(
"I checked this fingerprint and authorize displaying its private QR codes."
)
.performScrollTo().performClick()
compose.onNodeWithText(
"Confirm and export selected key"
).performScrollTo().assertIsEnabled().performClick()
assertEquals(1, repository.exports)
compose.runOnIdle {
repository.state.value = repository.state.value.copy(
transfer = repository.state.value.transfer.copy(
ready = TransferKey(
"SCANNED-FINGERPRINT",
"Scanned fixture",
true
),
received = 2u,
total = 2u
)
)
}
compose.onNodeWithText(
"Confirm and import reconstructed key"
).performScrollTo().assertIsNotEnabled()
compose.onNodeWithText("SCANNED-FINGERPRINT").performScrollTo().assertIsDisplayed()
compose.onNodeWithText(
"I checked this fingerprint and authorize importing its private key material."
)
.performScrollTo().performClick()
compose.onNodeWithText(
"Confirm and import reconstructed key"
).performScrollTo().assertIsEnabled().performClick()
assertEquals(1, repository.imports)
compose.onNodeWithText("Done").performClick()
assertFalse(repository.state.value.transfer.open)
}
internal class Fixture : SettingsRepository {
override val state = MutableStateFlow(
SettingsState(
@@ -193,6 +261,8 @@ class SettingsScreenTest {
var removals = 0
var reloads = 0
var choseImages = false
var exports = 0
var imports = 0
override fun reload() {
reloads += 1
}
@@ -211,15 +281,41 @@ class SettingsScreenTest {
override fun openCredentials() = Unit
override fun beginTransfer() {
state.value =
state.value.copy(error = null, transfer = KeyTransferState(open = true))
state.value.copy(
error = null,
transfer = KeyTransferState(
open = true,
keys = listOf(
TransferKey(
"PUBLIC-FINGERPRINT",
"Fixture GPG key",
true
)
)
)
)
}
override fun beginSetupTransfer() {
beginTransfer()
state.value =
state.value.copy(
transfer = state.value.transfer.copy(setup = true, keys = emptyList())
)
}
override fun cameraPreview(): CameraPreview? = null
override fun exportKey(fingerprint: String, privateKey: Boolean, passphrase: String) {
exports +=
1
}
override fun exportKey(fingerprint: String, privateKey: Boolean, passphrase: String) = Unit
override fun chooseQrImages() {
choseImages = true
}
override fun addQrImages(uris: List<String>) = Unit
override fun addQrPayload(payload: String) = Unit
override fun importKey(passphrase: String, makeDefault: Boolean, overwrite: Boolean) = Unit
override fun importKey(passphrase: String, makeDefault: Boolean, overwrite: Boolean) {
imports +=
1
}
override fun closeTransfer() {
state.value = state.value.copy(transfer = KeyTransferState())
}
+5 -1
View File
@@ -1,6 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" tools:node="remove" />
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<application
android:name=".IronStorageApplication"
android:allowBackup="false"
@@ -438,6 +438,7 @@ class NativeEntryViewerTest {
keystore: MemoryKeystore
) {
var image = ByteArray(0)
var cameraReceiver: ((CameraFrame) -> Unit)? = null
val platform = object : SettingsPlatform {
override fun autofill() = ProviderStatus(true, false, false)
override fun credentials() =
@@ -446,6 +447,13 @@ class NativeEntryViewerTest {
override fun version() = "native-settings-fixture"
override fun openAutofill() = true
override fun openCredentials() = true
override fun cameraPreview(
frame: (CameraFrame) -> Unit,
failure: () -> Unit
): CameraPreview? {
cameraReceiver = frame
return null
}
override fun chooseQrImages() = true
override suspend fun readQrImage(uri: String) = image.copyOf()
}
@@ -533,6 +541,18 @@ class NativeEntryViewerTest {
image = output.toByteArray()
request { settings.closeTransfer() }
assertTrue(frame.modules.all { it.toInt() == 0 })
assertNull(request { settings.beginTransfer() }.error)
withContext(Dispatchers.Main.immediate) { settings.cameraPreview() }
val luminance = ByteArray(size * size) { index ->
if (pixels[index] == android.graphics.Color.BLACK) 0 else 255.toByte()
}
requireNotNull(
cameraReceiver
)(CameraFrame(luminance, size.toUInt(), size.toUInt(), size.toUInt(), 1u, 90u))
withTimeout(30000) { settings.state.first { it.transfer.ready != null } }
assertEquals(fingerprint, settings.state.value.transfer.ready!!.fingerprint)
assertTrue(luminance.all { it.toInt() == 0 })
request { settings.closeTransfer() }
val qrStart = android.os.SystemClock.elapsedRealtime()
assertNull(request { settings.addQrImages(listOf("content://public-fixture/qr")) }.error)
val qrMs = android.os.SystemClock.elapsedRealtime() - qrStart
@@ -559,10 +579,14 @@ class NativeEntryViewerTest {
assertNotNull(request { settings.setDefaultKey(bob.fingerprint) }.error)
assertEquals(fingerprint, settings.state.value.page!!.defaultKeyFingerprint)
assertNull(request { settings.beginTransfer() }.error)
assertNull(request { settings.exportKey(fingerprint, false, "") }.error)
assertNull(request { settings.exportKey(fingerprint, true, passphrase) }.error)
assertTrue(settings.state.value.transfer.exportedKey!!.privateKey)
val privateFrames = settings.state.value.transfer.frames
assertTrue(privateFrames.isNotEmpty())
withContext(Dispatchers.Main.immediate) { authentication.presentation.clear() }
assertFalse(settings.state.value.transfer.open)
assertTrue(settings.state.value.transfer.frames.isEmpty())
assertTrue(privateFrames.all { frame -> frame.modules.all { it.toInt() == 0 } })
assertNull(request { settings.setDefaultKey(fingerprint) }.error)
assertFalse(authentication.state.value.unlocked)
assertEquals(fingerprint, settings.state.value.page!!.defaultKeyFingerprint)
@@ -2,6 +2,7 @@ package de.rfc1437.ironstorage.core
import de.rfc1437.ironstorage.bridge.NativeBridgeException
import de.rfc1437.ironstorage.bridge.generated.MobileAuthenticationFfiException
import de.rfc1437.ironstorage.bridge.generated.MobileCameraFrame
import de.rfc1437.ironstorage.bridge.generated.MobileKeyTransfer
import de.rfc1437.ironstorage.bridge.generated.MobileKeyTransferFfiException
import de.rfc1437.ironstorage.bridge.generated.MobileKeyTransferImport
@@ -10,7 +11,9 @@ import de.rfc1437.ironstorage.bridge.generated.MobileKeyTransferProgress
import de.rfc1437.ironstorage.bridge.generated.MobilePrivateStorageFfiException
import de.rfc1437.ironstorage.bridge.generated.MobileTotpOperation
import de.rfc1437.ironstorage.bridge.generated.mobileKeyTransfer
import de.rfc1437.ironstorage.bridge.generated.mobileLocalKeyTransferImporter
import de.rfc1437.ironstorage.bridge.generated.mobileTotpOperation
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -31,10 +34,12 @@ class NativeSettingsRepository(
private val mutable = MutableStateFlow(SettingsState())
override val state: StateFlow<SettingsState> = mutable
private val operation = Mutex()
private val cameraBusy = AtomicBoolean()
@Volatile private var epoch = 0L
@Volatile private var discovery: MobileTotpOperation? = null
private var visibility: Job? = null
private var transfer: MobileKeyTransfer? = null
private var importer: MobileKeyTransferImport? = null
init {
@@ -126,19 +131,27 @@ class NativeSettingsRepository(
)
}
}
override fun beginTransfer() = run { ticket ->
override fun beginTransfer() = begin(false)
override fun beginSetupTransfer() = begin(true)
private fun begin(setup: Boolean) = run { ticket ->
withContext(Dispatchers.IO) {
session.storage()
transfer = mobileKeyTransfer()
importer = requireNotNull(transfer).importer()
releaseTransfer()
if (setup) {
importer = mobileLocalKeyTransferImporter()
} else {
transfer = mobileKeyTransfer()
importer = requireNotNull(transfer).importer()
}
}
val keys = withContext(Dispatchers.IO) { requireNotNull(transfer).keys() }
val keys = withContext(Dispatchers.IO) { transfer?.keys().orEmpty() }
if (ticket ==
epoch
) {
mutable.value = mutable.value.copy(
transfer = KeyTransferState(
open = true,
setup = setup,
keys = keys.map {
TransferKey(
it.fingerprint,
@@ -152,6 +165,12 @@ class NativeSettingsRepository(
}
override fun exportKey(fingerprint: String, privateKey: Boolean, passphrase: String) =
run { ticket ->
visibility?.cancel()
mutable.value.transfer.frames.forEach { it.modules.fill(0) }
mutable.value =
mutable.value.copy(
transfer = mutable.value.transfer.copy(frames = emptyList(), exportedKey = null)
)
val result = withContext(Dispatchers.IO) {
requireNotNull(transfer).export(
fingerprint,
@@ -165,11 +184,28 @@ class NativeSettingsRepository(
mutable.value.transfer.frames.forEach { it.modules.fill(0) }
mutable.value = mutable.value.copy(
transfer = mutable.value.transfer.copy(
exportedKey = TransferKey(
result.key.fingerprint,
result.key.title,
result.key.kind == MobileKeyTransferKind.PRIVATE
),
frames = result.frames.map {
TransferFrame(it.sequence, it.total, it.width, it.modules)
}
)
)
visibility = scope.launch {
while (ticket == epoch) {
kotlinx.coroutines.delay(250)
val visible = operation.withLock {
withContext(Dispatchers.IO) { transfer?.exportVisible() == true }
}
if (ticket == epoch && !visible) {
closeTransfer()
break
}
}
}
} else {
result.frames.forEach { it.modules.fill(0) }
}
@@ -177,9 +213,11 @@ class NativeSettingsRepository(
private fun progress(ticket: Long, progress: MobileKeyTransferProgress) {
if (ticket != epoch) return
mutable.value = mutable.value.copy(
error = null,
transfer = mutable.value.transfer.copy(
received = progress.received,
total = progress.total,
duplicate = progress.duplicate,
ready = progress.key?.let {
TransferKey(
it.fingerprint,
@@ -190,6 +228,65 @@ class NativeSettingsRepository(
)
)
}
override fun cameraPreview(): CameraPreview? {
val ticket = epoch
return platform.cameraPreview({ frame ->
if (!cameraBusy.compareAndSet(false, true)) {
frame.pixels.fill(0)
} else {
scope.launch {
try {
operation.withLock {
if (ticket != epoch || !mutable.value.transfer.open ||
mutable.value.transfer.ready != null || mutable.value.busy
) {
return@withLock
}
val result = withContext(Dispatchers.IO) {
requireNotNull(importer).addCamera(
MobileCameraFrame(
frame.width,
frame.height,
frame.rowStride,
frame.pixelStride,
frame.pixels
),
frame.rotationDegrees
)
}
frame.pixels.fill(0)
if (result != null) progress(ticket, result)
}
} catch (error: MobileKeyTransferFfiException.Failed) {
if (ticket ==
epoch
) {
mutable.value = mutable.value.copy(error = error.rustMessage)
}
} catch (_: NativeBridgeException) {
if (ticket ==
epoch
) {
mutable.value =
mutable.value.copy(error = session.text(R.string.core_update_retry))
}
} finally {
frame.pixels.fill(0)
cameraBusy.set(false)
}
}
}
}, {
scope.launch {
if (ticket ==
epoch
) {
mutable.value =
mutable.value.copy(error = session.text(R.string.core_camera_unavailable))
}
}
})
}
override fun chooseQrImages() {
if (!platform.chooseQrImages()) {
mutable.value =
@@ -246,6 +343,15 @@ class NativeSettingsRepository(
progress(ticket, withContext(Dispatchers.IO) { requireNotNull(importer).addFrame(payload) })
}
override fun importKey(passphrase: String, makeDefault: Boolean, overwrite: Boolean) = run {
if (mutable.value.transfer.setup) {
withContext(Dispatchers.IO) {
session.storage().androidOnboarding(session.backend()).use {
it.importScanned(requireNotNull(importer), passphrase.ifEmpty { null })
}
}
closeTransfer()
return@run
}
val result = withContext(Dispatchers.IO) {
requireNotNull(importer).import(passphrase.ifEmpty { null }, makeDefault, overwrite)
}
@@ -264,6 +370,8 @@ class NativeSettingsRepository(
transfer = null
}
override fun closeTransfer() {
visibility?.cancel()
visibility = null
epoch += 1
mutable.value.transfer.frames.forEach { it.modules.fill(0) }
mutable.value = mutable.value.copy(transfer = KeyTransferState(), busy = false)
@@ -295,7 +403,11 @@ class NativeSettingsRepository(
mutable.value.copy(error = error.title + "\n" + error.detail)
}
} catch (error: MobileKeyTransferFfiException.Failed) {
if (ticket == epoch) mutable.value = mutable.value.copy(error = error.message)
if (ticket ==
epoch
) {
mutable.value = mutable.value.copy(error = error.rustMessage)
}
} catch (_: MobilePrivateStorageFfiException) {
if (ticket ==
epoch
@@ -12,6 +12,20 @@ interface SettingsPlatform {
fun openCredentials(): Boolean
fun chooseQrImages(): Boolean
suspend fun readQrImage(uri: String): ByteArray
fun cameraPreview(frame: (CameraFrame) -> Unit, failure: () -> Unit): CameraPreview?
}
data class CameraFrame(
val pixels: ByteArray,
val width: UInt,
val height: UInt,
val rowStride: UInt,
val pixelStride: UInt,
val rotationDegrees: UInt
) {
override fun toString() = "CameraFrame([REDACTED])"
}
interface CameraPreview : AutoCloseable {
val view: android.view.View
}
data class SettingsPage(
val repositoryTitle: String,
@@ -45,7 +59,10 @@ data class KeyTransferState(
val frames: List<TransferFrame> = emptyList(),
val received: UInt = 0u,
val total: UInt = 0u,
val ready: TransferKey? = null
val ready: TransferKey? = null,
val duplicate: Boolean = false,
val setup: Boolean = false,
val exportedKey: TransferKey? = null
) {
override fun toString() = "KeyTransferState([REDACTED])"
}
@@ -69,6 +86,8 @@ interface SettingsRepository {
fun openAutofill()
fun openCredentials()
fun beginTransfer()
fun beginSetupTransfer()
fun cameraPreview(): CameraPreview?
fun exportKey(fingerprint: String, privateKey: Boolean, passphrase: String)
fun chooseQrImages()
fun addQrImages(uris: List<String>)
@@ -38,4 +38,5 @@
<string name="core_copied">Copied. The clipboard will be cleared automatically.</string>
<string name="core_password_recovery">Unlock the device or recover the private store and refresh.</string>
<string name="core_password_storage">Private storage is unavailable. Unlock the device and refresh.</string>
<string name="core_camera_unavailable">The camera is unavailable. Pause scanning and retry, or choose QR images.</string>
</resources>
+25 -1
View File
@@ -176,7 +176,31 @@
"org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0": "Apache-2.0",
"org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0": "Apache-2.0",
"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0": "Apache-2.0",
"org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0": "Apache-2.0"
"org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0": "Apache-2.0",
"androidx.camera.featurecombinationquery:featurecombinationquery:1.6.2": "Apache-2.0",
"androidx.camera.viewfinder:viewfinder-core:1.5.1": "Apache-2.0",
"androidx.camera:camera-camera2-pipe:1.6.2": "Apache-2.0",
"androidx.camera:camera-camera2:1.6.2": "Apache-2.0",
"androidx.camera:camera-core:1.6.2": "Apache-2.0 AND BSD-3-Clause",
"androidx.camera:camera-lifecycle:1.6.2": "Apache-2.0",
"androidx.camera:camera-video:1.6.2": "Apache-2.0",
"androidx.camera:camera-view:1.6.2": "Apache-2.0",
"androidx.concurrent:concurrent-futures-ktx:1.1.0": "Apache-2.0",
"androidx.core:core-backported-fixes:1.0.0": "Apache-2.0",
"androidx.exifinterface:exifinterface:1.4.2": "Apache-2.0",
"androidx.media3:media3-common:1.9.0": "Apache-2.0",
"androidx.media3:media3-container:1.9.0": "Apache-2.0",
"androidx.media3:media3-muxer:1.9.0": "Apache-2.0",
"androidx.tracing:tracing-ktx:2.0.0": "Apache-2.0",
"com.google.auto.value:auto-value-annotations:1.6.3": "Apache-2.0",
"com.google.dagger:dagger:2.59": "Apache-2.0",
"com.google.errorprone:error_prone_annotations:2.28.0": "Apache-2.0",
"com.google.guava:failureaccess:1.0.2": "Apache-2.0",
"com.google.guava:guava:33.3.1-android": "Apache-2.0",
"com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava": "Apache-2.0",
"com.google.j2objc:j2objc-annotations:3.0.0": "Apache-2.0",
"org.checkerframework:checker-qual:3.43.0": "MIT",
"org.jetbrains.kotlinx:atomicfu-jvm:0.28.0": "Apache-2.0"
},
"rust_licenses": [
"(Apache-2.0 OR MIT) AND BSD-3-Clause",
@@ -19,6 +19,7 @@ import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
@@ -54,9 +55,17 @@ class SetupFormViewModel @Inject constructor() : ViewModel() {
fun OnboardingScreen(
repository: OnboardingRepository,
secrets: SetupFormViewModel,
settings: de.rfc1437.ironstorage.core.SettingsRepository,
authentication: de.rfc1437.ironstorage.core.AuthenticationRepository,
modifier: Modifier = Modifier,
scroll: Boolean = true
) {
val transfer by settings.state.collectAsStateWithLifecycle()
DisposableEffect(transfer.transfer.open) {
val wasOpen = transfer.transfer.open
onDispose { if (wasOpen) repository.refresh() }
}
if (transfer.transfer.open) KeyTransferDialog(settings, authentication)
ProtectedInputSurface()
val state by repository.setup.collectAsStateWithLifecycle()
var step by rememberSaveable { mutableStateOf("choice") }
@@ -183,6 +192,9 @@ fun OnboardingScreen(
}
"import" -> {
Text(stringResource(R.string.setup_import_help))
TextButton(onClick = settings::beginSetupTransfer) {
Text(stringResource(R.string.qr_scan))
}
SecretField(
stringResource(R.string.setup_armor),
secrets.armor,
@@ -0,0 +1,95 @@
package de.rfc1437.ironstorage.features
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.provider.Settings
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
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.draw.clipToBounds
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LifecycleEventEffect
import de.rfc1437.ironstorage.core.SettingsRepository
@Composable
internal fun QrCameraPane(repository: SettingsRepository) {
val context = LocalContext.current
fun allowed() = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) ==
PackageManager.PERMISSION_GRANTED
var permitted by remember { mutableStateOf(allowed()) }
var scanning by remember { mutableStateOf(false) }
val permission =
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) {
permitted = it
scanning = it
}
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { permitted = allowed() }
if (permitted && scanning) {
val camera = remember(repository) { repository.cameraPreview() }
DisposableEffect(camera) { onDispose { camera?.close() } }
if (camera == null) {
Text(stringResource(R.string.qr_camera_missing))
} else {
AndroidView(factory = {
camera.view
}, modifier = Modifier.fillMaxWidth().height(280.dp).clipToBounds())
}
TextButton(onClick = { scanning = false }) { Text(stringResource(R.string.qr_pause)) }
} else {
TextButton(onClick = {
if (permitted) {
scanning = true
} else {
permission.launch(Manifest.permission.CAMERA)
}
}) { Text(stringResource(if (permitted) R.string.qr_resume else R.string.qr_scan)) }
if (!permitted) {
Text(stringResource(R.string.qr_camera_denied))
TextButton(onClick = {
context.startActivity(
Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
"package:${context.packageName}".toUri()
)
)
}) { Text(stringResource(R.string.qr_camera_settings)) }
}
}
}
@Composable
internal fun QrDisplayWindow() {
val view = androidx.compose.ui.platform.LocalView.current
val window = (view.parent as? androidx.compose.ui.window.DialogWindowProvider)?.window
DisposableEffect(window, view) {
val brightness = window?.attributes?.screenBrightness
val keep = view.keepScreenOn
view.keepScreenOn = true
window?.let { it.attributes = it.attributes.apply { screenBrightness = 1f } }
onDispose {
view.keepScreenOn = keep
if (brightness !=
null
) {
window.attributes = window.attributes.apply { screenBrightness = brightness }
}
}
}
}
@@ -169,6 +169,8 @@ fun SettingsScreen(
OnboardingScreen(
onboarding,
setup,
repository,
authentication,
scroll = false
)
}
@@ -290,7 +292,7 @@ fun SettingsScreen(
SettingsEditDialog(repository, authentication, action) { edit = null }
}
}
if (state.transfer.open) KeyTransferDialog(repository, authentication)
if (state.transfer.open && !showSetup) KeyTransferDialog(repository, authentication)
if (licenses) {
val resources = LocalResources.current
val loading = stringResource(R.string.settings_license_loading)
@@ -422,7 +424,7 @@ private fun SettingsEditDialog(
}
@Composable
private fun KeyTransferDialog(
internal fun KeyTransferDialog(
repository: SettingsRepository,
authentication: AuthenticationRepository
) {
@@ -436,6 +438,15 @@ private fun KeyTransferDialog(
var frame by remember { mutableStateOf(0) }
var passphrase by rememberProtectedInput(authentication.presentation)
var payload by rememberProtectedInput(authentication.presentation)
var exportConfirmed by remember(fingerprint, privateKey) { mutableStateOf(false) }
var importConfirmed by remember(state.transfer.ready) { mutableStateOf(false) }
var animate by remember { mutableStateOf(false) }
LaunchedEffect(animate, state.transfer.frames) {
while (animate && state.transfer.frames.size > 1) {
kotlinx.coroutines.delay(1200)
frame = (frame + 1) % state.transfer.frames.size
}
}
ProtectedWindow()
AlertDialog(
properties = DialogProperties(
@@ -454,91 +465,126 @@ private fun KeyTransferDialog(
ProtectedInputSurface(authentication.presentation)
state.error?.let { Text(it, color = MaterialTheme.colorScheme.error) }
if (state.busy) CircularProgressIndicator()
state.transfer.keys.forEach { key ->
Row(
Modifier.fillMaxWidth().selectable(
selected = fingerprint == key.fingerprint,
enabled = !state.busy,
role = Role.RadioButton,
onClick = { fingerprint = key.fingerprint }
)
) {
RadioButton(selected = fingerprint == key.fingerprint, onClick = null)
Column(Modifier.weight(1f)) {
Text(key.title)
Text(key.fingerprint)
}
}
}
TextButton(
enabled =
!state.busy &&
state.transfer.keys.any {
it.fingerprint == fingerprint && it.privateKey
},
onClick = { repository.setDefaultKey(fingerprint) }
) { Text(stringResource(R.string.settings_default_key_select)) }
Text(stringResource(R.string.settings_private_warning))
SettingsToggle(stringResource(R.string.settings_private_export), privateKey) {
privateKey =
it
}
SecretField(stringResource(R.string.setup_passphrase), passphrase) {
passphrase = it
}
TextButton(enabled = !state.busy && fingerprint.isNotEmpty(), onClick = {
repository.exportKey(fingerprint, privateKey, passphrase)
passphrase = ""
frame =
0
}) { Text(stringResource(R.string.settings_export_confirm)) }
state.transfer.frames.getOrNull(frame)?.let { qr ->
val description =
stringResource(
R.string.settings_qr_frame,
qr.sequence.toInt(),
qr.total.toInt()
)
Text(description)
Canvas(
Modifier.fillMaxWidth().height(280.dp).semantics {
contentDescription =
description
}
) {
drawRect(Color.White)
val width = qr.width.toInt()
val scale = minOf(size.width, size.height) / (width + 8)
val x = (size.width - scale * width) / 2
val y = (size.height - scale * width) / 2
for (row in 0 until width) {
for (column in 0 until width) {
if (qr.modules[
row *
width +
column
].toInt() ==
1
) {
drawRect(
Color.Black,
Offset(x + column * scale, y + row * scale),
Size(scale, scale)
)
}
if (!state.transfer.setup) {
state.transfer.keys.forEach { key ->
Row(
Modifier.fillMaxWidth().selectable(
selected = fingerprint == key.fingerprint,
enabled = !state.busy,
role = Role.RadioButton,
onClick = { fingerprint = key.fingerprint }
)
) {
RadioButton(selected = fingerprint == key.fingerprint, onClick = null)
Column(Modifier.weight(1f)) {
Text(key.title)
Text(key.fingerprint)
}
}
}
FlowRow {
TextButton(enabled = frame > 0, onClick = {
frame -= 1
}) { Text(stringResource(R.string.settings_previous)) }
TextButton(enabled = frame < state.transfer.frames.lastIndex, onClick = {
frame +=
1
}) { Text(stringResource(R.string.settings_next)) }
TextButton(
enabled =
!state.busy &&
state.transfer.keys.any {
it.fingerprint == fingerprint && it.privateKey
},
onClick = { repository.setDefaultKey(fingerprint) }
) { Text(stringResource(R.string.settings_default_key_select)) }
Text(stringResource(R.string.settings_private_warning))
SettingsToggle(stringResource(R.string.settings_private_export), privateKey) {
privateKey =
it
}
SecretField(stringResource(R.string.setup_passphrase), passphrase) {
passphrase = it
}
if (privateKey) {
SettingsToggle(
stringResource(R.string.qr_export_private_confirm),
exportConfirmed
) {
exportConfirmed =
it
}
}
TextButton(
enabled =
!state.busy && fingerprint.isNotEmpty() && (!privateKey || exportConfirmed),
onClick = {
repository.exportKey(fingerprint, privateKey, passphrase)
passphrase = ""
frame =
0
}
) { Text(stringResource(R.string.settings_export_confirm)) }
state.transfer.frames.getOrNull(frame)?.let { qr ->
state.transfer.exportedKey?.let { exported ->
Text(exported.title)
Text(exported.fingerprint)
}
QrDisplayWindow()
val description =
stringResource(
R.string.settings_qr_frame,
qr.sequence.toInt(),
qr.total.toInt()
)
Text(description)
Canvas(
Modifier.fillMaxWidth().height(280.dp).semantics {
contentDescription =
description
}
) {
drawRect(Color.White)
val width = qr.width.toInt()
val scale = minOf(size.width, size.height) / (width + 8)
val x = (size.width - scale * width) / 2
val y = (size.height - scale * width) / 2
for (row in 0 until width) {
for (column in 0 until width) {
if (qr.modules[
row *
width +
column
].toInt() ==
1
) {
drawRect(
Color.Black,
Offset(x + column * scale, y + row * scale),
Size(scale, scale)
)
}
}
}
}
FlowRow {
TextButton(enabled = frame > 0, onClick = {
frame -= 1
}) { Text(stringResource(R.string.settings_previous)) }
TextButton(
enabled = frame < state.transfer.frames.lastIndex,
onClick = {
frame +=
1
}
) { Text(stringResource(R.string.settings_next)) }
}
}
if (state.transfer.frames.size > 1) {
SettingsToggle(stringResource(R.string.qr_animate), animate) {
animate = it
}
}
}
if (state.transfer.setup) {
SecretField(stringResource(R.string.setup_passphrase), passphrase) {
passphrase =
it
}
}
if (state.transfer.ready == null && !state.busy) QrCameraPane(repository)
TextButton(enabled = !state.busy, onClick = repository::chooseQrImages) {
Text(stringResource(R.string.settings_qr_images))
}
@@ -564,22 +610,50 @@ private fun KeyTransferDialog(
state.transfer.total.toInt()
)
)
if (state.transfer.duplicate) Text(stringResource(R.string.qr_duplicate))
state.transfer.ready?.let {
Text(it.title)
Text(it.fingerprint)
SettingsToggle(stringResource(R.string.settings_make_default), makeDefault) {
makeDefault =
it
if (!state.transfer.setup) {
SettingsToggle(
stringResource(R.string.settings_make_default),
makeDefault
) {
makeDefault =
it
}
SettingsToggle(stringResource(R.string.settings_replace_key), overwrite) {
overwrite =
it
}
}
SettingsToggle(stringResource(R.string.settings_replace_key), overwrite) {
overwrite =
it
if (it.privateKey) {
SettingsToggle(
stringResource(R.string.qr_import_private_confirm),
importConfirmed
) {
importConfirmed =
it
}
}
TextButton(
enabled = !state.busy && (!it.privateKey || importConfirmed),
onClick = {
repository.importKey(passphrase, makeDefault, overwrite)
passphrase =
""
}
) {
Text(
stringResource(
if (state.transfer.setup) {
R.string.qr_setup_import
} else {
R.string.settings_import_confirm
}
)
)
}
TextButton(enabled = !state.busy, onClick = {
repository.importKey(passphrase, makeDefault, overwrite)
passphrase =
""
}) { Text(stringResource(R.string.settings_import_confirm)) }
}
}
},
@@ -171,7 +171,13 @@ private fun ShellDestination(
Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
OnboardingScreen(viewModel.onboarding, setupForm, scroll = false)
OnboardingScreen(
viewModel.onboarding,
setupForm,
viewModel.settings,
viewModel.authentication,
scroll = false
)
}
}
ready && route == ShellRoute.Passwords -> {
@@ -224,4 +224,15 @@
<item quantity="other">%1$d results</item>
</plurals>
<string name="settings_license_loading">Loading license…</string>
<string name="qr_scan">Scan GPG QR codes</string>
<string name="qr_pause">Pause camera</string>
<string name="qr_resume">Resume camera</string>
<string name="qr_camera_denied">Camera access is required to scan. If permission was denied, allow it in app settings or choose QR images.</string>
<string name="qr_camera_settings">Open camera permission settings</string>
<string name="qr_camera_missing">The camera is unavailable. Choose QR images or paste an armored key.</string>
<string name="qr_duplicate">Duplicate frame; progress is unchanged.</string>
<string name="qr_import_private_confirm">I checked this fingerprint and authorize importing its private key material.</string>
<string name="qr_export_private_confirm">I checked this fingerprint and authorize displaying its private QR codes.</string>
<string name="qr_animate">Automatically advance frames</string>
<string name="qr_setup_import">Prepare the scanned key</string>
</resources>
+4
View File
@@ -1,4 +1,5 @@
[versions]
camera = "1.6.2"
agp = "9.4.1"
kotlin = "2.4.21"
ksp = "2.3.12"
@@ -28,6 +29,9 @@ biometric = "1.1.0"
fragment = "1.9.1"
[libraries]
camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camera" }
camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camera" }
camera-view = { module = "androidx.camera:camera-view", version.ref = "camera" }
benchmark-macro = { module = "androidx.benchmark:benchmark-macro-junit4", version.ref = "benchmark" }
rustls-verifier = { group = "rustls", name = "rustls-platform-verifier", version.ref = "rustlsVerifier" }
compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose" }
+260
View File
@@ -183,6 +183,11 @@
<sha256 value="03fb659177c8618e47425925c11bc91f384703ea26c265be9b25cd79292b511c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.appcompat" name="appcompat" version="1.1.0">
<artifact name="appcompat-1.1.0.pom">
<sha256 value="340d617121f8ef8e02a6680c8f357aa3e542276d0c8a1cdcb6fd98984b2cb7b9" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.appcompat" name="appcompat" version="1.2.0">
<artifact name="appcompat-1.2.0.aar">
<sha256 value="3d2131a55a61a777322e2126e0018011efa6339e53b44153eb651b16020cca70" origin="Generated by Gradle"/>
@@ -284,6 +289,70 @@
<sha256 value="4999f205b2b38e31180ad58111e32d564190c2024fbfd58527e2da8bfb67ff65" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.camera" name="camera-camera2" version="1.6.2">
<artifact name="camera-camera2-1.6.2.aar">
<sha256 value="5a55014ed96feda81a37799faf9f96150025a4434699d55caa1cdebf2ae07e20" origin="Generated by Gradle"/>
</artifact>
<artifact name="camera-camera2-1.6.2.module">
<sha256 value="4694b995fbb4934e5e1e3cedabef426e799a510cfc179b659c4e9632f6b2fe75" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.camera" name="camera-camera2-pipe" version="1.6.2">
<artifact name="camera-camera2-pipe-1.6.2.aar">
<sha256 value="b357bd94fd4273ff88f453f7e712a22e100adc932324fc6b51fbe9ef9cf45515" origin="Generated by Gradle"/>
</artifact>
<artifact name="camera-camera2-pipe-1.6.2.module">
<sha256 value="5470741b0a6fe88e560e7d929097e2ec60e7406aef09b48ab900b0befa102c0f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.camera" name="camera-core" version="1.6.2">
<artifact name="camera-core-1.6.2.aar">
<sha256 value="ebd66f090414f4c12f23a55b5023aeee1b096da8a06c02a0b6fb3520c4ddadf6" origin="Generated by Gradle"/>
</artifact>
<artifact name="camera-core-1.6.2.module">
<sha256 value="bee75b4ef561862030702a51f692057e034ae1f600f3091b2a5e5a89856ab410" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.camera" name="camera-lifecycle" version="1.6.2">
<artifact name="camera-lifecycle-1.6.2.aar">
<sha256 value="b7b701a3c6838bc7c4acd1372140e889fdf9681bf833ebfc638fdbe28ed5fa32" origin="Generated by Gradle"/>
</artifact>
<artifact name="camera-lifecycle-1.6.2.module">
<sha256 value="bd7fd00cb9de0f2ce8c3400b52504cf7691590bb29a1550a71b58255e483ff47" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.camera" name="camera-video" version="1.6.2">
<artifact name="camera-video-1.6.2.aar">
<sha256 value="dee568da7d1ce009506a538b2916cce6f23473a835b0aeb4e8193483ff1174d0" origin="Generated by Gradle"/>
</artifact>
<artifact name="camera-video-1.6.2.module">
<sha256 value="cf1e3fb9362af9c020f1126f8c56e7578f73a96b6f670136cc756a907182733c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.camera" name="camera-view" version="1.6.2">
<artifact name="camera-view-1.6.2.aar">
<sha256 value="2f258b88422b925717d2d1d1714d5572e48e2194336be227b35fc60000f6e166" origin="Generated by Gradle"/>
</artifact>
<artifact name="camera-view-1.6.2.module">
<sha256 value="c84dc907bff8a46133631d1e93edcef78ac608c83abd07b51056f9cca4ae800c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.camera.featurecombinationquery" name="featurecombinationquery" version="1.6.2">
<artifact name="featurecombinationquery-1.6.2.aar">
<sha256 value="2321cfd29678e3128284d1622bb6b06e0fecd2c2e69140471ab00e8616e622c2" origin="Generated by Gradle"/>
</artifact>
<artifact name="featurecombinationquery-1.6.2.module">
<sha256 value="4f6edda1492eaa928cfe7516d8efd097ed2f59ee4cedf1bec2d3345a3d6137fd" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.camera.viewfinder" name="viewfinder-core" version="1.5.1">
<artifact name="viewfinder-core-1.5.1.aar">
<sha256 value="31179ed5509e4d6d8d667efb91a039cac897b2bfd800d4c6e0b5bed376b4fa69" origin="Generated by Gradle"/>
</artifact>
<artifact name="viewfinder-core-1.5.1.module">
<sha256 value="31407c1af39b26a59bebdcd61a39b7aa1a8b7f51c63aa61127d4f977a36d877c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.cardview" name="cardview" version="1.0.0">
<artifact name="cardview-1.0.0.aar">
<sha256 value="1193c04c22a3d6b5946dae9f4e8c59d6adde6a71b6bd5d87fb99d82dda1afec7" origin="Generated by Gradle"/>
@@ -848,6 +917,14 @@
<sha256 value="afd7094d3a07422be2e991bc87048542ae9724a5c42bd54b5cae3029c7970a1d" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.concurrent" name="concurrent-futures-ktx" version="1.1.0">
<artifact name="concurrent-futures-ktx-1.1.0.jar">
<sha256 value="1968bf52039e38636aa6f114cd17d7256919d1e8997417716fef9d1da1f24d85" origin="Generated by Gradle"/>
</artifact>
<artifact name="concurrent-futures-ktx-1.1.0.module">
<sha256 value="69b79724566d49140846700690b8d2165231c577e93e66726a443e8f976bbe19" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.concurrent" name="concurrent-futures-ktx" version="1.2.0">
<artifact name="concurrent-futures-ktx-1.2.0.jar">
<sha256 value="e1f3e17bb4358ccd6c77ca45f70635c9aba237261f19eaa4f64a0218c00e2a3e" origin="Generated by Gradle"/>
@@ -903,6 +980,11 @@
<sha256 value="afb5ea494dd083ed404cd51f580d218e37362f8ae326e893bee521290ed34920" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.core" name="core" version="1.7.0">
<artifact name="core-1.7.0.module">
<sha256 value="988f820899d5a4982e5c878ca1cd417970ace332ea2ff72f5be19b233fa0e788" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.core" name="core" version="1.8.0">
<artifact name="core-1.8.0.module">
<sha256 value="505f1838869611519d65ec7feb650e88856e3682e37e42a2a24b73e655a98d74" origin="Generated by Gradle"/>
@@ -916,6 +998,14 @@
<sha256 value="8674ade1d12962469dd2d1e6edc03b465f20d180b0840a129bc40b351e8651d1" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.core" name="core-backported-fixes" version="1.0.0">
<artifact name="core-backported-fixes-1.0.0.aar">
<sha256 value="874722634db5f60745ab2f64d734dd81d3ee4eb71f175263e6565dabea54713d" origin="Generated by Gradle"/>
</artifact>
<artifact name="core-backported-fixes-1.0.0.module">
<sha256 value="05aef3e2e15168190894999d4b14907c84d066837964ec900f7a2f0e4d9354ac" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.core" name="core-ktx" version="1.1.0">
<artifact name="core-ktx-1.1.0.pom">
<sha256 value="729d11bad16d87c6212dc9ab80e9c770d333115062136e0732164a6b1696e11c" origin="Generated by Gradle"/>
@@ -1042,6 +1132,14 @@
<sha256 value="92be16cccb757f807bddd92f1fca95a038a810ccca051c4149c11682478954b8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.exifinterface" name="exifinterface" version="1.4.2">
<artifact name="exifinterface-1.4.2.aar">
<sha256 value="6bb2b68682a2023c55f35f941444ffea0157947b061e4fd7606ba935e2e77425" origin="Generated by Gradle"/>
</artifact>
<artifact name="exifinterface-1.4.2.module">
<sha256 value="6365165eb5aacc700a34b1650a2910ea01ebd819821b89af7689fa41274fb00b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.fragment" name="fragment" version="1.2.5">
<artifact name="fragment-1.2.5.pom">
<sha256 value="df0aca46b62bb47cc662cbcee63372db6d2a2859478ee38b594fba3433fe30a5" origin="Generated by Gradle"/>
@@ -1100,6 +1198,11 @@
<sha256 value="2baaf7f17551765524bd1e4d2f954a2a0178a112039cf549ba8b7d4b41e66931" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-common" version="2.1.0">
<artifact name="lifecycle-common-2.1.0.pom">
<sha256 value="692c2a60437a95220c8fbb6e8943196f255f535d1a7c58714e6c46530782f276" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-common" version="2.10.0">
<artifact name="lifecycle-common-2.10.0.module">
<sha256 value="5d1247b1edfbdc9ddedcbe525c9d2629567b1c538cb7a9c620cd044a124b5c73" origin="Generated by Gradle"/>
@@ -1170,6 +1273,11 @@
<sha256 value="1d1837f3942b33ea30aa2301fdcea2639408a0fd6fd4368c22478fa8153aebbf" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-livedata" version="2.1.0">
<artifact name="lifecycle-livedata-2.1.0.pom">
<sha256 value="1e4c580c995e41cd18455cfb87e3e43955be9db9ec63a8fb3c7259f01b7c4e3c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-livedata" version="2.10.0">
<artifact name="lifecycle-livedata-2.10.0.aar">
<sha256 value="3e8c009fc88da1c52e4ed05a81269d5ac9bd405046f300307f5e3a17e201a6df" origin="Generated by Gradle"/>
@@ -1186,6 +1294,11 @@
<sha256 value="d990bc920ae43729d702d80077328e8a59781ea5a35b24c02bc47d326ae73d7d" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-livedata" version="2.3.1">
<artifact name="lifecycle-livedata-2.3.1.module">
<sha256 value="93494e78d5049dff897fde50f477e7b965e9a3de4a1ed453b5f0cbc409eb4e4c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-livedata" version="2.6.1">
<artifact name="lifecycle-livedata-2.6.1.aar">
<sha256 value="67359f609dfc2bf65da1270b23033f856064ec279f058e0a70c715f7c9003031" origin="Generated by Gradle"/>
@@ -1194,6 +1307,11 @@
<sha256 value="e2659e87e890b52b66f300471828b387a205e5387a392713190166f399ecf641" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-livedata-core" version="2.1.0">
<artifact name="lifecycle-livedata-core-2.1.0.pom">
<sha256 value="6839d2e68c8fee4cc3bbf04e6b291a6ae842403fcd5f8d944c27814d296deff3" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-livedata-core" version="2.10.0">
<artifact name="lifecycle-livedata-core-2.10.0.aar">
<sha256 value="12dd61a9843ccebb4547d3ebe2f6d030ca8f6988d22f8fad19c0af93b4a97e95" origin="Generated by Gradle"/>
@@ -1215,6 +1333,11 @@
<sha256 value="aa51621be3817d1f5d21336b0c0525fe96ae899cec656c13d8ea18a51ea32c2f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-livedata-core" version="2.3.1">
<artifact name="lifecycle-livedata-core-2.3.1.module">
<sha256 value="b1e095d550d39a7d6c815761d6dbe3fd64eb31a39dc28146e78bbf2c01ba8f41" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-livedata-core" version="2.6.1">
<artifact name="lifecycle-livedata-core-2.6.1.aar">
<sha256 value="2256780a3cff4a1e57fbb3d442557c17dc363ab8af105bcaf5261d8e2d5db949" origin="Generated by Gradle"/>
@@ -1563,6 +1686,30 @@
<sha256 value="c978d550808b47434aa49a63164110a50b55b0bcc6160a93a2e37d5110df8c5e" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.media3" name="media3-common" version="1.9.0">
<artifact name="media3-common-1.9.0.aar">
<sha256 value="714114bf8a56fe44f0f78b499ae7ea3bed52391313794c8abdc1b687700d42aa" origin="Generated by Gradle"/>
</artifact>
<artifact name="media3-common-1.9.0.module">
<sha256 value="02d42f8fd7cc53c385256f417df73a5cf30e8b7c5635ca5786148518ce711b75" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.media3" name="media3-container" version="1.9.0">
<artifact name="media3-container-1.9.0.aar">
<sha256 value="f73cea4fe8a2bf9eb6462bfb7a547d1aaf55395d0007eed6fadb129b56927c0d" origin="Generated by Gradle"/>
</artifact>
<artifact name="media3-container-1.9.0.module">
<sha256 value="5a5f4d796aa34a3480ea75df73892d68eba7cd4bc239c116eee3816ce973d469" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.media3" name="media3-muxer" version="1.9.0">
<artifact name="media3-muxer-1.9.0.aar">
<sha256 value="452b182d84cfe20d1154d999107b9aac7c218ad8ee208d35d067916a92a02031" origin="Generated by Gradle"/>
</artifact>
<artifact name="media3-muxer-1.9.0.module">
<sha256 value="e22678eed7457db37ceb4f14d96c761fa5055a114b4750892cf42b820a791428" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.navigation3" name="navigation3-runtime" version="1.2.0">
<artifact name="navigation3-runtime-1.2.0.module">
<sha256 value="ff14442bf489768bbd2126536921a4d007bdf2d117b2c0451be67cd58eb66a21" origin="Generated by Gradle"/>
@@ -1989,6 +2136,19 @@
<sha256 value="6ec8aa998df9b964424c7ab215738237a9b2cc4301da135bb9b8cdc62325fb3e" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing-ktx" version="1.2.0">
<artifact name="tracing-ktx-1.2.0.module">
<sha256 value="39288b23c71a4391132aec9951f5e27f41727a2cdff69c6e4b4ece49f63e540c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing-ktx" version="2.0.0">
<artifact name="tracing-ktx-2.0.0.aar">
<sha256 value="7e7cf1386841b11310138723eb9f36b5eaf6c8caec896b16f99e579d220d6a0c" origin="Generated by Gradle"/>
</artifact>
<artifact name="tracing-ktx-2.0.0.module">
<sha256 value="f30609942614488befb6edaa36aa4d3b4fb1c1b7b79780630d3ca3a02b08c0a5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing-perfetto" version="1.0.1">
<artifact name="tracing-perfetto-1.0.1.aar">
<sha256 value="5387298b5bdd8f7d98315fe8720a757056ba0b9b5d9544d2e3fabc26b9ac55a6" origin="Generated by Gradle"/>
@@ -2551,6 +2711,11 @@
<sha256 value="05f740c6648165db00cf618dd56c200c4725e358e6d54f5853e0bec15734ea0a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.auto" name="auto-parent" version="7">
<artifact name="auto-parent-7.pom">
<sha256 value="a46426fccb5d32705ad9cbbc996f786bd048cc8cbdd21db046500169f15a4356" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.auto.value" name="auto-value-annotations" version="1.6.2">
<artifact name="auto-value-annotations-1.6.2.jar">
<sha256 value="b48b04ddba40e8ac33bf036f06fc43995fc5084bd94bdaace807ce27d3bea3fb" origin="Generated by Gradle"/>
@@ -2559,11 +2724,24 @@
<sha256 value="1c76cd462fc96e7aa96dc70ce82f0d54063d6df16db35c9c7d9cc0d1a99d3fff" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.auto.value" name="auto-value-annotations" version="1.6.3">
<artifact name="auto-value-annotations-1.6.3.jar">
<sha256 value="0e951fee8c31f60270bc46553a8586001b7b93dbb12aec06373aa99a150392c0" origin="Generated by Gradle"/>
</artifact>
<artifact name="auto-value-annotations-1.6.3.pom">
<sha256 value="e1fc780f7ee025e662b3da72723dbe2ac8dac0a2f8920f265315c4e1be3d765c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.auto.value" name="auto-value-parent" version="1.6.2">
<artifact name="auto-value-parent-1.6.2.pom">
<sha256 value="27b640c82179f5cff62009c0b72033d9bc60f60e9902a66802274b7fe37fc81c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.auto.value" name="auto-value-parent" version="1.6.3">
<artifact name="auto-value-parent-1.6.3.pom">
<sha256 value="e59df5732b4cb34d5727181446f9ded9ce4425131a0a1062519eea196de4d8a1" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.code.findbugs" name="jsr305" version="3.0.2">
<artifact name="jsr305-3.0.2.jar">
<sha256 value="766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" origin="Generated by Gradle"/>
@@ -2601,6 +2779,14 @@
<sha256 value="265ba959a8e13c3a06133f04b539169c1018daffd4d33f53c453ab4cb386f570" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.dagger" name="dagger" version="2.59">
<artifact name="dagger-2.59.jar">
<sha256 value="94d5b23812cc12fa903c9ac4a42978e7e7b2622e6b51c17f8b12037ceece43cd" origin="Generated by Gradle"/>
</artifact>
<artifact name="dagger-2.59.pom">
<sha256 value="0e47846618c7a1b7a8124fd20623a667643d73cbe0a561bb87056151aa967538" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.dagger" name="dagger" version="2.60.1">
<artifact name="dagger-2.60.1.jar">
<sha256 value="b012d0df527b51bfcd8953d5fcb4d022a45ebe66010d6f181ede68d1572b5cb2" origin="Generated by Gradle"/>
@@ -2716,6 +2902,9 @@
</artifact>
</component>
<component group="com.google.errorprone" name="error_prone_annotations" version="2.18.0">
<artifact name="error_prone_annotations-2.18.0.jar">
<sha256 value="9e6814cb71816988a4fd1b07a993a8f21bb7058d522c162b1de849e19bea54ae" origin="Generated by Gradle"/>
</artifact>
<artifact name="error_prone_annotations-2.18.0.pom">
<sha256 value="920135797dcca5917b5a5c017642a58d340a4cd1bcd12f56f892a5663bd7bddc" origin="Generated by Gradle"/>
</artifact>
@@ -2725,6 +2914,14 @@
<sha256 value="4ca5a35d61235e16549ac346d1e34551cdf0fe27e84aa57e03cbeb255ea4e5da" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.errorprone" name="error_prone_annotations" version="2.28.0">
<artifact name="error_prone_annotations-2.28.0.jar">
<sha256 value="f3fc8a3a0a4020706a373b00e7f57c2512dd26d1f83d28c7d38768f8682b231e" origin="Generated by Gradle"/>
</artifact>
<artifact name="error_prone_annotations-2.28.0.pom">
<sha256 value="0ce909f13a568148476e5ee200f380f98c75ba08971aaf15db295eb77598ef3a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.errorprone" name="error_prone_annotations" version="2.3.1">
<artifact name="error_prone_annotations-2.3.1.pom">
<sha256 value="3edce6b711ba368efe16b9b7aacb0214fbd648414cb9b965953a2e7ed89a819a" origin="Generated by Gradle"/>
@@ -2764,6 +2961,11 @@
<sha256 value="fa81829d049559df6927f9c9a6fd6bbd09f8b50e6d4736ae72c8300b6c3d7654" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.errorprone" name="error_prone_parent" version="2.28.0">
<artifact name="error_prone_parent-2.28.0.pom">
<sha256 value="accefdbb5416cef5fcd2ddc37db4f1fcb34a2193cc1a55dfe5970a131b3e7683" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.errorprone" name="error_prone_parent" version="2.3.1">
<artifact name="error_prone_parent-2.3.1.pom">
<sha256 value="767525d9a81129cd081968382980336327be4162b1e2251a182911daa733c123" origin="Generated by Gradle"/>
@@ -2837,6 +3039,25 @@
<sha256 value="13d4e2998aac652fc6d7b9eaf3e495fa2952bf98ef68a2c49aa5eaa0bbe731a9" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava" version="32.0.1-android">
<artifact name="guava-32.0.1-android.jar">
<sha256 value="12429ff9ac33b7afd8b1a5071179757cf4d3c47f0f4099bc0384e644ecbf82dd" origin="Generated by Gradle"/>
</artifact>
<artifact name="guava-32.0.1-android.pom">
<sha256 value="cf26ce63461fb585cad25dc6932765883122643c3beec5e3269f667c73295ac4" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava" version="33.3.1-android">
<artifact name="guava-33.3.1-android.jar">
<sha256 value="2c3e41d1b380f2044d257947a3aa82dabf3ae4b978622745254aa18b6cf89ab0" origin="Generated by Gradle"/>
</artifact>
<artifact name="guava-33.3.1-android.module">
<sha256 value="6971614ddeee003e54d2b69c6b0c94ce8a5b5eba980d16455c81df9089a7acb7" origin="Generated by Gradle"/>
</artifact>
<artifact name="guava-33.3.1-jre.jar">
<sha256 value="4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava" version="33.4.0-jre">
<artifact name="guava-33.4.0-jre.jar">
<sha256 value="b918c98a7e44dbe94ebd9fe3e40cddaadb5a93e6a78eb6008b42df237241e538" origin="Generated by Gradle"/>
@@ -2863,6 +3084,16 @@
<sha256 value="30a51d34b075c9b4eb6a2da3fba6c1f5047b45225a3a03aebcefc0b7880e5735" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava-parent" version="32.0.1-android">
<artifact name="guava-parent-32.0.1-android.pom">
<sha256 value="cddb3a941d71be5e871f39a239ba3bb8883c88c7763d8a3d2d342b09a1d5da30" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava-parent" version="33.3.1-android">
<artifact name="guava-parent-33.3.1-android.pom">
<sha256 value="6e11986ea7250b51f847157e2dc937f32a306804dfce0007a5e81ddb9b95c579" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava-parent" version="33.4.0-android">
<artifact name="guava-parent-33.4.0-android.pom">
<sha256 value="7220ede61026596fbc720ee4b93246fa2d14f328058532b59ef053de397c7d83" origin="Generated by Gradle"/>
@@ -2903,6 +3134,9 @@
</artifact>
</component>
<component group="com.google.j2objc" name="j2objc-annotations" version="2.8">
<artifact name="j2objc-annotations-2.8.jar">
<sha256 value="f02a95fa1a5e95edb3ed859fd0fb7df709d121a35290eff8b74dce2ab7f4d6ed" origin="Generated by Gradle"/>
</artifact>
<artifact name="j2objc-annotations-2.8.pom">
<sha256 value="37f87798b18385113c918bfa9e1276fe50735ef8fa849b5800c519d54dbf11f8" origin="Generated by Gradle"/>
</artifact>
@@ -3610,6 +3844,14 @@
<sha256 value="66f563d8a2a1ee395a1283992fd646347f42054d071592d1f477523816e397fa" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.checkerframework" name="checker-qual" version="3.33.0">
<artifact name="checker-qual-3.33.0.jar">
<sha256 value="e316255bbfcd9fe50d165314b85abb2b33cb2a66a93c491db648e498a82c2de1" origin="Generated by Gradle"/>
</artifact>
<artifact name="checker-qual-3.33.0.module">
<sha256 value="e8521d75625d41272c767d262a153ac163cc505b66644a2ef705fa8949ffb4e5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.checkerframework" name="checker-qual" version="3.43.0">
<artifact name="checker-qual-3.43.0.jar">
<sha256 value="3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" origin="Generated by Gradle"/>
@@ -3768,6 +4010,11 @@
<sha256 value="3090b4e873054cdc29991ae94c92fa45afb4c145d460441d803baf6ef165bbb8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-bom" version="1.8.0">
<artifact name="kotlin-bom-1.8.0.pom">
<sha256 value="681f9878548c0a149c678092fd8b1f4cc7c5c41ba1b24397101c1073a6c76dda" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-bom" version="2.3.21">
<artifact name="kotlin-bom-2.3.21.pom">
<sha256 value="950704bad04c5143656cfca1d2c574f25fcad9cac33905ab22a57e9fdf7bfd88" origin="Generated by Gradle"/>
@@ -4400,6 +4647,19 @@
<sha256 value="ee66712b1006e971636f11554af63c56835caf315382ce23e54f59f5b8b9d4f9" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="atomicfu" version="0.28.0">
<artifact name="atomicfu-0.28.0.module">
<sha256 value="535568c9291a3da7705c4b40535eacedf86b248df570ac52669c907d4bb8558b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="atomicfu-jvm" version="0.28.0">
<artifact name="atomicfu-jvm-0.28.0.jar">
<sha256 value="39576ec46b83412ff7a2909d24cccbba90400e91f9aa67b250309bc0872ecf52" origin="Generated by Gradle"/>
</artifact>
<artifact name="atomicfu-jvm-0.28.0.module">
<sha256 value="34b623af9c25ca7f2e52406f10495c46e6f410835d7a058c60ba36923d689635" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-android" version="1.11.0">
<artifact name="kotlinx-coroutines-android-1.11.0.jar">
<sha256 value="c2cb206d27017c7d1bf5ff179787397543d13748dbabb0d7237e1585e0b29044" origin="Generated by Gradle"/>
+26 -5
View File
@@ -22,6 +22,14 @@ androidx.benchmark:benchmark-macro:1.5.0=benchmarkCompileClasspath,benchmarkLint
androidx.benchmark:benchmark-traceprocessor-android:1.5.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.benchmark:benchmark-traceprocessor:1.5.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.biometric:biometric:1.1.0=benchmarkTestedApks,debugTestedApks
androidx.camera.featurecombinationquery:featurecombinationquery:1.6.2=benchmarkTestedApks,debugTestedApks
androidx.camera.viewfinder:viewfinder-core:1.5.1=benchmarkTestedApks,debugTestedApks
androidx.camera:camera-camera2-pipe:1.6.2=benchmarkTestedApks,debugTestedApks
androidx.camera:camera-camera2:1.6.2=benchmarkTestedApks,debugTestedApks
androidx.camera:camera-core:1.6.2=benchmarkTestedApks,debugTestedApks
androidx.camera:camera-lifecycle:1.6.2=benchmarkTestedApks,debugTestedApks
androidx.camera:camera-video:1.6.2=benchmarkTestedApks,debugTestedApks
androidx.camera:camera-view:1.6.2=benchmarkTestedApks,debugTestedApks
androidx.collection:collection-jvm:1.6.0=benchmarkTestedApks,debugTestedApks
androidx.collection:collection:1.0.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.collection:collection:1.6.0=benchmarkTestedApks,debugTestedApks
@@ -64,9 +72,11 @@ androidx.compose.ui:ui-util-jvmstubs:1.12.1=benchmarkTestedApks,debugTestedApks
androidx.compose.ui:ui-util:1.12.1=benchmarkTestedApks,debugTestedApks
androidx.compose.ui:ui:1.12.1=benchmarkTestedApks,debugTestedApks
androidx.compose:compose-bom:2026.09.00=benchmarkTestedApks,debugTestedApks
androidx.concurrent:concurrent-futures-ktx:1.1.0=benchmarkTestedApks,debugTestedApks
androidx.concurrent:concurrent-futures-ktx:1.2.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.concurrent:concurrent-futures:1.1.0=benchmarkTestedApks,debugTestedApks
androidx.concurrent:concurrent-futures:1.2.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.core:core-backported-fixes:1.0.0=benchmarkTestedApks,debugTestedApks
androidx.core:core-ktx:1.18.0=benchmarkTestedApks,debugTestedApks
androidx.core:core-viewtree:1.0.0=benchmarkTestedApks,debugTestedApks
androidx.core:core:1.18.0=benchmarkTestedApks,debugTestedApks
@@ -74,6 +84,7 @@ androidx.core:core:1.9.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,
androidx.cursoradapter:cursoradapter:1.0.0=benchmarkTestedApks,debugTestedApks
androidx.customview:customview:1.0.0=benchmarkTestedApks,debugTestedApks
androidx.drawerlayout:drawerlayout:1.0.0=benchmarkTestedApks,debugTestedApks
androidx.exifinterface:exifinterface:1.4.2=benchmarkTestedApks,debugTestedApks
androidx.fragment:fragment:1.9.1=benchmarkTestedApks,debugTestedApks
androidx.interpolator:interpolator:1.0.0=benchmarkTestedApks,debugTestedApks
androidx.lifecycle:lifecycle-common-jvm:2.11.0=benchmarkTestedApks,debugTestedApks
@@ -97,6 +108,9 @@ androidx.lifecycle:lifecycle-viewmodel-savedstate-desktop:2.11.0=benchmarkTested
androidx.lifecycle:lifecycle-viewmodel-savedstate:2.11.0=benchmarkTestedApks,debugTestedApks
androidx.lifecycle:lifecycle-viewmodel:2.11.0=benchmarkTestedApks,debugTestedApks
androidx.loader:loader:1.0.0=benchmarkTestedApks,debugTestedApks
androidx.media3:media3-common:1.9.0=benchmarkTestedApks,debugTestedApks
androidx.media3:media3-container:1.9.0=benchmarkTestedApks,debugTestedApks
androidx.media3:media3-muxer:1.9.0=benchmarkTestedApks,debugTestedApks
androidx.navigation3:navigation3-runtime-desktop:1.2.0=benchmarkTestedApks,debugTestedApks
androidx.navigation3:navigation3-runtime:1.2.0=benchmarkTestedApks,debugTestedApks
androidx.navigation3:navigation3-ui-jvmstubs:1.2.0=benchmarkTestedApks,debugTestedApks
@@ -125,6 +139,7 @@ androidx.test:rules:1.5.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath
androidx.test:runner:1.7.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.tracing:tracing-desktop:2.0.0=benchmarkTestedApks,debugTestedApks
androidx.tracing:tracing-ktx:1.1.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.tracing:tracing-ktx:2.0.0=benchmarkTestedApks,debugTestedApks
androidx.tracing:tracing-perfetto-binary:1.0.1=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.tracing:tracing-perfetto-handshake:1.0.1=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.tracing:tracing-perfetto:1.0.1=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
@@ -169,18 +184,21 @@ com.android.tools:sdk-common:32.4.1=androidLintTool
com.android.tools:sdklib:32.4.1=androidLintTool
com.github.ajalt.clikt:clikt-jvm:5.0.3=ktlint
com.github.ajalt.clikt:clikt:5.0.3=ktlint
com.google.auto.value:auto-value-annotations:1.6.3=benchmarkTestedApks,debugTestedApks
com.google.code.findbugs:jsr305:3.0.2=androidLintTool,benchmarkTestedApks,debugTestedApks
com.google.code.gson:gson:2.11.0=androidLintTool
com.google.dagger:dagger-lint-aar:2.60.1=benchmarkTestedApks,debugTestedApks
com.google.dagger:dagger:2.60.1=benchmarkTestedApks,debugTestedApks
com.google.dagger:hilt-android:2.60.1=benchmarkTestedApks,debugTestedApks
com.google.dagger:hilt-core:2.60.1=benchmarkTestedApks,debugTestedApks
com.google.errorprone:error_prone_annotations:2.28.0=benchmarkTestedApks,debugTestedApks
com.google.errorprone:error_prone_annotations:2.36.0=androidLintTool
com.google.guava:failureaccess:1.0.2=androidLintTool
com.google.guava:failureaccess:1.0.2=androidLintTool,benchmarkTestedApks,debugTestedApks
com.google.guava:guava:33.3.1-android=benchmarkTestedApks,debugTestedApks
com.google.guava:guava:33.4.0-jre=androidLintTool
com.google.guava:listenablefuture:1.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,benchmarkTestedApks,debugLintChecksClasspath,debugRuntimeClasspath,debugTestedApks
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=androidLintTool
com.google.j2objc:j2objc-annotations:3.0.0=androidLintTool
com.google.guava:listenablefuture:1.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=androidLintTool,benchmarkTestedApks,debugTestedApks
com.google.j2objc:j2objc-annotations:3.0.0=androidLintTool,benchmarkTestedApks,debugTestedApks
com.google.jimfs:jimfs:1.1=androidLintTool
com.google.protobuf:protobuf-java:3.25.5=androidLintTool,unified-test-platform-gradle-work-action
com.pinterest.ktlint:ktlint-cli-reporter-baseline:1.7.1=ktlint,ktlintBaselineReporter
@@ -233,11 +251,12 @@ org.apache.httpcomponents:httpmime:4.5.6=androidLintTool
org.bouncycastle:bcpkix-jdk18on:1.80.2=androidLintTool
org.bouncycastle:bcprov-jdk18on:1.80.2=androidLintTool
org.bouncycastle:bcutil-jdk18on:1.80.2=androidLintTool
org.checkerframework:checker-qual:3.43.0=androidLintTool
org.checkerframework:checker-qual:3.43.0=androidLintTool,benchmarkTestedApks,debugTestedApks
org.ec4j.core:ec4j-core:1.1.1=ktlint,ktlintBaselineReporter,ktlintRuleset
org.glassfish.jaxb:jaxb-runtime:2.3.2=androidLintTool
org.glassfish.jaxb:txw2:2.3.2=androidLintTool
org.hamcrest:hamcrest-core:1.3=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath
org.jetbrains.kotlin:kotlin-bom:1.8.0=benchmarkTestedApks,debugTestedApks
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-build-tools-compat:2.4.21=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
@@ -270,6 +289,8 @@ org.jetbrains.kotlin:kotlin-stdlib:2.2.10=androidLintTool,unified-test-platform-
org.jetbrains.kotlin:kotlin-stdlib:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.4.21=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,benchmarkTestedApks,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugTestedApks,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-tooling-core:2.4.21=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlinx:atomicfu-jvm:0.28.0=benchmarkTestedApks,debugTestedApks
org.jetbrains.kotlinx:atomicfu:0.28.0=benchmarkTestedApks,debugTestedApks
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0=benchmarkTestedApks,debugTestedApks
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.11.0=benchmarkTestedApks,debugTestedApks
+3
View File
@@ -40,6 +40,9 @@ kotlin {
dependencies {
implementation(project(":core"))
implementation(libs.camera.camera2)
implementation(libs.camera.lifecycle)
implementation(libs.camera.view)
implementation(libs.biometric)
implementation(libs.fragment)
testImplementation(libs.junit)
+34 -7
View File
@@ -14,13 +14,23 @@ androidx.appcompat:appcompat:1.2.0=debugAndroidTestLintChecksClasspath,debugAndr
androidx.arch.core:core-common:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.arch.core:core-runtime:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.biometric:biometric:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera.featurecombinationquery:featurecombinationquery:1.6.2=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera.viewfinder:viewfinder-core:1.5.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-camera2-pipe:1.6.2=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-camera2:1.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-core:1.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-lifecycle:1.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-video:1.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.camera:camera-view:1.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.collection:collection-jvm:1.4.2=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
androidx.collection:collection-jvm:1.6.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.collection:collection:1.4.2=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
androidx.collection:collection:1.6.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.concurrent:concurrent-futures-ktx:1.1.0=debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.concurrent:concurrent-futures-ktx:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.concurrent:concurrent-futures:1.1.0=debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.concurrent:concurrent-futures:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.core:core-backported-fixes:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.core:core-ktx:1.13.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.core:core-ktx:1.9.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
androidx.core:core-viewtree:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
@@ -29,6 +39,7 @@ androidx.core:core:1.9.0=debugAndroidTestCompileClasspath,debugCompileClasspath,
androidx.cursoradapter:cursoradapter:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.customview:customview:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.drawerlayout:drawerlayout:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.exifinterface:exifinterface:1.4.2=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.fragment:fragment:1.9.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.interpolator:interpolator:1.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-common-jvm:2.10.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
@@ -46,6 +57,9 @@ androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.10.0=debugAndroidTes
androidx.lifecycle:lifecycle-viewmodel-savedstate:2.10.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel:2.10.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.loader:loader:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.media3:media3-common:1.9.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.media3:media3-container:1.9.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.media3:media3-muxer:1.9.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.profileinstaller:profileinstaller:1.4.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.savedstate:savedstate-android:1.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.savedstate:savedstate-ktx:1.4.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
@@ -56,6 +70,7 @@ androidx.test:core:1.7.0=debugAndroidTestCompileClasspath,debugAndroidTestLintCh
androidx.test:monitor:1.8.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.test:runner:1.7.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.tracing:tracing-android:2.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.tracing:tracing-ktx:2.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.tracing:tracing:1.1.0=debugAndroidTestCompileClasspath
androidx.tracing:tracing:2.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.vectordrawable:vectordrawable-animated:1.1.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
@@ -95,14 +110,21 @@ com.android.tools:sdk-common:32.4.1=androidLintTool
com.android.tools:sdklib:32.4.1=androidLintTool
com.github.ajalt.clikt:clikt-jvm:5.0.3=ktlint
com.github.ajalt.clikt:clikt:5.0.3=ktlint
com.google.code.findbugs:jsr305:3.0.2=androidLintTool
com.google.auto.value:auto-value-annotations:1.6.3=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.code.findbugs:jsr305:3.0.2=androidLintTool,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.code.gson:gson:2.11.0=androidLintTool
com.google.dagger:dagger:2.59=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.errorprone:error_prone_annotations:2.18.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
com.google.errorprone:error_prone_annotations:2.28.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.errorprone:error_prone_annotations:2.36.0=androidLintTool
com.google.guava:failureaccess:1.0.2=androidLintTool
com.google.guava:failureaccess:1.0.1=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
com.google.guava:failureaccess:1.0.2=androidLintTool,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.guava:guava:32.0.1-android=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
com.google.guava:guava:33.3.1-android=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.guava:guava:33.4.0-jre=androidLintTool
com.google.guava:listenablefuture:1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=androidLintTool
com.google.j2objc:j2objc-annotations:3.0.0=androidLintTool
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=androidLintTool,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.j2objc:j2objc-annotations:2.8=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
com.google.j2objc:j2objc-annotations:3.0.0=androidLintTool,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.jimfs:jimfs:1.1=androidLintTool
com.google.protobuf:protobuf-java:3.25.5=androidLintTool,unified-test-platform-gradle-work-action
com.pinterest.ktlint:ktlint-cli-reporter-baseline:1.7.1=ktlint,ktlintBaselineReporter
@@ -132,8 +154,9 @@ io.github.detekt.sarif4k:sarif4k:0.6.0=ktlint,ktlintReporter
io.github.oshai:kotlin-logging-jvm:7.0.7=ktlint,ktlintBaselineReporter,ktlintReporter,ktlintRuleset
io.github.oshai:kotlin-logging:5.1.0=ktlint,ktlintBaselineReporter,ktlintReporter
jakarta.activation:jakarta.activation-api:1.2.1=androidLintTool
jakarta.inject:jakarta.inject-api:2.0.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
jakarta.xml.bind:jakarta.xml.bind-api:2.3.2=androidLintTool
javax.inject:javax.inject:1=androidLintTool
javax.inject:javax.inject:1=androidLintTool,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
junit:junit:4.13.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
net.java.dev.jna:jna-platform:5.6.0=androidLintTool
net.java.dev.jna:jna:5.19.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
@@ -149,11 +172,13 @@ org.apache.httpcomponents:httpmime:4.5.6=androidLintTool
org.bouncycastle:bcpkix-jdk18on:1.80.2=androidLintTool
org.bouncycastle:bcprov-jdk18on:1.80.2=androidLintTool
org.bouncycastle:bcutil-jdk18on:1.80.2=androidLintTool
org.checkerframework:checker-qual:3.43.0=androidLintTool
org.checkerframework:checker-qual:3.33.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
org.checkerframework:checker-qual:3.43.0=androidLintTool,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.ec4j.core:ec4j-core:1.1.1=ktlint,ktlintBaselineReporter,ktlintRuleset
org.glassfish.jaxb:jaxb-runtime:2.3.2=androidLintTool
org.glassfish.jaxb:txw2:2.3.2=androidLintTool
org.hamcrest:hamcrest-core:1.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.jetbrains.kotlin:kotlin-bom:1.8.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-build-tools-compat:2.4.21=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
@@ -186,6 +211,8 @@ org.jetbrains.kotlin:kotlin-stdlib:2.2.10=androidLintTool,unified-test-platform-
org.jetbrains.kotlin:kotlin-stdlib:2.4.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.4.21=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlin:kotlin-tooling-core:2.4.21=kotlinAbiValidationCompatClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlinx:atomicfu-jvm:0.28.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:atomicfu:0.28.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.11.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.CAMERA" />
<application>
<activity android:name="de.rfc1437.ironstorage.platform.KeystoreTestActivity" android:exported="false" android:theme="@android:style/Theme.Material.Light.NoActionBar" />
</application>
@@ -0,0 +1,50 @@
package de.rfc1437.ironstorage.platform
import android.Manifest
import android.content.pm.PackageManager
import androidx.test.core.app.ActivityScenario
import androidx.test.platform.app.InstrumentationRegistry
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class AndroidQrCameraTest {
@Test fun livePreviewDeliversBoundedPlanesAndCanBeClosedAndReopened() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals(
PackageManager.PERMISSION_GRANTED,
context.checkSelfPermission(Manifest.permission.CAMERA)
)
ActivityScenario.launch(KeystoreTestActivity::class.java).use { activity ->
repeat(2) {
val received = CountDownLatch(1)
val failed = CountDownLatch(1)
var camera: AndroidQrCamera? = null
activity.onActivity { host ->
camera = AndroidQrCamera(host, { frame ->
try {
assertTrue(frame.width in 1u..2048u)
assertTrue(frame.height in 1u..2048u)
assertTrue(frame.pixels.size <= 4 * 1024 * 1024)
assertTrue(frame.rotationDegrees in listOf(0u, 90u, 180u, 270u))
received.countDown()
} finally {
frame.pixels.fill(0)
}
}, { failed.countDown() })
host.setContentView(requireNotNull(camera).view)
assertTrue(requireNotNull(camera).view.clipToOutline)
}
assertTrue(
"The emulator camera did not deliver a frame",
received.await(15, TimeUnit.SECONDS)
)
assertFalse("CameraX failed to bind", failed.count == 0L)
activity.onActivity { camera?.close() }
}
}
}
}
@@ -0,0 +1,113 @@
package de.rfc1437.ironstorage.platform
import android.util.Size
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.Preview
import androidx.camera.core.resolutionselector.ResolutionSelector
import androidx.camera.core.resolutionselector.ResolutionStrategy
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import de.rfc1437.ironstorage.core.CameraFrame
import de.rfc1437.ironstorage.core.CameraPreview
import java.util.concurrent.Executors
internal class AndroidQrCamera(
private val activity: FragmentActivity,
private val frame: (CameraFrame) -> Unit,
private val failure: () -> Unit
) : CameraPreview {
override val view = PreviewView(activity).apply {
implementationMode = PreviewView.ImplementationMode.COMPATIBLE
outlineProvider = android.view.ViewOutlineProvider.BOUNDS
clipToOutline = true
scaleType = PreviewView.ScaleType.FIT_CENTER
importantForAccessibility = android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO
}
private val executor = Executors.newSingleThreadExecutor()
private val preview = Preview.Builder().build()
private val analysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setResolutionSelector(
ResolutionSelector.Builder().setResolutionStrategy(
ResolutionStrategy(Size(1280, 720), ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER)
).build()
).build()
private var provider: ProcessCameraProvider? = null
@Volatile private var active = true
init {
analysis.setAnalyzer(executor) { image ->
var bytes: ByteArray? = null
try {
if (active && image.width in 1..2048 && image.height in 1..2048) {
val plane = image.planes[0]
val source = plane.buffer.duplicate()
if (source.remaining() <= 4 * 1024 * 1024) {
val pixels = ByteArray(source.remaining())
bytes = pixels
source.get(pixels)
if (active) {
frame(
CameraFrame(
pixels,
image.width.toUInt(),
image.height.toUInt(),
plane.rowStride.toUInt(),
plane.pixelStride.toUInt(),
image.imageInfo.rotationDegrees.toUInt()
)
)
// The receiver clears transferred bytes after the native call.
bytes = null
}
} else {
failure()
}
}
} finally {
bytes?.fill(0)
image.close()
}
}
val pending = ProcessCameraProvider.getInstance(activity)
pending.addListener({
if (active) {
try {
provider = pending.get()
preview.setSurfaceProvider(view.surfaceProvider)
provider!!.bindToLifecycle(
activity,
CameraSelector.DEFAULT_BACK_CAMERA,
preview,
analysis
)
} catch (_: java.util.concurrent.ExecutionException) {
close()
failure()
} catch (_: InterruptedException) {
Thread.currentThread().interrupt()
close()
failure()
} catch (_: IllegalArgumentException) {
close()
failure()
} catch (_: SecurityException) {
close()
failure()
}
}
}, ContextCompat.getMainExecutor(activity))
}
override fun close() {
active = false
analysis.clearAnalyzer()
provider?.unbind(preview, analysis)
provider = null
executor.shutdown()
}
}
@@ -40,6 +40,13 @@ class AndroidSettingsPlatform(
host.clear()
}
}
override fun cameraPreview(
frame: (de.rfc1437.ironstorage.core.CameraFrame) -> Unit,
failure: () -> Unit
): de.rfc1437.ironstorage.core.CameraPreview? {
val activity = host.get() ?: return null
return AndroidQrCamera(activity, frame, failure)
}
override fun chooseQrImages(): Boolean {
val picker = picker ?: return false
return try {