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); revision-checked [adaptive navigation](../docs/android/password-navigation.md);
Home renders Rust-owned [Git activity and HTTPS synchronization](../docs/android/home-sync.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). 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 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. 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.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.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.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.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.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 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-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.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.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.1.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.concurrent:concurrent-futures:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath androidx.concurrent:concurrent-futures:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath
androidx.coordinatorlayout:coordinatorlayout:1.1.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath 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-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-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 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.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.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.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.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.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 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-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.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.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-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-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 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:rules:1.4.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.test:runner:1.7.0=debugAndroidTestCompileClasspath,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-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:1.1.0=debugAndroidTestCompileClasspath
androidx.tracing:tracing:2.0.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath 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 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.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.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.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.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.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 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.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.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.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.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.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.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,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.guava:failureaccess:1.0.2=androidLintTool
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: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.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: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: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,debugAndroidTestLintChecksClasspath,hiltAnnotationProcessorBenchmark,hiltAnnotationProcessorDebug,hiltAnnotationProcessorDebugAndroidTest,hiltAnnotationProcessorDebugUnitTest,hiltAnnotationProcessorProfile,hiltAnnotationProcessorRelease,kspBenchmarkKotlinProcessorClasspath,kspDebugKotlinProcessorClasspath,kspProfileKotlinProcessorClasspath,kspReleaseKotlinProcessorClasspath 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:1.3=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath 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.0.0=androidLintTool
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.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.jimfs:jimfs:1.1=androidLintTool
com.google.protobuf:protobuf-java:3.25.5=androidLintTool,unified-test-platform-gradle-work-action 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:bcprov-jdk18on:1.80.2=androidLintTool
org.bouncycastle:bcutil-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.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.43.0=androidLintTool,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,profileLintChecksClasspath,profileRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
org.checkerframework:checker-qual:3.22.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
org.checkerframework:checker-qual:3.43.0=androidLintTool
org.ec4j.core:ec4j-core:1.1.1=ktlint,ktlintBaselineReporter,ktlintRuleset org.ec4j.core:ec4j-core:1.1.1=ktlint,ktlintBaselineReporter,ktlintRuleset
org.glassfish.jaxb:jaxb-runtime:2.3.2=androidLintTool org.glassfish.jaxb:jaxb-runtime:2.3.2=androidLintTool
org.glassfish.jaxb:txw2: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-core:1.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath
org.hamcrest:hamcrest-library:1.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath org.hamcrest:hamcrest-library:1.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
org.jetbrains.kotlin:compose-group-mapping:2.4.21=composeMappingProducerClasspath 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.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath 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 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.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-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.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-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.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 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.ow2.asm:asm:9.9.1=composeMappingProducerClasspath
org.slf4j:slf4j-api:2.0.7=ktlint 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 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.performClick
import androidx.compose.ui.test.performScrollTo import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTextInput import androidx.compose.ui.test.performTextInput
import androidx.lifecycle.ViewModelProvider
import de.rfc1437.ironstorage.core.OnboardingRepository import de.rfc1437.ironstorage.core.OnboardingRepository
import de.rfc1437.ironstorage.core.SetupKey import de.rfc1437.ironstorage.core.SetupKey
import de.rfc1437.ironstorage.core.SetupStatus import de.rfc1437.ironstorage.core.SetupStatus
import de.rfc1437.ironstorage.features.OnboardingScreen import de.rfc1437.ironstorage.features.OnboardingScreen
import de.rfc1437.ironstorage.features.SetupFormViewModel import de.rfc1437.ironstorage.features.SetupFormViewModel
import de.rfc1437.ironstorage.features.ShellViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
@@ -32,7 +34,16 @@ class OnboardingScreenTest {
val secrets = SetupFormViewModel() val secrets = SetupFormViewModel()
compose.activity.runOnUiThread { compose.activity.runOnUiThread {
compose.activity.setProtectedTestContent { 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() 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 { private class FixtureRepository : OnboardingRepository {
override val setup = MutableStateFlow(SetupStatus()) override val setup = MutableStateFlow(SetupStatus())
val calls = mutableListOf<String>() val calls = mutableListOf<String>()
@@ -6,6 +6,8 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.test.assertHeightIsAtLeast import androidx.compose.ui.test.assertHeightIsAtLeast
import androidx.compose.ui.test.assertIsDisplayed 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.assertTextContains
import androidx.compose.ui.test.assertWidthIsAtLeast import androidx.compose.ui.test.assertWidthIsAtLeast
import androidx.compose.ui.test.hasText import androidx.compose.ui.test.hasText
@@ -24,6 +26,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import de.rfc1437.ironstorage.core.AuthenticationRepository import de.rfc1437.ironstorage.core.AuthenticationRepository
import de.rfc1437.ironstorage.core.AuthenticationState import de.rfc1437.ironstorage.core.AuthenticationState
import de.rfc1437.ironstorage.core.CameraPreview
import de.rfc1437.ironstorage.core.KeyTransferState import de.rfc1437.ironstorage.core.KeyTransferState
import de.rfc1437.ironstorage.core.ProtectedPresentation import de.rfc1437.ironstorage.core.ProtectedPresentation
import de.rfc1437.ironstorage.core.ProviderStatus 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.SettingsRepository
import de.rfc1437.ironstorage.core.SettingsState import de.rfc1437.ironstorage.core.SettingsState
import de.rfc1437.ironstorage.core.TransferFrame import de.rfc1437.ironstorage.core.TransferFrame
import de.rfc1437.ironstorage.core.TransferKey
import de.rfc1437.ironstorage.features.ApplicationTheme 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.SettingsScreen
import de.rfc1437.ironstorage.features.SetupFormViewModel import de.rfc1437.ironstorage.features.SetupFormViewModel
import de.rfc1437.ironstorage.features.ShellViewModel import de.rfc1437.ironstorage.features.ShellViewModel
@@ -164,6 +169,69 @@ class SettingsScreenTest {
authentication.presentation.clear() 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 { internal class Fixture : SettingsRepository {
override val state = MutableStateFlow( override val state = MutableStateFlow(
SettingsState( SettingsState(
@@ -193,6 +261,8 @@ class SettingsScreenTest {
var removals = 0 var removals = 0
var reloads = 0 var reloads = 0
var choseImages = false var choseImages = false
var exports = 0
var imports = 0
override fun reload() { override fun reload() {
reloads += 1 reloads += 1
} }
@@ -211,15 +281,41 @@ class SettingsScreenTest {
override fun openCredentials() = Unit override fun openCredentials() = Unit
override fun beginTransfer() { override fun beginTransfer() {
state.value = 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() { override fun chooseQrImages() {
choseImages = true choseImages = true
} }
override fun addQrImages(uris: List<String>) = Unit override fun addQrImages(uris: List<String>) = Unit
override fun addQrPayload(payload: 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() { override fun closeTransfer() {
state.value = state.value.copy(transfer = KeyTransferState()) 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.INTERNET" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" /> <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 <application
android:name=".IronStorageApplication" android:name=".IronStorageApplication"
android:allowBackup="false" android:allowBackup="false"
@@ -438,6 +438,7 @@ class NativeEntryViewerTest {
keystore: MemoryKeystore keystore: MemoryKeystore
) { ) {
var image = ByteArray(0) var image = ByteArray(0)
var cameraReceiver: ((CameraFrame) -> Unit)? = null
val platform = object : SettingsPlatform { val platform = object : SettingsPlatform {
override fun autofill() = ProviderStatus(true, false, false) override fun autofill() = ProviderStatus(true, false, false)
override fun credentials() = override fun credentials() =
@@ -446,6 +447,13 @@ class NativeEntryViewerTest {
override fun version() = "native-settings-fixture" override fun version() = "native-settings-fixture"
override fun openAutofill() = true override fun openAutofill() = true
override fun openCredentials() = true override fun openCredentials() = true
override fun cameraPreview(
frame: (CameraFrame) -> Unit,
failure: () -> Unit
): CameraPreview? {
cameraReceiver = frame
return null
}
override fun chooseQrImages() = true override fun chooseQrImages() = true
override suspend fun readQrImage(uri: String) = image.copyOf() override suspend fun readQrImage(uri: String) = image.copyOf()
} }
@@ -533,6 +541,18 @@ class NativeEntryViewerTest {
image = output.toByteArray() image = output.toByteArray()
request { settings.closeTransfer() } request { settings.closeTransfer() }
assertTrue(frame.modules.all { it.toInt() == 0 }) 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() val qrStart = android.os.SystemClock.elapsedRealtime()
assertNull(request { settings.addQrImages(listOf("content://public-fixture/qr")) }.error) assertNull(request { settings.addQrImages(listOf("content://public-fixture/qr")) }.error)
val qrMs = android.os.SystemClock.elapsedRealtime() - qrStart val qrMs = android.os.SystemClock.elapsedRealtime() - qrStart
@@ -559,10 +579,14 @@ class NativeEntryViewerTest {
assertNotNull(request { settings.setDefaultKey(bob.fingerprint) }.error) assertNotNull(request { settings.setDefaultKey(bob.fingerprint) }.error)
assertEquals(fingerprint, settings.state.value.page!!.defaultKeyFingerprint) assertEquals(fingerprint, settings.state.value.page!!.defaultKeyFingerprint)
assertNull(request { settings.beginTransfer() }.error) 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() } withContext(Dispatchers.Main.immediate) { authentication.presentation.clear() }
assertFalse(settings.state.value.transfer.open) assertFalse(settings.state.value.transfer.open)
assertTrue(settings.state.value.transfer.frames.isEmpty()) assertTrue(settings.state.value.transfer.frames.isEmpty())
assertTrue(privateFrames.all { frame -> frame.modules.all { it.toInt() == 0 } })
assertNull(request { settings.setDefaultKey(fingerprint) }.error) assertNull(request { settings.setDefaultKey(fingerprint) }.error)
assertFalse(authentication.state.value.unlocked) assertFalse(authentication.state.value.unlocked)
assertEquals(fingerprint, settings.state.value.page!!.defaultKeyFingerprint) 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.NativeBridgeException
import de.rfc1437.ironstorage.bridge.generated.MobileAuthenticationFfiException 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.MobileKeyTransfer
import de.rfc1437.ironstorage.bridge.generated.MobileKeyTransferFfiException import de.rfc1437.ironstorage.bridge.generated.MobileKeyTransferFfiException
import de.rfc1437.ironstorage.bridge.generated.MobileKeyTransferImport 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.MobilePrivateStorageFfiException
import de.rfc1437.ironstorage.bridge.generated.MobileTotpOperation import de.rfc1437.ironstorage.bridge.generated.MobileTotpOperation
import de.rfc1437.ironstorage.bridge.generated.mobileKeyTransfer import de.rfc1437.ironstorage.bridge.generated.mobileKeyTransfer
import de.rfc1437.ironstorage.bridge.generated.mobileLocalKeyTransferImporter
import de.rfc1437.ironstorage.bridge.generated.mobileTotpOperation import de.rfc1437.ironstorage.bridge.generated.mobileTotpOperation
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -31,10 +34,12 @@ class NativeSettingsRepository(
private val mutable = MutableStateFlow(SettingsState()) private val mutable = MutableStateFlow(SettingsState())
override val state: StateFlow<SettingsState> = mutable override val state: StateFlow<SettingsState> = mutable
private val operation = Mutex() private val operation = Mutex()
private val cameraBusy = AtomicBoolean()
@Volatile private var epoch = 0L @Volatile private var epoch = 0L
@Volatile private var discovery: MobileTotpOperation? = null @Volatile private var discovery: MobileTotpOperation? = null
private var visibility: Job? = null
private var transfer: MobileKeyTransfer? = null private var transfer: MobileKeyTransfer? = null
private var importer: MobileKeyTransferImport? = null private var importer: MobileKeyTransferImport? = null
init { 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) { withContext(Dispatchers.IO) {
session.storage() session.storage()
transfer = mobileKeyTransfer() releaseTransfer()
importer = requireNotNull(transfer).importer() 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 == if (ticket ==
epoch epoch
) { ) {
mutable.value = mutable.value.copy( mutable.value = mutable.value.copy(
transfer = KeyTransferState( transfer = KeyTransferState(
open = true, open = true,
setup = setup,
keys = keys.map { keys = keys.map {
TransferKey( TransferKey(
it.fingerprint, it.fingerprint,
@@ -152,6 +165,12 @@ class NativeSettingsRepository(
} }
override fun exportKey(fingerprint: String, privateKey: Boolean, passphrase: String) = override fun exportKey(fingerprint: String, privateKey: Boolean, passphrase: String) =
run { ticket -> 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) { val result = withContext(Dispatchers.IO) {
requireNotNull(transfer).export( requireNotNull(transfer).export(
fingerprint, fingerprint,
@@ -165,11 +184,28 @@ class NativeSettingsRepository(
mutable.value.transfer.frames.forEach { it.modules.fill(0) } mutable.value.transfer.frames.forEach { it.modules.fill(0) }
mutable.value = mutable.value.copy( mutable.value = mutable.value.copy(
transfer = mutable.value.transfer.copy( transfer = mutable.value.transfer.copy(
exportedKey = TransferKey(
result.key.fingerprint,
result.key.title,
result.key.kind == MobileKeyTransferKind.PRIVATE
),
frames = result.frames.map { frames = result.frames.map {
TransferFrame(it.sequence, it.total, it.width, it.modules) 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 { } else {
result.frames.forEach { it.modules.fill(0) } result.frames.forEach { it.modules.fill(0) }
} }
@@ -177,9 +213,11 @@ class NativeSettingsRepository(
private fun progress(ticket: Long, progress: MobileKeyTransferProgress) { private fun progress(ticket: Long, progress: MobileKeyTransferProgress) {
if (ticket != epoch) return if (ticket != epoch) return
mutable.value = mutable.value.copy( mutable.value = mutable.value.copy(
error = null,
transfer = mutable.value.transfer.copy( transfer = mutable.value.transfer.copy(
received = progress.received, received = progress.received,
total = progress.total, total = progress.total,
duplicate = progress.duplicate,
ready = progress.key?.let { ready = progress.key?.let {
TransferKey( TransferKey(
it.fingerprint, 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() { override fun chooseQrImages() {
if (!platform.chooseQrImages()) { if (!platform.chooseQrImages()) {
mutable.value = mutable.value =
@@ -246,6 +343,15 @@ class NativeSettingsRepository(
progress(ticket, withContext(Dispatchers.IO) { requireNotNull(importer).addFrame(payload) }) progress(ticket, withContext(Dispatchers.IO) { requireNotNull(importer).addFrame(payload) })
} }
override fun importKey(passphrase: String, makeDefault: Boolean, overwrite: Boolean) = run { 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) { val result = withContext(Dispatchers.IO) {
requireNotNull(importer).import(passphrase.ifEmpty { null }, makeDefault, overwrite) requireNotNull(importer).import(passphrase.ifEmpty { null }, makeDefault, overwrite)
} }
@@ -264,6 +370,8 @@ class NativeSettingsRepository(
transfer = null transfer = null
} }
override fun closeTransfer() { override fun closeTransfer() {
visibility?.cancel()
visibility = null
epoch += 1 epoch += 1
mutable.value.transfer.frames.forEach { it.modules.fill(0) } mutable.value.transfer.frames.forEach { it.modules.fill(0) }
mutable.value = mutable.value.copy(transfer = KeyTransferState(), busy = false) mutable.value = mutable.value.copy(transfer = KeyTransferState(), busy = false)
@@ -295,7 +403,11 @@ class NativeSettingsRepository(
mutable.value.copy(error = error.title + "\n" + error.detail) mutable.value.copy(error = error.title + "\n" + error.detail)
} }
} catch (error: MobileKeyTransferFfiException.Failed) { } 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) { } catch (_: MobilePrivateStorageFfiException) {
if (ticket == if (ticket ==
epoch epoch
@@ -12,6 +12,20 @@ interface SettingsPlatform {
fun openCredentials(): Boolean fun openCredentials(): Boolean
fun chooseQrImages(): Boolean fun chooseQrImages(): Boolean
suspend fun readQrImage(uri: String): ByteArray 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( data class SettingsPage(
val repositoryTitle: String, val repositoryTitle: String,
@@ -45,7 +59,10 @@ data class KeyTransferState(
val frames: List<TransferFrame> = emptyList(), val frames: List<TransferFrame> = emptyList(),
val received: UInt = 0u, val received: UInt = 0u,
val total: 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])" override fun toString() = "KeyTransferState([REDACTED])"
} }
@@ -69,6 +86,8 @@ interface SettingsRepository {
fun openAutofill() fun openAutofill()
fun openCredentials() fun openCredentials()
fun beginTransfer() fun beginTransfer()
fun beginSetupTransfer()
fun cameraPreview(): CameraPreview?
fun exportKey(fingerprint: String, privateKey: Boolean, passphrase: String) fun exportKey(fingerprint: String, privateKey: Boolean, passphrase: String)
fun chooseQrImages() fun chooseQrImages()
fun addQrImages(uris: List<String>) fun addQrImages(uris: List<String>)
@@ -38,4 +38,5 @@
<string name="core_copied">Copied. The clipboard will be cleared automatically.</string> <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_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_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> </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-jdk7:1.8.0": "Apache-2.0",
"org.jetbrains.kotlin:kotlin-stdlib-jdk8: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-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": [ "rust_licenses": [
"(Apache-2.0 OR MIT) AND BSD-3-Clause", "(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.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
@@ -54,9 +55,17 @@ class SetupFormViewModel @Inject constructor() : ViewModel() {
fun OnboardingScreen( fun OnboardingScreen(
repository: OnboardingRepository, repository: OnboardingRepository,
secrets: SetupFormViewModel, secrets: SetupFormViewModel,
settings: de.rfc1437.ironstorage.core.SettingsRepository,
authentication: de.rfc1437.ironstorage.core.AuthenticationRepository,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
scroll: Boolean = true 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() ProtectedInputSurface()
val state by repository.setup.collectAsStateWithLifecycle() val state by repository.setup.collectAsStateWithLifecycle()
var step by rememberSaveable { mutableStateOf("choice") } var step by rememberSaveable { mutableStateOf("choice") }
@@ -183,6 +192,9 @@ fun OnboardingScreen(
} }
"import" -> { "import" -> {
Text(stringResource(R.string.setup_import_help)) Text(stringResource(R.string.setup_import_help))
TextButton(onClick = settings::beginSetupTransfer) {
Text(stringResource(R.string.qr_scan))
}
SecretField( SecretField(
stringResource(R.string.setup_armor), stringResource(R.string.setup_armor),
secrets.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( OnboardingScreen(
onboarding, onboarding,
setup, setup,
repository,
authentication,
scroll = false scroll = false
) )
} }
@@ -290,7 +292,7 @@ fun SettingsScreen(
SettingsEditDialog(repository, authentication, action) { edit = null } SettingsEditDialog(repository, authentication, action) { edit = null }
} }
} }
if (state.transfer.open) KeyTransferDialog(repository, authentication) if (state.transfer.open && !showSetup) KeyTransferDialog(repository, authentication)
if (licenses) { if (licenses) {
val resources = LocalResources.current val resources = LocalResources.current
val loading = stringResource(R.string.settings_license_loading) val loading = stringResource(R.string.settings_license_loading)
@@ -422,7 +424,7 @@ private fun SettingsEditDialog(
} }
@Composable @Composable
private fun KeyTransferDialog( internal fun KeyTransferDialog(
repository: SettingsRepository, repository: SettingsRepository,
authentication: AuthenticationRepository authentication: AuthenticationRepository
) { ) {
@@ -436,6 +438,15 @@ private fun KeyTransferDialog(
var frame by remember { mutableStateOf(0) } var frame by remember { mutableStateOf(0) }
var passphrase by rememberProtectedInput(authentication.presentation) var passphrase by rememberProtectedInput(authentication.presentation)
var payload 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() ProtectedWindow()
AlertDialog( AlertDialog(
properties = DialogProperties( properties = DialogProperties(
@@ -454,91 +465,126 @@ private fun KeyTransferDialog(
ProtectedInputSurface(authentication.presentation) ProtectedInputSurface(authentication.presentation)
state.error?.let { Text(it, color = MaterialTheme.colorScheme.error) } state.error?.let { Text(it, color = MaterialTheme.colorScheme.error) }
if (state.busy) CircularProgressIndicator() if (state.busy) CircularProgressIndicator()
state.transfer.keys.forEach { key -> if (!state.transfer.setup) {
Row( state.transfer.keys.forEach { key ->
Modifier.fillMaxWidth().selectable( Row(
selected = fingerprint == key.fingerprint, Modifier.fillMaxWidth().selectable(
enabled = !state.busy, selected = fingerprint == key.fingerprint,
role = Role.RadioButton, enabled = !state.busy,
onClick = { fingerprint = key.fingerprint } role = Role.RadioButton,
) onClick = { fingerprint = key.fingerprint }
) { )
RadioButton(selected = fingerprint == key.fingerprint, onClick = null) ) {
Column(Modifier.weight(1f)) { RadioButton(selected = fingerprint == key.fingerprint, onClick = null)
Text(key.title) Column(Modifier.weight(1f)) {
Text(key.fingerprint) 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)
)
}
} }
} }
} }
FlowRow { TextButton(
TextButton(enabled = frame > 0, onClick = { enabled =
frame -= 1 !state.busy &&
}) { Text(stringResource(R.string.settings_previous)) } state.transfer.keys.any {
TextButton(enabled = frame < state.transfer.frames.lastIndex, onClick = { it.fingerprint == fingerprint && it.privateKey
frame += },
1 onClick = { repository.setDefaultKey(fingerprint) }
}) { Text(stringResource(R.string.settings_next)) } ) { 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) { TextButton(enabled = !state.busy, onClick = repository::chooseQrImages) {
Text(stringResource(R.string.settings_qr_images)) Text(stringResource(R.string.settings_qr_images))
} }
@@ -564,22 +610,50 @@ private fun KeyTransferDialog(
state.transfer.total.toInt() state.transfer.total.toInt()
) )
) )
if (state.transfer.duplicate) Text(stringResource(R.string.qr_duplicate))
state.transfer.ready?.let { state.transfer.ready?.let {
Text(it.title) Text(it.title)
Text(it.fingerprint) Text(it.fingerprint)
SettingsToggle(stringResource(R.string.settings_make_default), makeDefault) { if (!state.transfer.setup) {
makeDefault = SettingsToggle(
it 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) { if (it.privateKey) {
overwrite = SettingsToggle(
it 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()), Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(16.dp) 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 -> { ready && route == ShellRoute.Passwords -> {
@@ -224,4 +224,15 @@
<item quantity="other">%1$d results</item> <item quantity="other">%1$d results</item>
</plurals> </plurals>
<string name="settings_license_loading">Loading license…</string> <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> </resources>
+4
View File
@@ -1,4 +1,5 @@
[versions] [versions]
camera = "1.6.2"
agp = "9.4.1" agp = "9.4.1"
kotlin = "2.4.21" kotlin = "2.4.21"
ksp = "2.3.12" ksp = "2.3.12"
@@ -28,6 +29,9 @@ biometric = "1.1.0"
fragment = "1.9.1" fragment = "1.9.1"
[libraries] [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" } benchmark-macro = { module = "androidx.benchmark:benchmark-macro-junit4", version.ref = "benchmark" }
rustls-verifier = { group = "rustls", name = "rustls-platform-verifier", version.ref = "rustlsVerifier" } rustls-verifier = { group = "rustls", name = "rustls-platform-verifier", version.ref = "rustlsVerifier" }
compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose" } compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose" }
+260
View File
@@ -183,6 +183,11 @@
<sha256 value="03fb659177c8618e47425925c11bc91f384703ea26c265be9b25cd79292b511c" origin="Generated by Gradle"/> <sha256 value="03fb659177c8618e47425925c11bc91f384703ea26c265be9b25cd79292b511c" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.appcompat" name="appcompat" version="1.2.0">
<artifact name="appcompat-1.2.0.aar"> <artifact name="appcompat-1.2.0.aar">
<sha256 value="3d2131a55a61a777322e2126e0018011efa6339e53b44153eb651b16020cca70" origin="Generated by Gradle"/> <sha256 value="3d2131a55a61a777322e2126e0018011efa6339e53b44153eb651b16020cca70" origin="Generated by Gradle"/>
@@ -284,6 +289,70 @@
<sha256 value="4999f205b2b38e31180ad58111e32d564190c2024fbfd58527e2da8bfb67ff65" origin="Generated by Gradle"/> <sha256 value="4999f205b2b38e31180ad58111e32d564190c2024fbfd58527e2da8bfb67ff65" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.cardview" name="cardview" version="1.0.0">
<artifact name="cardview-1.0.0.aar"> <artifact name="cardview-1.0.0.aar">
<sha256 value="1193c04c22a3d6b5946dae9f4e8c59d6adde6a71b6bd5d87fb99d82dda1afec7" origin="Generated by Gradle"/> <sha256 value="1193c04c22a3d6b5946dae9f4e8c59d6adde6a71b6bd5d87fb99d82dda1afec7" origin="Generated by Gradle"/>
@@ -848,6 +917,14 @@
<sha256 value="afd7094d3a07422be2e991bc87048542ae9724a5c42bd54b5cae3029c7970a1d" origin="Generated by Gradle"/> <sha256 value="afd7094d3a07422be2e991bc87048542ae9724a5c42bd54b5cae3029c7970a1d" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.concurrent" name="concurrent-futures-ktx" version="1.2.0">
<artifact name="concurrent-futures-ktx-1.2.0.jar"> <artifact name="concurrent-futures-ktx-1.2.0.jar">
<sha256 value="e1f3e17bb4358ccd6c77ca45f70635c9aba237261f19eaa4f64a0218c00e2a3e" origin="Generated by Gradle"/> <sha256 value="e1f3e17bb4358ccd6c77ca45f70635c9aba237261f19eaa4f64a0218c00e2a3e" origin="Generated by Gradle"/>
@@ -903,6 +980,11 @@
<sha256 value="afb5ea494dd083ed404cd51f580d218e37362f8ae326e893bee521290ed34920" origin="Generated by Gradle"/> <sha256 value="afb5ea494dd083ed404cd51f580d218e37362f8ae326e893bee521290ed34920" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.core" name="core" version="1.8.0">
<artifact name="core-1.8.0.module"> <artifact name="core-1.8.0.module">
<sha256 value="505f1838869611519d65ec7feb650e88856e3682e37e42a2a24b73e655a98d74" origin="Generated by Gradle"/> <sha256 value="505f1838869611519d65ec7feb650e88856e3682e37e42a2a24b73e655a98d74" origin="Generated by Gradle"/>
@@ -916,6 +998,14 @@
<sha256 value="8674ade1d12962469dd2d1e6edc03b465f20d180b0840a129bc40b351e8651d1" origin="Generated by Gradle"/> <sha256 value="8674ade1d12962469dd2d1e6edc03b465f20d180b0840a129bc40b351e8651d1" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.core" name="core-ktx" version="1.1.0">
<artifact name="core-ktx-1.1.0.pom"> <artifact name="core-ktx-1.1.0.pom">
<sha256 value="729d11bad16d87c6212dc9ab80e9c770d333115062136e0732164a6b1696e11c" origin="Generated by Gradle"/> <sha256 value="729d11bad16d87c6212dc9ab80e9c770d333115062136e0732164a6b1696e11c" origin="Generated by Gradle"/>
@@ -1042,6 +1132,14 @@
<sha256 value="92be16cccb757f807bddd92f1fca95a038a810ccca051c4149c11682478954b8" origin="Generated by Gradle"/> <sha256 value="92be16cccb757f807bddd92f1fca95a038a810ccca051c4149c11682478954b8" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.fragment" name="fragment" version="1.2.5">
<artifact name="fragment-1.2.5.pom"> <artifact name="fragment-1.2.5.pom">
<sha256 value="df0aca46b62bb47cc662cbcee63372db6d2a2859478ee38b594fba3433fe30a5" origin="Generated by Gradle"/> <sha256 value="df0aca46b62bb47cc662cbcee63372db6d2a2859478ee38b594fba3433fe30a5" origin="Generated by Gradle"/>
@@ -1100,6 +1198,11 @@
<sha256 value="2baaf7f17551765524bd1e4d2f954a2a0178a112039cf549ba8b7d4b41e66931" origin="Generated by Gradle"/> <sha256 value="2baaf7f17551765524bd1e4d2f954a2a0178a112039cf549ba8b7d4b41e66931" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.lifecycle" name="lifecycle-common" version="2.10.0">
<artifact name="lifecycle-common-2.10.0.module"> <artifact name="lifecycle-common-2.10.0.module">
<sha256 value="5d1247b1edfbdc9ddedcbe525c9d2629567b1c538cb7a9c620cd044a124b5c73" origin="Generated by Gradle"/> <sha256 value="5d1247b1edfbdc9ddedcbe525c9d2629567b1c538cb7a9c620cd044a124b5c73" origin="Generated by Gradle"/>
@@ -1170,6 +1273,11 @@
<sha256 value="1d1837f3942b33ea30aa2301fdcea2639408a0fd6fd4368c22478fa8153aebbf" origin="Generated by Gradle"/> <sha256 value="1d1837f3942b33ea30aa2301fdcea2639408a0fd6fd4368c22478fa8153aebbf" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.lifecycle" name="lifecycle-livedata" version="2.10.0">
<artifact name="lifecycle-livedata-2.10.0.aar"> <artifact name="lifecycle-livedata-2.10.0.aar">
<sha256 value="3e8c009fc88da1c52e4ed05a81269d5ac9bd405046f300307f5e3a17e201a6df" origin="Generated by Gradle"/> <sha256 value="3e8c009fc88da1c52e4ed05a81269d5ac9bd405046f300307f5e3a17e201a6df" origin="Generated by Gradle"/>
@@ -1186,6 +1294,11 @@
<sha256 value="d990bc920ae43729d702d80077328e8a59781ea5a35b24c02bc47d326ae73d7d" origin="Generated by Gradle"/> <sha256 value="d990bc920ae43729d702d80077328e8a59781ea5a35b24c02bc47d326ae73d7d" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.lifecycle" name="lifecycle-livedata" version="2.6.1">
<artifact name="lifecycle-livedata-2.6.1.aar"> <artifact name="lifecycle-livedata-2.6.1.aar">
<sha256 value="67359f609dfc2bf65da1270b23033f856064ec279f058e0a70c715f7c9003031" origin="Generated by Gradle"/> <sha256 value="67359f609dfc2bf65da1270b23033f856064ec279f058e0a70c715f7c9003031" origin="Generated by Gradle"/>
@@ -1194,6 +1307,11 @@
<sha256 value="e2659e87e890b52b66f300471828b387a205e5387a392713190166f399ecf641" origin="Generated by Gradle"/> <sha256 value="e2659e87e890b52b66f300471828b387a205e5387a392713190166f399ecf641" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.lifecycle" name="lifecycle-livedata-core" version="2.10.0">
<artifact name="lifecycle-livedata-core-2.10.0.aar"> <artifact name="lifecycle-livedata-core-2.10.0.aar">
<sha256 value="12dd61a9843ccebb4547d3ebe2f6d030ca8f6988d22f8fad19c0af93b4a97e95" origin="Generated by Gradle"/> <sha256 value="12dd61a9843ccebb4547d3ebe2f6d030ca8f6988d22f8fad19c0af93b4a97e95" origin="Generated by Gradle"/>
@@ -1215,6 +1333,11 @@
<sha256 value="aa51621be3817d1f5d21336b0c0525fe96ae899cec656c13d8ea18a51ea32c2f" origin="Generated by Gradle"/> <sha256 value="aa51621be3817d1f5d21336b0c0525fe96ae899cec656c13d8ea18a51ea32c2f" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.lifecycle" name="lifecycle-livedata-core" version="2.6.1">
<artifact name="lifecycle-livedata-core-2.6.1.aar"> <artifact name="lifecycle-livedata-core-2.6.1.aar">
<sha256 value="2256780a3cff4a1e57fbb3d442557c17dc363ab8af105bcaf5261d8e2d5db949" origin="Generated by Gradle"/> <sha256 value="2256780a3cff4a1e57fbb3d442557c17dc363ab8af105bcaf5261d8e2d5db949" origin="Generated by Gradle"/>
@@ -1563,6 +1686,30 @@
<sha256 value="c978d550808b47434aa49a63164110a50b55b0bcc6160a93a2e37d5110df8c5e" origin="Generated by Gradle"/> <sha256 value="c978d550808b47434aa49a63164110a50b55b0bcc6160a93a2e37d5110df8c5e" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.navigation3" name="navigation3-runtime" version="1.2.0">
<artifact name="navigation3-runtime-1.2.0.module"> <artifact name="navigation3-runtime-1.2.0.module">
<sha256 value="ff14442bf489768bbd2126536921a4d007bdf2d117b2c0451be67cd58eb66a21" origin="Generated by Gradle"/> <sha256 value="ff14442bf489768bbd2126536921a4d007bdf2d117b2c0451be67cd58eb66a21" origin="Generated by Gradle"/>
@@ -1989,6 +2136,19 @@
<sha256 value="6ec8aa998df9b964424c7ab215738237a9b2cc4301da135bb9b8cdc62325fb3e" origin="Generated by Gradle"/> <sha256 value="6ec8aa998df9b964424c7ab215738237a9b2cc4301da135bb9b8cdc62325fb3e" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="androidx.tracing" name="tracing-perfetto" version="1.0.1">
<artifact name="tracing-perfetto-1.0.1.aar"> <artifact name="tracing-perfetto-1.0.1.aar">
<sha256 value="5387298b5bdd8f7d98315fe8720a757056ba0b9b5d9544d2e3fabc26b9ac55a6" origin="Generated by Gradle"/> <sha256 value="5387298b5bdd8f7d98315fe8720a757056ba0b9b5d9544d2e3fabc26b9ac55a6" origin="Generated by Gradle"/>
@@ -2551,6 +2711,11 @@
<sha256 value="05f740c6648165db00cf618dd56c200c4725e358e6d54f5853e0bec15734ea0a" origin="Generated by Gradle"/> <sha256 value="05f740c6648165db00cf618dd56c200c4725e358e6d54f5853e0bec15734ea0a" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.auto.value" name="auto-value-annotations" version="1.6.2">
<artifact name="auto-value-annotations-1.6.2.jar"> <artifact name="auto-value-annotations-1.6.2.jar">
<sha256 value="b48b04ddba40e8ac33bf036f06fc43995fc5084bd94bdaace807ce27d3bea3fb" origin="Generated by Gradle"/> <sha256 value="b48b04ddba40e8ac33bf036f06fc43995fc5084bd94bdaace807ce27d3bea3fb" origin="Generated by Gradle"/>
@@ -2559,11 +2724,24 @@
<sha256 value="1c76cd462fc96e7aa96dc70ce82f0d54063d6df16db35c9c7d9cc0d1a99d3fff" origin="Generated by Gradle"/> <sha256 value="1c76cd462fc96e7aa96dc70ce82f0d54063d6df16db35c9c7d9cc0d1a99d3fff" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.auto.value" name="auto-value-parent" version="1.6.2">
<artifact name="auto-value-parent-1.6.2.pom"> <artifact name="auto-value-parent-1.6.2.pom">
<sha256 value="27b640c82179f5cff62009c0b72033d9bc60f60e9902a66802274b7fe37fc81c" origin="Generated by Gradle"/> <sha256 value="27b640c82179f5cff62009c0b72033d9bc60f60e9902a66802274b7fe37fc81c" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.code.findbugs" name="jsr305" version="3.0.2">
<artifact name="jsr305-3.0.2.jar"> <artifact name="jsr305-3.0.2.jar">
<sha256 value="766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" origin="Generated by Gradle"/> <sha256 value="766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" origin="Generated by Gradle"/>
@@ -2601,6 +2779,14 @@
<sha256 value="265ba959a8e13c3a06133f04b539169c1018daffd4d33f53c453ab4cb386f570" origin="Generated by Gradle"/> <sha256 value="265ba959a8e13c3a06133f04b539169c1018daffd4d33f53c453ab4cb386f570" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.dagger" name="dagger" version="2.60.1">
<artifact name="dagger-2.60.1.jar"> <artifact name="dagger-2.60.1.jar">
<sha256 value="b012d0df527b51bfcd8953d5fcb4d022a45ebe66010d6f181ede68d1572b5cb2" origin="Generated by Gradle"/> <sha256 value="b012d0df527b51bfcd8953d5fcb4d022a45ebe66010d6f181ede68d1572b5cb2" origin="Generated by Gradle"/>
@@ -2716,6 +2902,9 @@
</artifact> </artifact>
</component> </component>
<component group="com.google.errorprone" name="error_prone_annotations" version="2.18.0"> <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"> <artifact name="error_prone_annotations-2.18.0.pom">
<sha256 value="920135797dcca5917b5a5c017642a58d340a4cd1bcd12f56f892a5663bd7bddc" origin="Generated by Gradle"/> <sha256 value="920135797dcca5917b5a5c017642a58d340a4cd1bcd12f56f892a5663bd7bddc" origin="Generated by Gradle"/>
</artifact> </artifact>
@@ -2725,6 +2914,14 @@
<sha256 value="4ca5a35d61235e16549ac346d1e34551cdf0fe27e84aa57e03cbeb255ea4e5da" origin="Generated by Gradle"/> <sha256 value="4ca5a35d61235e16549ac346d1e34551cdf0fe27e84aa57e03cbeb255ea4e5da" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.errorprone" name="error_prone_annotations" version="2.3.1">
<artifact name="error_prone_annotations-2.3.1.pom"> <artifact name="error_prone_annotations-2.3.1.pom">
<sha256 value="3edce6b711ba368efe16b9b7aacb0214fbd648414cb9b965953a2e7ed89a819a" origin="Generated by Gradle"/> <sha256 value="3edce6b711ba368efe16b9b7aacb0214fbd648414cb9b965953a2e7ed89a819a" origin="Generated by Gradle"/>
@@ -2764,6 +2961,11 @@
<sha256 value="fa81829d049559df6927f9c9a6fd6bbd09f8b50e6d4736ae72c8300b6c3d7654" origin="Generated by Gradle"/> <sha256 value="fa81829d049559df6927f9c9a6fd6bbd09f8b50e6d4736ae72c8300b6c3d7654" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.errorprone" name="error_prone_parent" version="2.3.1">
<artifact name="error_prone_parent-2.3.1.pom"> <artifact name="error_prone_parent-2.3.1.pom">
<sha256 value="767525d9a81129cd081968382980336327be4162b1e2251a182911daa733c123" origin="Generated by Gradle"/> <sha256 value="767525d9a81129cd081968382980336327be4162b1e2251a182911daa733c123" origin="Generated by Gradle"/>
@@ -2837,6 +3039,25 @@
<sha256 value="13d4e2998aac652fc6d7b9eaf3e495fa2952bf98ef68a2c49aa5eaa0bbe731a9" origin="Generated by Gradle"/> <sha256 value="13d4e2998aac652fc6d7b9eaf3e495fa2952bf98ef68a2c49aa5eaa0bbe731a9" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.guava" name="guava" version="33.4.0-jre">
<artifact name="guava-33.4.0-jre.jar"> <artifact name="guava-33.4.0-jre.jar">
<sha256 value="b918c98a7e44dbe94ebd9fe3e40cddaadb5a93e6a78eb6008b42df237241e538" origin="Generated by Gradle"/> <sha256 value="b918c98a7e44dbe94ebd9fe3e40cddaadb5a93e6a78eb6008b42df237241e538" origin="Generated by Gradle"/>
@@ -2863,6 +3084,16 @@
<sha256 value="30a51d34b075c9b4eb6a2da3fba6c1f5047b45225a3a03aebcefc0b7880e5735" origin="Generated by Gradle"/> <sha256 value="30a51d34b075c9b4eb6a2da3fba6c1f5047b45225a3a03aebcefc0b7880e5735" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.guava" name="guava-parent" version="33.4.0-android">
<artifact name="guava-parent-33.4.0-android.pom"> <artifact name="guava-parent-33.4.0-android.pom">
<sha256 value="7220ede61026596fbc720ee4b93246fa2d14f328058532b59ef053de397c7d83" origin="Generated by Gradle"/> <sha256 value="7220ede61026596fbc720ee4b93246fa2d14f328058532b59ef053de397c7d83" origin="Generated by Gradle"/>
@@ -2903,6 +3134,9 @@
</artifact> </artifact>
</component> </component>
<component group="com.google.j2objc" name="j2objc-annotations" version="2.8"> <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"> <artifact name="j2objc-annotations-2.8.pom">
<sha256 value="37f87798b18385113c918bfa9e1276fe50735ef8fa849b5800c519d54dbf11f8" origin="Generated by Gradle"/> <sha256 value="37f87798b18385113c918bfa9e1276fe50735ef8fa849b5800c519d54dbf11f8" origin="Generated by Gradle"/>
</artifact> </artifact>
@@ -3610,6 +3844,14 @@
<sha256 value="66f563d8a2a1ee395a1283992fd646347f42054d071592d1f477523816e397fa" origin="Generated by Gradle"/> <sha256 value="66f563d8a2a1ee395a1283992fd646347f42054d071592d1f477523816e397fa" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="org.checkerframework" name="checker-qual" version="3.43.0">
<artifact name="checker-qual-3.43.0.jar"> <artifact name="checker-qual-3.43.0.jar">
<sha256 value="3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" origin="Generated by Gradle"/> <sha256 value="3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6" origin="Generated by Gradle"/>
@@ -3768,6 +4010,11 @@
<sha256 value="3090b4e873054cdc29991ae94c92fa45afb4c145d460441d803baf6ef165bbb8" origin="Generated by Gradle"/> <sha256 value="3090b4e873054cdc29991ae94c92fa45afb4c145d460441d803baf6ef165bbb8" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="org.jetbrains.kotlin" name="kotlin-bom" version="2.3.21">
<artifact name="kotlin-bom-2.3.21.pom"> <artifact name="kotlin-bom-2.3.21.pom">
<sha256 value="950704bad04c5143656cfca1d2c574f25fcad9cac33905ab22a57e9fdf7bfd88" origin="Generated by Gradle"/> <sha256 value="950704bad04c5143656cfca1d2c574f25fcad9cac33905ab22a57e9fdf7bfd88" origin="Generated by Gradle"/>
@@ -4400,6 +4647,19 @@
<sha256 value="ee66712b1006e971636f11554af63c56835caf315382ce23e54f59f5b8b9d4f9" origin="Generated by Gradle"/> <sha256 value="ee66712b1006e971636f11554af63c56835caf315382ce23e54f59f5b8b9d4f9" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-android" version="1.11.0">
<artifact name="kotlinx-coroutines-android-1.11.0.jar"> <artifact name="kotlinx-coroutines-android-1.11.0.jar">
<sha256 value="c2cb206d27017c7d1bf5ff179787397543d13748dbabb0d7237e1585e0b29044" origin="Generated by Gradle"/> <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-android:1.5.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.benchmark:benchmark-traceprocessor: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.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-jvm:1.6.0=benchmarkTestedApks,debugTestedApks
androidx.collection:collection:1.0.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath androidx.collection:collection:1.0.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.collection:collection:1.6.0=benchmarkTestedApks,debugTestedApks 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-util:1.12.1=benchmarkTestedApks,debugTestedApks
androidx.compose.ui:ui:1.12.1=benchmarkTestedApks,debugTestedApks androidx.compose.ui:ui:1.12.1=benchmarkTestedApks,debugTestedApks
androidx.compose:compose-bom:2026.09.00=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-ktx:1.2.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.concurrent:concurrent-futures:1.1.0=benchmarkTestedApks,debugTestedApks androidx.concurrent:concurrent-futures:1.1.0=benchmarkTestedApks,debugTestedApks
androidx.concurrent:concurrent-futures:1.2.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath 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-ktx:1.18.0=benchmarkTestedApks,debugTestedApks
androidx.core:core-viewtree:1.0.0=benchmarkTestedApks,debugTestedApks androidx.core:core-viewtree:1.0.0=benchmarkTestedApks,debugTestedApks
androidx.core:core:1.18.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.cursoradapter:cursoradapter:1.0.0=benchmarkTestedApks,debugTestedApks
androidx.customview:customview:1.0.0=benchmarkTestedApks,debugTestedApks androidx.customview:customview:1.0.0=benchmarkTestedApks,debugTestedApks
androidx.drawerlayout:drawerlayout: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.fragment:fragment:1.9.1=benchmarkTestedApks,debugTestedApks
androidx.interpolator:interpolator:1.0.0=benchmarkTestedApks,debugTestedApks androidx.interpolator:interpolator:1.0.0=benchmarkTestedApks,debugTestedApks
androidx.lifecycle:lifecycle-common-jvm:2.11.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-savedstate:2.11.0=benchmarkTestedApks,debugTestedApks
androidx.lifecycle:lifecycle-viewmodel:2.11.0=benchmarkTestedApks,debugTestedApks androidx.lifecycle:lifecycle-viewmodel:2.11.0=benchmarkTestedApks,debugTestedApks
androidx.loader:loader:1.0.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-desktop:1.2.0=benchmarkTestedApks,debugTestedApks
androidx.navigation3:navigation3-runtime:1.2.0=benchmarkTestedApks,debugTestedApks androidx.navigation3:navigation3-runtime:1.2.0=benchmarkTestedApks,debugTestedApks
androidx.navigation3:navigation3-ui-jvmstubs: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.test:runner:1.7.0=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.tracing:tracing-desktop:2.0.0=benchmarkTestedApks,debugTestedApks androidx.tracing:tracing-desktop:2.0.0=benchmarkTestedApks,debugTestedApks
androidx.tracing:tracing-ktx:1.1.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath 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-binary:1.0.1=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
androidx.tracing:tracing-perfetto-handshake: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 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.android.tools:sdklib:32.4.1=androidLintTool
com.github.ajalt.clikt:clikt-jvm:5.0.3=ktlint com.github.ajalt.clikt:clikt-jvm:5.0.3=ktlint
com.github.ajalt.clikt:clikt: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.findbugs:jsr305:3.0.2=androidLintTool,benchmarkTestedApks,debugTestedApks
com.google.code.gson:gson:2.11.0=androidLintTool com.google.code.gson:gson:2.11.0=androidLintTool
com.google.dagger:dagger-lint-aar:2.60.1=benchmarkTestedApks,debugTestedApks com.google.dagger:dagger-lint-aar:2.60.1=benchmarkTestedApks,debugTestedApks
com.google.dagger:dagger: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-android:2.60.1=benchmarkTestedApks,debugTestedApks
com.google.dagger:hilt-core: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.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:guava:33.4.0-jre=androidLintTool
com.google.guava:listenablefuture:1.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,benchmarkTestedApks,debugLintChecksClasspath,debugRuntimeClasspath,debugTestedApks com.google.guava:listenablefuture:1.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=androidLintTool com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=androidLintTool,benchmarkTestedApks,debugTestedApks
com.google.j2objc:j2objc-annotations:3.0.0=androidLintTool com.google.j2objc:j2objc-annotations:3.0.0=androidLintTool,benchmarkTestedApks,debugTestedApks
com.google.jimfs:jimfs:1.1=androidLintTool com.google.jimfs:jimfs:1.1=androidLintTool
com.google.protobuf:protobuf-java:3.25.5=androidLintTool,unified-test-platform-gradle-work-action 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 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:bcpkix-jdk18on:1.80.2=androidLintTool
org.bouncycastle:bcprov-jdk18on:1.80.2=androidLintTool org.bouncycastle:bcprov-jdk18on:1.80.2=androidLintTool
org.bouncycastle:bcutil-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.ec4j.core:ec4j-core:1.1.1=ktlint,ktlintBaselineReporter,ktlintRuleset
org.glassfish.jaxb:jaxb-runtime:2.3.2=androidLintTool org.glassfish.jaxb:jaxb-runtime:2.3.2=androidLintTool
org.glassfish.jaxb:txw2: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.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.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath 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 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.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.4.21=benchmarkCompileClasspath,benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,benchmarkTestedApks,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugTestedApks,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath 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.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.11.0=benchmarkTestedApks,debugTestedApks
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0=benchmarkLintChecksClasspath,benchmarkRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.11.0=benchmarkTestedApks,debugTestedApks org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.11.0=benchmarkTestedApks,debugTestedApks
+3
View File
@@ -40,6 +40,9 @@ kotlin {
dependencies { dependencies {
implementation(project(":core")) implementation(project(":core"))
implementation(libs.camera.camera2)
implementation(libs.camera.lifecycle)
implementation(libs.camera.view)
implementation(libs.biometric) implementation(libs.biometric)
implementation(libs.fragment) implementation(libs.fragment)
testImplementation(libs.junit) 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-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.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.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.4.2=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
androidx.collection:collection-jvm:1.6.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath 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.4.2=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
androidx.collection:collection:1.6.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath 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-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.1.0=debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.concurrent:concurrent-futures:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath 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.13.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
androidx.core:core-ktx:1.9.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath 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 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.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.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.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.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.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 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-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.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.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.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-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 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:monitor:1.8.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath
androidx.test:runner:1.7.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-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:1.1.0=debugAndroidTestCompileClasspath
androidx.tracing:tracing:2.0.0=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath 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 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.android.tools:sdklib:32.4.1=androidLintTool
com.github.ajalt.clikt:clikt-jvm:5.0.3=ktlint com.github.ajalt.clikt:clikt-jvm:5.0.3=ktlint
com.github.ajalt.clikt:clikt: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.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.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: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,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=androidLintTool com.google.j2objc:j2objc-annotations:2.8=debugAndroidTestCompileClasspath,debugCompileClasspath,debugUnitTestCompileClasspath,releaseCompileClasspath
com.google.j2objc:j2objc-annotations:3.0.0=androidLintTool 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.jimfs:jimfs:1.1=androidLintTool
com.google.protobuf:protobuf-java:3.25.5=androidLintTool,unified-test-platform-gradle-work-action 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 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-jvm:7.0.7=ktlint,ktlintBaselineReporter,ktlintReporter,ktlintRuleset
io.github.oshai:kotlin-logging:5.1.0=ktlint,ktlintBaselineReporter,ktlintReporter io.github.oshai:kotlin-logging:5.1.0=ktlint,ktlintBaselineReporter,ktlintReporter
jakarta.activation:jakarta.activation-api:1.2.1=androidLintTool 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 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 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-platform:5.6.0=androidLintTool
net.java.dev.jna:jna:5.19.1=debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath 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:bcpkix-jdk18on:1.80.2=androidLintTool
org.bouncycastle:bcprov-jdk18on:1.80.2=androidLintTool org.bouncycastle:bcprov-jdk18on:1.80.2=androidLintTool
org.bouncycastle:bcutil-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.ec4j.core:ec4j-core:1.1.1=ktlint,ktlintBaselineReporter,ktlintRuleset
org.glassfish.jaxb:jaxb-runtime:2.3.2=androidLintTool org.glassfish.jaxb:jaxb-runtime:2.3.2=androidLintTool
org.glassfish.jaxb:txw2: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-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.0=kotlinAbiValidationCompatClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath 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 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.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-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.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.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-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 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"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.CAMERA" />
<application> <application>
<activity android:name="de.rfc1437.ironstorage.platform.KeystoreTestActivity" android:exported="false" android:theme="@android:style/Theme.Material.Light.NoActionBar" /> <activity android:name="de.rfc1437.ironstorage.platform.KeystoreTestActivity" android:exported="false" android:theme="@android:style/Theme.Material.Light.NoActionBar" />
</application> </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() 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 { override fun chooseQrImages(): Boolean {
val picker = picker ?: return false val picker = picker ?: return false
return try { return try {
@@ -101,6 +101,24 @@ impl MobileAndroidOnboarding {
.import(SecretBytes::new(armor.into_bytes()), secret(passphrase))? .import(SecretBytes::new(armor.into_bytes()), secret(passphrase))?
.into()) .into())
} }
pub fn import_scanned(
&self,
transfer: Arc<crate::MobileKeyTransferImport>,
passphrase: Option<String>,
) -> Result<MobileAndroidSetupStatus, crate::MobileKeyTransferFfiError> {
let importer =
transfer
.importer
.lock()
.map_err(|_| crate::MobileKeyTransferFfiError::Failed {
kind: crate::MobileKeyTransferErrorKind::Failed,
message: "the key-transfer session is unavailable".to_owned(),
})?;
Ok(self
.setup
.import_scanned(&importer, secret(passphrase))?
.into())
}
pub fn setup_local( pub fn setup_local(
&self, &self,
fingerprint: String, fingerprint: String,
+29
View File
@@ -1581,6 +1581,10 @@ pub struct MobileKeyTransfer {
#[uniffi::export] #[uniffi::export]
impl MobileKeyTransfer { impl MobileKeyTransfer {
pub fn export_visible(&self) -> bool {
self.service.export_visible()
}
pub fn keys(&self) -> Vec<MobileKeyTransferKey> { pub fn keys(&self) -> Vec<MobileKeyTransferKey> {
self.service.keys().iter().map(Into::into).collect() self.service.keys().iter().map(Into::into).collect()
} }
@@ -1635,6 +1639,31 @@ pub struct MobileKeyTransferImport {
#[uniffi::export] #[uniffi::export]
impl MobileKeyTransferImport { impl MobileKeyTransferImport {
pub fn add_camera(
&self,
frame: platform::MobileCameraFrame,
rotation_degrees: u32,
) -> Result<Option<MobileKeyTransferProgress>, MobileKeyTransferFfiError> {
self.importer
.lock()
.map_err(|_| MobileKeyTransferFfiError::Failed {
kind: MobileKeyTransferErrorKind::Failed,
message: "the key-transfer session is unavailable".to_owned(),
})?
.add_camera(
ironstorage::mobile_platform::MobileCameraFrame {
luminance: ironstorage::repository::SecretBytes::new(frame.luminance),
width: frame.width,
height: frame.height,
row_stride: frame.row_stride,
pixel_stride: frame.pixel_stride,
},
rotation_degrees,
)
.map(|progress| progress.map(Into::into))
.map_err(Into::into)
}
pub fn add_image( pub fn add_image(
&self, &self,
image: Vec<u8>, image: Vec<u8>,
+178 -25
View File
@@ -1,6 +1,9 @@
//! Storage-owned OpenPGP key transfer for native mobile QR adapters. //! Storage-owned OpenPGP key transfer for native mobile QR adapters.
use std::{collections::BTreeMap, error::Error, fmt, fs, io::Write as _, path::Path}; use std::{
collections::BTreeMap, error::Error, fmt, fs, io::Write as _, path::Path, sync::Mutex,
time::Instant,
};
use crate::atomic_file::TempFile; use crate::atomic_file::TempFile;
use cap_std::{ambient_authority, fs::Dir}; use cap_std::{ambient_authority, fs::Dir};
@@ -17,6 +20,9 @@ use crate::{
const FRAME_PREFIX: &str = "ISKT1"; const FRAME_PREFIX: &str = "ISKT1";
const FRAME_CHUNK_BYTES: usize = 1_600; const FRAME_CHUNK_BYTES: usize = 1_600;
const MAX_TRANSFER_FRAMES: usize = 11_000; const MAX_TRANSFER_FRAMES: usize = 11_000;
const MAX_TRANSFER_BYTES: usize = MAX_TRANSFER_FRAMES * FRAME_CHUNK_BYTES;
// Bound eager QR matrices to under 8 MiB on lower-memory mobile clients.
const MAX_EXPORT_FRAMES: usize = 256;
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MobileKeyTransferKind { pub enum MobileKeyTransferKind {
@@ -140,13 +146,18 @@ impl MobileKeyTransferOutcome {
pub struct MobileKeyTransferService { pub struct MobileKeyTransferService {
config: Config, config: Config,
keys: KeyStore, keys: KeyStore,
export_deadline: Mutex<Option<Instant>>,
} }
impl MobileKeyTransferService { impl MobileKeyTransferService {
pub fn load() -> Result<Self, MobileKeyTransferError> { pub fn load() -> Result<Self, MobileKeyTransferError> {
let config = Config::load(None)?; let config = Config::load(None)?;
let keys = load_keys_or_empty(config.key_material())?; let keys = load_keys_or_empty(config.key_material())?;
Ok(Self { config, keys }) Ok(Self {
config,
keys,
export_deadline: Mutex::new(None),
})
} }
pub fn keys(&self) -> Vec<MobileKeyTransferKey> { pub fn keys(&self) -> Vec<MobileKeyTransferKey> {
@@ -169,6 +180,11 @@ impl MobileKeyTransferService {
.find(|key| key.fingerprint().as_str() == fingerprint) .find(|key| key.fingerprint().as_str() == fingerprint)
.ok_or(MobileKeyTransferError::KeyUnavailable)?; .ok_or(MobileKeyTransferError::KeyUnavailable)?;
let frames = encode_frames(armor)?; let frames = encode_frames(armor)?;
*self
.export_deadline
.lock()
.map_err(|_| MobileKeyTransferError::KeyUnavailable)? =
Some(Instant::now() + self.config.authentication_timeout().duration());
Ok(MobileKeyTransferExport { Ok(MobileKeyTransferExport {
key: transfer_key_with_kind(&info, kind), key: transfer_key_with_kind(&info, kind),
frames, frames,
@@ -178,13 +194,20 @@ impl MobileKeyTransferService {
pub fn importer(&self) -> MobileKeyTransferImport { pub fn importer(&self) -> MobileKeyTransferImport {
MobileKeyTransferImport::new(self.config.clone()) MobileKeyTransferImport::new(self.config.clone())
} }
pub fn export_visible(&self) -> bool {
self.export_deadline
.lock()
.ok()
.is_some_and(|deadline| deadline.is_some_and(|deadline| Instant::now() < deadline))
}
} }
pub struct MobileKeyTransferImport { pub struct MobileKeyTransferImport {
destination: MobileKeyTransferDestination, destination: MobileKeyTransferDestination,
digest: Option<String>, digest: Option<String>,
total: Option<usize>, total: Option<usize>,
chunks: BTreeMap<usize, Vec<u8>>, chunks: BTreeMap<usize, SecretBytes>,
complete: Option<SecretBytes>, complete: Option<SecretBytes>,
key: Option<MobileKeyTransferKey>, key: Option<MobileKeyTransferKey>,
} }
@@ -195,6 +218,31 @@ enum MobileKeyTransferDestination {
} }
impl MobileKeyTransferImport { impl MobileKeyTransferImport {
pub fn add_camera(
&mut self,
frame: crate::mobile_platform::MobileCameraFrame,
rotation_degrees: u32,
) -> Result<Option<MobileKeyTransferProgress>, MobileKeyTransferError> {
if frame.width > 2048
|| frame.height > 2048
|| frame.luminance.expose().len() > 4 * 1024 * 1024
{
return Err(MobileKeyTransferError::InvalidFrame);
}
match frame.decode_rotated(rotation_degrees) {
Ok(payload) => self.add_frame(payload).map(Some),
Err(QrError::NotFound) => Ok(None),
Err(_) => Err(MobileKeyTransferError::InvalidFrame),
}
}
pub(crate) fn completed_armor(&self) -> Result<SecretBytes, MobileKeyTransferError> {
self.complete
.as_ref()
.map(|armor| SecretBytes::new(armor.expose().to_vec()))
.ok_or(MobileKeyTransferError::IncompleteTransfer)
}
fn new(config: Config) -> Self { fn new(config: Config) -> Self {
Self { Self {
destination: MobileKeyTransferDestination::Configured(Box::new(config)), destination: MobileKeyTransferDestination::Configured(Box::new(config)),
@@ -230,6 +278,9 @@ impl MobileKeyTransferImport {
&mut self, &mut self,
payload: SecretBytes, payload: SecretBytes,
) -> Result<MobileKeyTransferProgress, MobileKeyTransferError> { ) -> Result<MobileKeyTransferProgress, MobileKeyTransferError> {
if payload.expose().len() > MAX_TRANSFER_BYTES {
return Err(MobileKeyTransferError::InvalidFrame);
}
if self.complete.is_some() { if self.complete.is_some() {
return Err(MobileKeyTransferError::AlreadyComplete); return Err(MobileKeyTransferError::AlreadyComplete);
} }
@@ -251,52 +302,58 @@ impl MobileKeyTransferImport {
if digest.len() != 64 || !digest.bytes().all(|byte| byte.is_ascii_hexdigit()) { if digest.len() != 64 || !digest.bytes().all(|byte| byte.is_ascii_hexdigit()) {
return Err(MobileKeyTransferError::InvalidFrame); return Err(MobileKeyTransferError::InvalidFrame);
} }
let digest = digest.to_ascii_lowercase();
let sequence = parse_positive(fields.next())?; let sequence = parse_positive(fields.next())?;
let total = parse_positive(fields.next())?; let total = parse_positive(fields.next())?;
if sequence > total || total > MAX_TRANSFER_FRAMES { if sequence > total || total > MAX_TRANSFER_FRAMES {
return Err(MobileKeyTransferError::InvalidFrame); return Err(MobileKeyTransferError::InvalidFrame);
} }
let chunk = BASE64URL_NOPAD let encoded = fields.next().ok_or(MobileKeyTransferError::InvalidFrame)?;
.decode( if encoded.len() > FRAME_CHUNK_BYTES.div_ceil(3) * 4 {
fields return Err(MobileKeyTransferError::InvalidFrame);
.next() }
.ok_or(MobileKeyTransferError::InvalidFrame)? let chunk = SecretBytes::new(
.as_bytes(), BASE64URL_NOPAD
) .decode(encoded.as_bytes())
.map_err(|_| MobileKeyTransferError::InvalidFrame)?; .map_err(|_| MobileKeyTransferError::InvalidFrame)?,
if chunk.is_empty() || chunk.len() > FRAME_CHUNK_BYTES { );
if chunk.expose().is_empty() || chunk.expose().len() > FRAME_CHUNK_BYTES {
return Err(MobileKeyTransferError::InvalidFrame); return Err(MobileKeyTransferError::InvalidFrame);
} }
if self.digest.as_deref().is_some_and(|value| value != digest) if self
.digest
.as_deref()
.is_some_and(|value| value != digest.as_str())
|| self.total.is_some_and(|value| value != total) || self.total.is_some_and(|value| value != total)
{ {
return Err(MobileKeyTransferError::MixedTransfers); return Err(MobileKeyTransferError::MixedTransfers);
} }
self.digest self.digest.get_or_insert(digest);
.get_or_insert_with(|| digest.to_ascii_lowercase());
self.total.get_or_insert(total); self.total.get_or_insert(total);
if let Some(existing) = self.chunks.get(&sequence) { if let Some(existing) = self.chunks.get(&sequence) {
if existing == &chunk { if existing.expose() == chunk.expose() {
return Ok(self.progress(true)); return Ok(self.progress(true));
} }
return Err(MobileKeyTransferError::ConflictingFrame); return Err(MobileKeyTransferError::ConflictingFrame);
} }
self.chunks.insert(sequence, chunk); self.chunks.insert(sequence, chunk);
if self.chunks.len() == total { if self.chunks.len() == total {
let mut armor = Vec::new(); let mut armor = zeroize::Zeroizing::new(Vec::new());
for sequence in 1..=total { for sequence in 1..=total {
armor.extend( armor.extend_from_slice(
self.chunks self.chunks
.get(&sequence) .get(&sequence)
.ok_or(MobileKeyTransferError::IncompleteTransfer)?, .ok_or(MobileKeyTransferError::IncompleteTransfer)?
.expose(),
); );
} }
let actual = HEXLOWER.encode(&Sha256::digest(&armor)); let actual = HEXLOWER.encode(&Sha256::digest(armor.as_slice()));
if self.digest.as_deref() != Some(actual.as_str()) { if self.digest.as_deref() != Some(actual.as_str()) {
return Err(MobileKeyTransferError::ChecksumMismatch); return Err(MobileKeyTransferError::ChecksumMismatch);
} }
self.finish(SecretBytes::new(armor))?; self.finish(SecretBytes::new(std::mem::take(&mut *armor)))?;
self.chunks.clear();
} }
Ok(self.progress(false)) Ok(self.progress(false))
} }
@@ -435,7 +492,11 @@ impl MobileKeyTransferImport {
fn progress(&self, duplicate: bool) -> MobileKeyTransferProgress { fn progress(&self, duplicate: bool) -> MobileKeyTransferProgress {
MobileKeyTransferProgress { MobileKeyTransferProgress {
received: self.chunks.len().max(usize::from(self.complete.is_some())), received: if self.complete.is_some() {
self.total.unwrap_or(1)
} else {
self.chunks.len()
},
total: self.total.unwrap_or(1), total: self.total.unwrap_or(1),
duplicate, duplicate,
key: self.key.clone(), key: self.key.clone(),
@@ -576,7 +637,7 @@ fn encode_multipart(
} }
let digest = HEXLOWER.encode(&Sha256::digest(armor.expose())); let digest = HEXLOWER.encode(&Sha256::digest(armor.expose()));
let total = armor.expose().len().div_ceil(chunk_bytes); let total = armor.expose().len().div_ceil(chunk_bytes);
if total == 0 || total > MAX_TRANSFER_FRAMES { if total == 0 || total > MAX_EXPORT_FRAMES {
return Err(MobileKeyTransferError::QrPayload); return Err(MobileKeyTransferError::QrPayload);
} }
let mut frames = Vec::with_capacity(total); let mut frames = Vec::with_capacity(total);
@@ -706,6 +767,7 @@ mod tests {
MobileKeyTransferService, encode_multipart, load_keys_or_empty, MobileKeyTransferService, encode_multipart, load_keys_or_empty,
}; };
use crate::{config::Config, crypto::KeyStore, repository::SecretBytes}; use crate::{config::Config, crypto::KeyStore, repository::SecretBytes};
use std::{sync::Mutex, time::Instant};
use tempfile::tempdir; use tempfile::tempdir;
const ALICE_PUBLIC: &[u8] = const ALICE_PUBLIC: &[u8] =
@@ -714,6 +776,89 @@ mod tests {
include_bytes!("../tests/fixtures/compatibility/keys/alice-secret.asc"); include_bytes!("../tests/fixtures/compatibility/keys/alice-secret.asc");
const ALICE_FINGERPRINT: &str = "7E5C5241B25F6FFAAD717EBFA132DCB2DC23AE30"; const ALICE_FINGERPRINT: &str = "7E5C5241B25F6FFAAD717EBFA132DCB2DC23AE30";
#[test]
fn export_rejects_oversized_matrix_sets_before_rendering() {
assert!(matches!(
encode_multipart(SecretBytes::new(vec![0; 257]), 1),
Err(MobileKeyTransferError::QrPayload)
));
}
#[test]
fn export_visibility_is_bounded_and_does_not_renew_while_reading() {
let fixture = configured_secret_key().unwrap();
let service = MobileKeyTransferService {
keys: load_keys_or_empty(fixture.config.key_material()).unwrap(),
config: fixture.config,
export_deadline: Mutex::new(None),
};
assert!(!service.export_visible());
service
.export(ALICE_FINGERPRINT, MobileKeyTransferKind::Public, None)
.unwrap();
assert!(service.export_visible());
*service.export_deadline.lock().unwrap() = Some(Instant::now());
assert!(!service.export_visible());
assert!(!service.export_visible());
}
#[test]
fn camera_multipart_is_exact_duplicate_safe_and_transactional()
-> Result<(), Box<dyn std::error::Error>> {
let fixture = configured_public_key()?;
let mut importer = MobileKeyTransferImport::new(fixture.config.clone());
let mut frames = encode_multipart(SecretBytes::new(ALICE_SECRET.to_vec()), 128)?;
frames.reverse();
for (index, frame) in frames.iter().enumerate() {
let matrix = frame.matrix();
let width = (matrix.width() + 8) * 4;
let height = width + 8;
let row = width * 2 + 13;
let mut pixels = vec![255; height * row];
for y in 0..height {
for x in 0..width {
let dark = x / 4 >= 4
&& y / 4 >= 4
&& matrix.is_dark(x / 4 - 4, y / 4 - 4).unwrap_or(false);
pixels[y * row + x * 2] = if dark { 0 } else { 255 };
}
}
let camera = || crate::mobile_platform::MobileCameraFrame {
width: width as u32,
height: height as u32,
row_stride: row as u32,
pixel_stride: 2,
luminance: SecretBytes::new(pixels.clone()),
};
let progress = importer
.add_camera(camera(), [0, 90, 180, 270][index % 4])?
.unwrap();
assert_eq!(progress.received(), index + 1);
if index + 1 < frames.len() {
assert!(importer.add_camera(camera(), 0)?.unwrap().duplicate());
assert_eq!(
importer.import(None, true, false),
Err(MobileKeyTransferError::IncompleteTransfer)
);
}
}
assert_eq!(importer.completed_armor()?.expose(), ALICE_SECRET);
assert_eq!(
importer.import(Some(SecretBytes::new(b"wrong".to_vec())), true, false),
Err(MobileKeyTransferError::IncorrectPassphrase)
);
let before = std::fs::read(fixture.key_path.join("alice-public.asc"))?;
importer.import(
Some(SecretBytes::new(b"fixture-alice-passphrase".to_vec())),
true,
false,
)?;
assert_eq!(before, ALICE_PUBLIC);
let keys = KeyStore::load(fixture.config.key_material())?;
assert!(keys.infos().any(|key| key.has_secret()));
Ok(())
}
#[test] #[test]
fn qr_images_reconstruct_the_same_key_and_reject_invalid_images() fn qr_images_reconstruct_the_same_key_and_reject_invalid_images()
-> Result<(), Box<dyn std::error::Error>> { -> Result<(), Box<dyn std::error::Error>> {
@@ -750,8 +895,14 @@ mod tests {
let mut importer = MobileKeyTransferImport::new(fixture.config); let mut importer = MobileKeyTransferImport::new(fixture.config);
let mut frames = encode_multipart(SecretBytes::new(ALICE_SECRET.to_vec()), 128)?; let mut frames = encode_multipart(SecretBytes::new(ALICE_SECRET.to_vec()), 128)?;
frames.reverse(); frames.reverse();
for frame in frames { for (index, frame) in frames.into_iter().enumerate() {
importer.add_frame(SecretBytes::new(frame.payload().to_vec()))?; let mut payload = frame.payload().to_vec();
if index % 2 == 0 {
for byte in &mut payload[6..70] {
byte.make_ascii_uppercase();
}
}
importer.add_frame(SecretBytes::new(payload))?;
} }
assert_eq!( assert_eq!(
importer.complete.as_ref().map(SecretBytes::expose), importer.complete.as_ref().map(SecretBytes::expose),
@@ -769,6 +920,7 @@ mod tests {
-> Result<(), Box<dyn std::error::Error>> { -> Result<(), Box<dyn std::error::Error>> {
let fixture = configured_secret_key()?; let fixture = configured_secret_key()?;
let service = MobileKeyTransferService { let service = MobileKeyTransferService {
export_deadline: Mutex::new(None),
config: fixture.config.clone(), config: fixture.config.clone(),
keys: KeyStore::load(&fixture.key_path)?, keys: KeyStore::load(&fixture.key_path)?,
}; };
@@ -953,6 +1105,7 @@ mod tests {
let fixture = configured_key("placeholder", b"")?; let fixture = configured_key("placeholder", b"")?;
std::fs::remove_file(fixture.key_path.join("placeholder"))?; std::fs::remove_file(fixture.key_path.join("placeholder"))?;
let service = MobileKeyTransferService { let service = MobileKeyTransferService {
export_deadline: Mutex::new(None),
keys: load_keys_or_empty(&fixture.key_path)?, keys: load_keys_or_empty(&fixture.key_path)?,
config: fixture.config.clone(), config: fixture.config.clone(),
}; };
@@ -163,6 +163,15 @@ impl AndroidOnboarding {
self.status_unlocked() self.status_unlocked()
} }
pub fn import_scanned(
&self,
transfer: &crate::mobile_key_transfer::MobileKeyTransferImport,
passphrase: Option<SecretBytes>,
) -> Result<AndroidOnboardingStatus, crate::mobile_key_transfer::MobileKeyTransferError> {
self.import(transfer.completed_armor()?, passphrase)
.map_err(crate::mobile_key_transfer::MobileKeyTransferError::LocalSetup)
}
fn stage_key( fn stage_key(
&self, &self,
fingerprint: &str, fingerprint: &str,
@@ -1052,6 +1061,40 @@ pub(crate) mod tests {
Ok(true) Ok(true)
} }
} }
#[test]
fn scanned_import_stages_exact_key_without_creating_a_store() {
use crate::mobile_key_transfer::{MobileKeyTransferError, MobileKeyTransferImport};
let temp = tempfile::tempdir().unwrap();
let private = storage(temp.path());
let backend = Arc::new(Backend::default());
let setup = AndroidOnboarding::new(&private, backend).unwrap();
let mut transfer = MobileKeyTransferImport::local_setup();
assert!(matches!(
setup.import_scanned(&transfer, password()),
Err(MobileKeyTransferError::IncompleteTransfer)
));
transfer
.add_frame(SecretBytes::new(
include_bytes!("../../tests/fixtures/compatibility/keys/alice-secret.asc").to_vec(),
))
.unwrap();
assert!(
setup
.import_scanned(&transfer, Some(SecretBytes::new(b"wrong".to_vec())))
.is_err()
);
assert!(setup.status().unwrap().keys.is_empty());
let status = setup.import_scanned(&transfer, password()).unwrap();
assert_eq!(status.keys[0].fingerprint().as_str(), ALICE);
assert!(!status.configured);
assert!(setup.configuration().unwrap().is_none());
assert!(matches!(
setup.import_scanned(&transfer, password()),
Err(MobileKeyTransferError::LocalSetup(_))
));
assert_eq!(setup.status().unwrap().keys.len(), 1);
}
#[test] #[test]
fn imported_key_restarts_then_creates_a_pass_compatible_local_store() { fn imported_key_restarts_then_creates_a_pass_compatible_local_store() {
let temp = tempfile::tempdir().unwrap(); let temp = tempfile::tempdir().unwrap();
+74 -1
View File
@@ -90,6 +90,10 @@ pub struct MobileCameraFrame {
impl MobileCameraFrame { impl MobileCameraFrame {
pub fn decode(&self) -> Result<SecretBytes, QrError> { pub fn decode(&self) -> Result<SecretBytes, QrError> {
self.decode_rotated(0)
}
pub fn decode_rotated(&self, rotation_degrees: u32) -> Result<SecretBytes, QrError> {
let width = self.width as usize; let width = self.width as usize;
let height = self.height as usize; let height = self.height as usize;
let row = self.row_stride as usize; let row = self.row_stride as usize;
@@ -99,6 +103,7 @@ impl MobileCameraFrame {
|| !(1..=4096).contains(&height) || !(1..=4096).contains(&height)
|| !(1..=4).contains(&pixel) || !(1..=4).contains(&pixel)
|| bytes.len() > 32 * 1024 * 1024 || bytes.len() > 32 * 1024 * 1024
|| !matches!(rotation_degrees, 0 | 90 | 180 | 270)
{ {
return Err(QrError::InvalidImage); return Err(QrError::InvalidImage);
} }
@@ -112,7 +117,20 @@ impl MobileCameraFrame {
if row <= last_column || last >= bytes.len() { if row <= last_column || last >= bytes.len() {
return Err(QrError::InvalidImage); return Err(QrError::InvalidImage);
} }
QrMatrix::decode_luminance(width, height, |x, y| bytes[y * row + x * pixel]) let (output_width, output_height) = if matches!(rotation_degrees, 90 | 270) {
(height, width)
} else {
(width, height)
};
QrMatrix::decode_luminance(output_width, output_height, |x, y| {
let (x, y) = match rotation_degrees {
90 => (y, height - 1 - x),
180 => (width - 1 - x, height - 1 - y),
270 => (width - 1 - y, x),
_ => (x, y),
};
bytes[y * row + x * pixel]
})
} }
} }
@@ -223,6 +241,61 @@ impl fmt::Debug for MobileCredentialRequest {
mod tests { mod tests {
use super::*; use super::*;
#[test]
fn camera_planes_decode_padded_pixels_at_every_orientation() {
let payload = SecretBytes::new(b"camera-qr-byte-exact".to_vec());
let matrix = QrMatrix::encode(&payload).unwrap();
let size = (matrix.width() + 8) * 5;
let row = size * 2 + 7;
let mut pixels = vec![255; size * row];
for y in 0..size {
for x in 0..size {
let dark = x / 5 >= 4
&& y / 5 >= 4
&& matrix.is_dark(x / 5 - 4, y / 5 - 4).unwrap_or(false);
pixels[y * row + x * 2] = if dark { 0 } else { 255 };
}
}
for rotation in [0, 90, 180, 270] {
let decoded = MobileCameraFrame {
luminance: SecretBytes::new(pixels.clone()),
width: size as u32,
height: size as u32,
row_stride: row as u32,
pixel_stride: 2,
}
.decode_rotated(rotation)
.unwrap();
assert_eq!(decoded.expose(), payload.expose());
}
}
#[test]
fn camera_rejects_truncated_oversized_and_invalid_planes() {
for (width, height, row, step, rotation, length) in [
(0, 1, 1, 1, 0, 1),
(4097, 1, 4097, 1, 0, 4097),
(2, 2, 2, 1, 0, 3),
(2, 2, 1, 1, 0, 4),
(2, 2, 2, 0, 0, 4),
(2, 2, 2, 1, 45, 4),
(2, 2, u32::MAX, 1, 0, 4),
(1, 1, 1, 1, 0, 32 * 1024 * 1024 + 1),
] {
assert!(matches!(
MobileCameraFrame {
luminance: SecretBytes::new(vec![255; length]),
width,
height,
row_stride: row,
pixel_stride: step,
}
.decode_rotated(rotation),
Err(QrError::InvalidImage)
));
}
}
#[test] #[test]
fn roots_and_caller_facts_are_validated_without_creating_data_or_grants() { fn roots_and_caller_facts_are_validated_without_creating_data_or_grants() {
let paths = let paths =
+96
View File
@@ -0,0 +1,96 @@
# Android GPG QR transfer
Issue [#106](https://git.rfc1437.de/hugo/IronStorage/issues/106) uses the shared
ASCII-armored OpenPGP and `ISKT1` multipart format. `crates/storage` owns frame
bounds/orientation, QR decoding, checksum/reassembly, identity, passphrase
validation, duplicate handling and transactional key installation. Kotlin only
supplies camera facts and renders storage-provided QR matrices.
## Import and export
Open Preferences → Import or export GPG QR codes. Scanning requests camera
permission, binds CameraX Preview and ImageAnalysis to the Activity lifecycle,
and uses only the latest Y plane. Pause closes the camera while retaining the
in-memory multipart session; resume opens a fresh camera. Frames can arrive in
any order. Duplicate frames leave the received count unchanged. The complete
key's Rust-derived identity and fingerprint appear before import. Private import
requires a separate fingerprint/security confirmation. Default-key selection
and replacement are explicit controls; incorrect passphrases, incomplete,
conflicting or mismatched transfers cannot replace valid keys/configuration.
Onboarding → Import existing GPG key → Scan GPG QR codes uses the same scanner
and review, then stages the key through Android's existing onboarding transaction.
It does not create a store or bypass the later local/HTTPS setup choice. Public
keys cannot become onboarding/default decryption keys; Rust rejects them.
Export selects a storage-provided key. Private export requires a GPG passphrase
and a separate fingerprint/security confirmation. Every displayed export remains
labeled with the identity that produced it, even if selection changes. Previous
matrices are erased before a new export. Multipart exports support previous/next
and optional automatic frame advancement. Brightness and keep-screen-on apply
only while QR display is composed and are restored on disposal. Frame ticks do
not renew the storage-owned visibility deadline (the configured authentication
timeout).
## Privacy and interruption
The Activity and transfer dialog use `FLAG_SECURE`. Lock/background/focus loss,
process recreation and dismissal remove transfer state, clear Kotlin matrices
and inputs, and drop the Rust importer/exporter. Rust multipart chunks and
reassembly buffers zeroize on drop. No camera frame, armor or QR payload is
written to Android persistence, photos, clipboard, saved instance state or logs.
Only an explicitly confirmed successful import persists GPG-compatible keys.
Camera permission prompts, an external image picker, device rotation and app
backgrounding follow the existing fail-closed lifecycle policy: an interrupted
transfer is discarded; reopen and rescan rather than retaining private bytes
across an OS interruption. A camera pause within the active dialog retains
progress. Denial (including permanent denial) presents a settings link and image/
manual alternatives. Missing/busy camera reports an actionable recovery message.
Camera decoding accepts at most 2048 × 2048 pixels and a 4 MiB Y plane, validates
row/pixel strides and the four quarter-turn rotations before accessing pixels,
and allows one native decode in flight. Kotlin clears the input plane before
publishing a completed key to state observers, with a final cleanup on every
error/cancellation path. CameraX drops obsolete frames rather
than queueing them. Imported `ISKT1` envelopes retain the existing 11,000-frame/
17.6 MB armor bound and 1,600-byte chunk bound. Export rejects more than 256
frames before allocating matrices, keeping the matrix set below 8 MiB.
## Verification
Rust tests use upstream-compatible Alice public/private armor to prove byte-exact
single/multipart reconstruction, rotated/padded camera planes, out-of-order and
duplicate delivery, incomplete/corrupt/mismatched rejection, wrong-passphrase
retention, import rollback, export size bounds and deadline polling without renewal.
Native Android settings tests also exercise camera planes through generated UniFFI
and verify Kotlin frame bytes and QR matrices are cleared. Compose tests check
private import/export confirmation and hidden-input saved-state exclusions.
`AndroidQrCameraTest` exercises actual emulator CameraX frame delivery and repeated
close/reopen; the preview is clipped in both Android View and Compose layout.
Build gates include both ARM ABIs, debug/release/AAB, JVM tests, lint/ktlint,
architecture/source checks, exact dependency locks/checksums, the license/SBOM
gate, every native ELF/APK 16 KB alignment check, and matching FULL release symbols.
CameraX 1.6.2 and its exact transitives are reviewed against official Maven POM
licenses; camera-core includes Apache-2.0 and BSD-3-Clause portions. The merged
manifest removes Media3's unused ACCESS_NETWORK_STATE permission; only CAMERA
is added for this feature.
Validation is emulator-only under the milestone's ARM support policy. The
Keystore enrollment/invalidation tests require separate interactive device setup;
their setup failures are not camera test results and do not establish biometric
coverage for this change.
Recorded for #106 on 2026-10-12:
| Environment | Evidence |
| --- | --- |
| API 26 ARM, 4 KB | Native settings/viewer fixture including private export and relock erasure; 14 bridge tests; app runner OK (32 reported, including four opt-in/biometric methods not enabled); final onboarding/settings regression group 4 passed; CameraX close/reopen passed |
| API 37 ARM, strict 16 KB | Native fixture including private export and relock erasure; 14 bridge tests; CameraX close/reopen passed; original app run included both passing QR settings tests |
| API 37 ARM, 4 KB, computer use | Update retained existing store/preferences; camera permission interruption discarded transfer; live CameraX preview and clipped scrolling controls inspected; private export stayed disabled until fingerprint confirmation |
| Rust 1.97.1 and Android packages | Required workspace gates, androidVerification and release lint, license/SBOM, debug/release manifests/backup exclusions, ARM native alignment and release FULL-symbol checks passed |
Private export from the retained simulator profile awaits optional user input.
Successful private export/import and erasure evidence above uses isolated,
encrypted compatibility fixtures rather than a user's passphrase.
+3 -3
View File
@@ -46,9 +46,9 @@ shared core's recipient/signing rules, pass/pass-otp format, HOTP counter safety
Git failure semantics and secret-redaction requirements remain binding wherever Git failure semantics and secret-redaction requirements remain binding wherever
an Android workflow invokes them; platform presentation may not replace them. an Android workflow invokes them; platform presentation may not replace them.
Current iPhone camera adapters pass OS-decoded QR bytes. Android #106/#91 must Current iPhone camera adapters pass OS-decoded QR bytes. Android #106 supplies bounded Y-plane/stride/orientation facts through the
also supply bounded image/frame facts for Rust decoding as required by the shared native contract; storage decodes and reassembles the transfer. See
Android contract; that additional adapter is pending, not an existing API. [key-transfer.md](key-transfer.md) for interruption/security behavior and evidence.
The external-credential matching/grant API required by #109 must be implemented The external-credential matching/grant API required by #109 must be implemented
in storage, rather than copying frontend selection assumptions from AutoFill. in storage, rather than copying frontend selection assumptions from AutoFill.
+9 -1
View File
@@ -60,6 +60,7 @@ fn manifest_text(source: &str) -> Result<(), Box<dyn Error>> {
"android.permission.USE_FINGERPRINT", "android.permission.USE_FINGERPRINT",
"de.rfc1437.ironstorage.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION", "de.rfc1437.ironstorage.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
"android.permission.INTERNET", "android.permission.INTERNET",
"android.permission.CAMERA",
]; ];
for node in root.children().filter(|node| node.is_element()) { for node in root.children().filter(|node| node.is_element()) {
if node.tag_name().name().starts_with("uses-permission") if node.tag_name().name().starts_with("uses-permission")
@@ -448,6 +449,13 @@ mod tests {
)) ))
.is_ok() .is_ok()
); );
assert!(
manifest_text(&source.replace(
"<application",
"<uses-permission android:name=\"android.permission.CAMERA\"/><application"
))
.is_ok()
);
for bad in [ for bad in [
source.replace("allowBackup=\"false\"", "allowBackup=\"true\""), source.replace("allowBackup=\"false\"", "allowBackup=\"true\""),
source.replace( source.replace(
@@ -456,7 +464,7 @@ mod tests {
), ),
source.replace( source.replace(
"<application", "<application",
"<uses-permission android:name=\"android.permission.CAMERA\"/><application", "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/><application",
), ),
] { ] {
assert!(manifest_text(&bad).is_err()); assert!(manifest_text(&bad).is_err());
+2
View File
@@ -191,6 +191,8 @@ fn audit_package_abis(path: &Path, abis: &[&str]) -> Result<(), Box<dyn Error>>
if ![ if ![
"libironstorage_android.so", "libironstorage_android.so",
"libjnidispatch.so", "libjnidispatch.so",
"libimage_processing_util_jni.so",
"libsurface_util_jni.so",
"libandroidx.graphics.path.so", "libandroidx.graphics.path.so",
] ]
.contains(&library) .contains(&library)